diff --git a/3rdparty/hunspell/Makefile.am b/3rdparty/hunspell/Makefile.am index 7e0429d014..fe0502dd21 100644 --- a/3rdparty/hunspell/Makefile.am +++ b/3rdparty/hunspell/Makefile.am @@ -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 \ diff --git a/INSTALL b/INSTALL index c18d140074..99114b2de3 100644 --- a/INSTALL +++ b/INSTALL @@ -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 diff --git a/config/lyxinclude.m4 b/config/lyxinclude.m4 index ead704b166..2c1a9af12a 100644 --- a/config/lyxinclude.m4 +++ b/config/lyxinclude.m4 @@ -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 diff --git a/config/spell.m4 b/config/spell.m4 index 2a030264b5..d686a82951 100644 --- a/config/spell.m4 +++ b/config/spell.m4 @@ -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 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 diff --git a/development/PAINTING_ANALYSIS b/development/PAINTING_ANALYSIS index e53b4b23df..32bc93a5ff 100644 --- a/development/PAINTING_ANALYSIS +++ b/development/PAINTING_ANALYSIS @@ -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(). diff --git a/development/cmake/ConfigureChecks.cmake b/development/cmake/ConfigureChecks.cmake index d2695b041a..20ee32f169 100644 --- a/development/cmake/ConfigureChecks.cmake +++ b/development/cmake/ConfigureChecks.cmake @@ -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 + + 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( # " diff --git a/development/cmake/configCompiler.h.cmake b/development/cmake/configCompiler.h.cmake index 52f629e7bb..8934986cb0 100644 --- a/development/cmake/configCompiler.h.cmake +++ b/development/cmake/configCompiler.h.cmake @@ -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 diff --git a/lib/CREDITS b/lib/CREDITS index 1575ab5183..9ef4043ebb 100644 --- a/lib/CREDITS +++ b/lib/CREDITS @@ -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 diff --git a/lib/RELEASE-NOTES b/lib/RELEASE-NOTES index 14ebfda6d4..b897ce101e 100644 --- a/lib/RELEASE-NOTES +++ b/lib/RELEASE-NOTES @@ -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. diff --git a/lib/configure.py b/lib/configure.py index 6f8efcd255..ac4f0fd0cb 100644 --- a/lib/configure.py +++ b/lib/configure.py @@ -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 "%%" ""']) diff --git a/lib/doc/Customization.lyx b/lib/doc/Customization.lyx index 40b32b4f15..7b81ff850e 100644 --- a/lib/doc/Customization.lyx +++ b/lib/doc/Customization.lyx @@ -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 diff --git a/lib/doc/UserGuide.lyx b/lib/doc/UserGuide.lyx index fb8cac9d10..fa0b3081a2 100644 --- a/lib/doc/UserGuide.lyx +++ b/lib/doc/UserGuide.lyx @@ -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 diff --git a/lib/doc/de/Customization.lyx b/lib/doc/de/Customization.lyx index 270e385de6..0420dad76e 100644 --- a/lib/doc/de/Customization.lyx +++ b/lib/doc/de/Customization.lyx @@ -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 diff --git a/lib/generate_contributions.py b/lib/generate_contributions.py index c698ff3c9c..e3d966a92d 100755 --- a/lib/generate_contributions.py +++ b/lib/generate_contributions.py @@ -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", diff --git a/lib/layouts/IEEEtran.layout b/lib/layouts/IEEEtran.layout index 52b867af10..21e117f499 100644 --- a/lib/layouts/IEEEtran.layout +++ b/lib/layouts/IEEEtran.layout @@ -364,6 +364,7 @@ Style Bibliography Size Larger EndFont TocLevel 1 + IsTocCaption 0 End diff --git a/lib/layouts/aguplus.inc b/lib/layouts/aguplus.inc index 57ebab331c..6c23fc97a0 100644 --- a/lib/layouts/aguplus.inc +++ b/lib/layouts/aguplus.inc @@ -177,6 +177,7 @@ Style Bibliography Size Huge EndFont TocLevel 1 + IsTocCaption 0 End diff --git a/lib/layouts/agutex.layout b/lib/layouts/agutex.layout index eacc73eac5..ee812d3e9e 100644 --- a/lib/layouts/agutex.layout +++ b/lib/layouts/agutex.layout @@ -228,5 +228,6 @@ Style Bibliography Size Larger EndFont TocLevel 1 + IsTocCaption 0 End diff --git a/lib/layouts/amsdefs.inc b/lib/layouts/amsdefs.inc index 3820739bc7..0744ecdd06 100644 --- a/lib/layouts/amsdefs.inc +++ b/lib/layouts/amsdefs.inc @@ -226,4 +226,5 @@ Style Bibliography Shape Smallcaps EndFont TocLevel 1 + IsTocCaption 0 End diff --git a/lib/layouts/beamer.layout b/lib/layouts/beamer.layout index ff8046ad54..ed15d26fe8 100644 --- a/lib/layouts/beamer.layout +++ b/lib/layouts/beamer.layout @@ -1126,6 +1126,7 @@ Style Bibliography Size Larger EndFont TocLevel 1 + IsTocCaption 0 End diff --git a/lib/layouts/cl2emult.layout b/lib/layouts/cl2emult.layout index 0dadd2af7d..69e5fbbac7 100644 --- a/lib/layouts/cl2emult.layout +++ b/lib/layouts/cl2emult.layout @@ -126,6 +126,7 @@ Style Bibliography Size Larger EndFont TocLevel 1 + IsTocCaption 0 End diff --git a/lib/layouts/egs.layout b/lib/layouts/egs.layout index ac53e616dd..b411c4384f 100644 --- a/lib/layouts/egs.layout +++ b/lib/layouts/egs.layout @@ -596,6 +596,7 @@ Style Bibliography Series Bold EndFont TocLevel 1 + IsTocCaption 0 End diff --git a/lib/layouts/elsarticle.layout b/lib/layouts/elsarticle.layout index 42e86fec1f..89a6a20cd0 100644 --- a/lib/layouts/elsarticle.layout +++ b/lib/layouts/elsarticle.layout @@ -326,5 +326,6 @@ Style Bibliography Size Larger EndFont TocLevel 1 + IsTocCaption 0 End diff --git a/lib/layouts/ijmpc.layout b/lib/layouts/ijmpc.layout index 38b43131e1..0f2b25c609 100644 --- a/lib/layouts/ijmpc.layout +++ b/lib/layouts/ijmpc.layout @@ -468,4 +468,5 @@ Style Bibliography Size Larger EndFont TocLevel 1 + IsTocCaption 0 End diff --git a/lib/layouts/ijmpd.layout b/lib/layouts/ijmpd.layout index c3835b828b..e35f852bcc 100644 --- a/lib/layouts/ijmpd.layout +++ b/lib/layouts/ijmpd.layout @@ -481,4 +481,5 @@ Style Bibliography Size Larger EndFont TocLevel 1 + IsTocCaption 0 End diff --git a/lib/layouts/iopart.layout b/lib/layouts/iopart.layout index 3ff99ef7b6..cea40c800f 100644 --- a/lib/layouts/iopart.layout +++ b/lib/layouts/iopart.layout @@ -288,6 +288,7 @@ Style "Bibliography (plain)" Size Larger EndFont TocLevel 1 + IsTocCaption 0 End diff --git a/lib/layouts/jasatex.layout b/lib/layouts/jasatex.layout index 4c1f46e80c..6dc7b2b6c4 100644 --- a/lib/layouts/jasatex.layout +++ b/lib/layouts/jasatex.layout @@ -289,5 +289,6 @@ Style Bibliography Size Larger EndFont TocLevel 1 + IsTocCaption 0 End diff --git a/lib/layouts/kluwer.layout b/lib/layouts/kluwer.layout index 9fed36753d..8e8ee3428b 100644 --- a/lib/layouts/kluwer.layout +++ b/lib/layouts/kluwer.layout @@ -367,4 +367,5 @@ Style References Series Bold EndFont TocLevel 1 + IsTocCaption 0 End diff --git a/lib/layouts/llncs.layout b/lib/layouts/llncs.layout index 0787825b15..6942f224a6 100644 --- a/lib/layouts/llncs.layout +++ b/lib/layouts/llncs.layout @@ -288,6 +288,7 @@ Style Bibliography Series Bold EndFont TocLevel 0 + IsTocCaption 0 End diff --git a/lib/layouts/moderncv.layout b/lib/layouts/moderncv.layout index afcfd1e115..03c2ed8582 100644 --- a/lib/layouts/moderncv.layout +++ b/lib/layouts/moderncv.layout @@ -520,6 +520,7 @@ Style Bibliography Family Sans EndFont TocLevel 1 + IsTocCaption 0 End Style Recipient diff --git a/lib/layouts/powerdot.layout b/lib/layouts/powerdot.layout index 4c3e37edf8..f59b48b2da 100644 --- a/lib/layouts/powerdot.layout +++ b/lib/layouts/powerdot.layout @@ -371,7 +371,8 @@ Style Bibliography LabelType Bibliography LabelString "" LabelBottomSep 0 - TocLevel 1 + TocLevel 1 + IsTocCaption 0 End ### diff --git a/lib/layouts/siamltex.layout b/lib/layouts/siamltex.layout index a0f4565bb3..2515d49125 100644 --- a/lib/layouts/siamltex.layout +++ b/lib/layouts/siamltex.layout @@ -349,6 +349,7 @@ Style Bibliography Shape Smallcaps EndFont TocLevel 1 + IsTocCaption 0 End NoStyle Chapter diff --git a/lib/layouts/simplecv.layout b/lib/layouts/simplecv.layout index 88a452f43a..dc3654e6fe 100644 --- a/lib/layouts/simplecv.layout +++ b/lib/layouts/simplecv.layout @@ -165,4 +165,5 @@ Style Bibliography Size Tiny EndFont TocLevel 1 + IsTocCaption 0 End diff --git a/lib/layouts/stdstruct.inc b/lib/layouts/stdstruct.inc index b323cf37ea..1c2c8c60ed 100644 --- a/lib/layouts/stdstruct.inc +++ b/lib/layouts/stdstruct.inc @@ -82,4 +82,5 @@ Style Bibliography span.bibitemlabel:after { content: "] "; } EndHTMLStyle TocLevel 1 + ISTocCaption 0 End diff --git a/lib/layouts/svcommon.inc b/lib/layouts/svcommon.inc index 3b1ea043ab..1adc110a49 100644 --- a/lib/layouts/svcommon.inc +++ b/lib/layouts/svcommon.inc @@ -594,6 +594,7 @@ Style Bibliography Series Bold EndFont TocLevel 0 + IsTocCaption 0 End Style Description diff --git a/lib/syntax.default b/lib/syntax.default index cbdec85090..175558e109 100644 --- a/lib/syntax.default +++ b/lib/syntax.default @@ -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": diff --git a/lib/ui/stdcontext.inc b/lib/ui/stdcontext.inc index 3e490927be..9acf334562 100644 --- a/lib/ui/stdcontext.inc +++ b/lib/ui/stdcontext.inc @@ -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" diff --git a/src/Buffer.cpp b/src/Buffer.cpp index a25f91daa5..6e523d9098 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -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 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. diff --git a/src/BufferParams.cpp b/src/BufferParams.cpp index b709b16a54..60c7bb0c12 100644 --- a/src/BufferParams.cpp +++ b/src/BufferParams.cpp @@ -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 const & engine) string const & BufferParams::defaultBiblioStyle() const { + if (!biblio_style.empty()) + return biblio_style; + map const & bs = documentClass().defaultBiblioStyle(); auto cit = bs.find(theCiteEnginesList.getTypeAsString(citeEngineType())); if (cit != bs.end()) diff --git a/src/BufferView.cpp b/src/BufferView.cpp index e141f34c7e..7788f76d3d 100644 --- a/src/BufferView.cpp +++ b/src/BufferView.cpp @@ -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(*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(); } diff --git a/src/BufferView.h b/src/BufferView.h index 588e9d0665..3cbf22a8ba 100644 --- a/src/BufferView.h +++ b/src/BufferView.h @@ -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. diff --git a/src/Converter.cpp b/src/Converter.cpp index 664a4bb718..e272d08a64 100644 --- a/src/Converter.cpp +++ b/src/Converter.cpp @@ -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) { diff --git a/src/Converter.h b/src/Converter.h index 8f63aba512..e144ca2fa4 100644 --- a/src/Converter.h +++ b/src/Converter.h @@ -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 diff --git a/src/Cursor.cpp b/src/Cursor.cpp index 984eb09ae5..91df9d6887 100644 --- a/src/Cursor.cpp +++ b/src/Cursor.cpp @@ -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); diff --git a/src/Cursor.h b/src/Cursor.h index 1e66778e99..55cc30e941 100644 --- a/src/Cursor.h +++ b/src/Cursor.h @@ -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 diff --git a/src/CutAndPaste.cpp b/src/CutAndPaste.cpp index f48eb4511d..be8142b76e 100644 --- a/src/CutAndPaste.cpp +++ b/src/CutAndPaste.cpp @@ -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(*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(*it); diff --git a/src/Font.cpp b/src/Font.cpp index 5ac0328ea5..e81e12449b 100644 --- a/src/Font.cpp +++ b/src/Font.cpp @@ -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" diff --git a/src/FuncCode.h b/src/FuncCode.h index 92d5e35268..cce61f0df5 100644 --- a/src/FuncCode.h +++ b/src/FuncCode.h @@ -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 }; diff --git a/src/HunspellChecker.cpp b/src/HunspellChecker.cpp index 6dc6647b5c..1c2612e78e 100644 --- a/src/HunspellChecker.cpp +++ b/src/HunspellChecker.cpp @@ -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 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 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 } diff --git a/src/Layout.cpp b/src/Layout.cpp index 0a783d0aad..005acb2258 100644 --- a/src/Layout.cpp +++ b/src/Layout.cpp @@ -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; diff --git a/src/LyXAction.cpp b/src/LyXAction.cpp index 7cd103b1c4..d2e92b1c94 100644 --- a/src/LyXAction.cpp +++ b/src/LyXAction.cpp @@ -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 [] + * \li Params: : 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". diff --git a/src/LyXRC.cpp b/src/LyXRC.cpp index 104dcfbb21..361c64e853 100644 --- a/src/LyXRC.cpp +++ b/src/LyXRC.cpp @@ -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(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: diff --git a/src/LyXRC.h b/src/LyXRC.h index f2b0ab73e6..7049bf8d5e 100644 --- a/src/LyXRC.h +++ b/src/LyXRC.h @@ -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? diff --git a/src/MetricsInfo.h b/src/MetricsInfo.h index 2a18cf8cff..ff0b1c6989 100644 --- a/src/MetricsInfo.h +++ b/src/MetricsInfo.h @@ -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; diff --git a/src/OutputParams.h b/src/OutputParams.h index 1631dac9d0..ff9491205b 100644 --- a/src/OutputParams.h +++ b/src/OutputParams.h @@ -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; diff --git a/src/PDFOptions.cpp b/src/PDFOptions.cpp index a4d23ee5a9..b030baa62e 100644 --- a/src/PDFOptions.cpp +++ b/src/PDFOptions.cpp @@ -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,"; diff --git a/src/Paragraph.cpp b/src/Paragraph.cpp index 5c5a728027..350418c564 100644 --- a/src/Paragraph.cpp +++ b/src/Paragraph.cpp @@ -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, diff --git a/src/Paragraph.h b/src/Paragraph.h index d462f4c413..0c6c48090f 100644 --- a/src/Paragraph.h +++ b/src/Paragraph.h @@ -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 &, diff --git a/src/ParagraphMetrics.cpp b/src/ParagraphMetrics.cpp index 01db6cb934..1d3ce0db5c 100644 --- a/src/ParagraphMetrics.cpp +++ b/src/ParagraphMetrics.cpp @@ -47,8 +47,6 @@ #include "support/lstrings.h" #include "support/textutils.h" -#include - #include #include #include @@ -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(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; diff --git a/src/ParagraphMetrics.h b/src/ParagraphMetrics.h index 55304fd9ed..63ed0f3cdf 100644 --- a/src/ParagraphMetrics.h +++ b/src/ParagraphMetrics.h @@ -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); diff --git a/src/Row.cpp b/src/Row.cpp index db1bd2883c..3fb87bd7e1 100644 --- a/src/Row.cpp +++ b/src/Row.cpp @@ -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; diff --git a/src/Row.h b/src/Row.h index 498fd07d7f..49513d32b9 100644 --- a/src/Row.h +++ b/src/Row.h @@ -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 + 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 + 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_; }; diff --git a/src/RowPainter.cpp b/src/RowPainter.cpp index 1f89b252b9..38bcefadad 100644 --- a/src/RowPainter.cpp +++ b/src/RowPainter.cpp @@ -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; diff --git a/src/Spacing.cpp b/src/Spacing.cpp index cd5816e600..9ac8345ab4 100644 --- a/src/Spacing.cpp +++ b/src/Spacing.cpp @@ -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; diff --git a/src/Spacing.h b/src/Spacing.h index 274cfc20f2..7311f792b5 100644 --- a/src/Spacing.h +++ b/src/Spacing.h @@ -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: diff --git a/src/Text.cpp b/src/Text.cpp index d5d729a277..ed3650b8df 100644 --- a/src/Text.cpp +++ b/src/Text.cpp @@ -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', '-'); diff --git a/src/Text3.cpp b/src/Text3.cpp index 36739a8e1e..adeb80b88f 100644 --- a/src/Text3.cpp +++ b/src/Text3.cpp @@ -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) diff --git a/src/TextMetrics.cpp b/src/TextMetrics.cpp index 4768157bda..5d4b88d604 100644 --- a/src/TextMetrics.cpp +++ b/src/TextMetrics.cpp @@ -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(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, "."); } diff --git a/src/TextMetrics.h b/src/TextMetrics.h index 3000b218bf..ae99490955 100644 --- a/src/TextMetrics.h +++ b/src/TextMetrics.h @@ -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 diff --git a/src/Undo.cpp b/src/Undo.cpp index f7ee48205f..01394bc27f 100644 --- a/src/Undo.cpp +++ b/src/Undo.cpp @@ -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 #include +#include 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, - * but I am not sure yet how to do it. A use case is - * InsetLabel::updateReferences. - */ +class UndoGroupHelper::Impl { + friend class UndoGroupHelper; + set 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(); + } } diff --git a/src/Undo.h b/src/Undo.h index 70897bfa28..b1908a25b3 100644 --- a/src/Undo.h +++ b/src/Undo.h @@ -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; }; diff --git a/src/frontends/Makefile.am b/src/frontends/Makefile.am index 862613a67f..22b7508a7a 100644 --- a/src/frontends/Makefile.am +++ b/src/frontends/Makefile.am @@ -17,6 +17,7 @@ liblyxfrontends_a_SOURCES = \ Delegates.h \ KeyModifier.h \ KeySymbol.h \ + NullPainter.h \ Painter.h \ Clipboard.h \ Selection.h \ diff --git a/src/frontends/NullPainter.h b/src/frontends/NullPainter.h new file mode 100644 index 0000000000..19ac8b66a2 --- /dev/null +++ b/src/frontends/NullPainter.h @@ -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 diff --git a/src/frontends/Painter.h b/src/frontends/Painter.h index d03ebf40e8..17253b2f44 100644 --- a/src/frontends/Painter.h +++ b/src/frontends/Painter.h @@ -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_; }; diff --git a/src/frontends/WorkArea.h b/src/frontends/WorkArea.h index 8e459ca375..c21555913e 100644 --- a/src/frontends/WorkArea.h +++ b/src/frontends/WorkArea.h @@ -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. diff --git a/src/frontends/WorkAreaManager.cpp b/src/frontends/WorkAreaManager.cpp index b98163cecc..c79f08bef7 100644 --- a/src/frontends/WorkAreaManager.cpp +++ b/src/frontends/WorkAreaManager.cpp @@ -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); } diff --git a/src/frontends/qt4/FileDialog.cpp b/src/frontends/qt4/FileDialog.cpp index f1156348d4..ee5f3503da 100644 --- a/src/frontends/qt4/FileDialog.cpp +++ b/src/frontends/qt4/FileDialog.cpp @@ -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 -/** when this is defined, the code will use +#include + +/** 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 -#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; } diff --git a/src/frontends/qt4/FindAndReplace.cpp b/src/frontends/qt4/FindAndReplace.cpp index 4411074de7..607f1653aa 100644 --- a/src/frontends/qt4/FindAndReplace.cpp +++ b/src/frontends/qt4/FindAndReplace.cpp @@ -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(); } diff --git a/src/frontends/qt4/GuiApplication.cpp b/src/frontends/qt4/GuiApplication.cpp index 35b639c9ac..323e654a41 100644 --- a/src/frontends/qt4/GuiApplication.cpp +++ b/src/frontends/qt4/GuiApplication.cpp @@ -1395,9 +1395,9 @@ DispatchResult const & GuiApplication::dispatch(FuncRequest const & cmd) if (current_view_ && current_view_->currentBufferView()) { current_view_->currentBufferView()->cursor().saveBeforeDispatchPosXY(); buffer = ¤t_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; } diff --git a/src/frontends/qt4/GuiCitation.cpp b/src/frontends/qt4/GuiCitation.cpp index 66e7ba7aa4..46cdd8d36f 100644 --- a/src/frontends/qt4/GuiCitation.cpp +++ b/src/frontends/qt4/GuiCitation.cpp @@ -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 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(); } diff --git a/src/frontends/qt4/GuiCitation.h b/src/frontends/qt4/GuiCitation.h index 008d2d556d..4d85c0315a 100644 --- a/src/frontends/qt4/GuiCitation.h +++ b/src/frontends/qt4/GuiCitation.h @@ -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. diff --git a/src/frontends/qt4/GuiDocument.cpp b/src/frontends/qt4/GuiDocument.cpp index 74c4807878..1748ddcaaa 100644 --- a/src/frontends/qt4/GuiDocument.cpp +++ b/src/frontends/qt4/GuiDocument.cpp @@ -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(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 const & packages = BufferParams::auto_packages(); mathsModule->packagesTW->setRowCount(packages.size()); - int i = 0; + int packnum = 0; for (map::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())->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()); 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); } } diff --git a/src/frontends/qt4/GuiDocument.h b/src/frontends/qt4/GuiDocument.h index 1fe1dc74a1..e4f32e2570 100644 --- a/src/frontends/qt4/GuiDocument.h +++ b/src/frontends/qt4/GuiDocument.h @@ -332,10 +332,14 @@ private: void closeEvent(QCloseEvent *); void on_preambleTE_textChanged() { changed(); } -private: typedef std::map > Coords; Coords preamble_coords_; BufferId current_id_; + +private Q_SLOTS: + /// + void checkFindButton(); + void findText(); }; diff --git a/src/frontends/qt4/GuiExternal.cpp b/src/frontends/qt4/GuiExternal.cpp index f994aafaef..ba1c171ebf 100644 --- a/src/frontends/qt4/GuiExternal.cpp +++ b/src/frontends/qt4/GuiExternal.cpp @@ -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)); diff --git a/src/frontends/qt4/GuiInclude.cpp b/src/frontends/qt4/GuiInclude.cpp index aa2fc65725..edbf7a19c4 100644 --- a/src/frontends/qt4/GuiInclude.cpp +++ b/src/frontends/qt4/GuiInclude.cpp @@ -31,9 +31,9 @@ #include "insets/InsetListingsParams.h" #include "insets/InsetInclude.h" -#include #include #include +#include #include diff --git a/src/frontends/qt4/GuiPainter.cpp b/src/frontends/qt4/GuiPainter.cpp index e832f23e99..b03bb44ad3 100644 --- a/src/frontends/qt4/GuiPainter.cpp +++ b/src/frontends/qt4/GuiPainter.cpp @@ -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 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 diff --git a/src/frontends/qt4/GuiPainter.h b/src/frontends/qt4/GuiPainter.h index 9657ed9770..7513965ea6 100644 --- a/src/frontends/qt4/GuiPainter.h +++ b/src/frontends/qt4/GuiPainter.h @@ -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, diff --git a/src/frontends/qt4/GuiView.cpp b/src/frontends/qt4/GuiView.cpp index 45c26c325c..f20bc02195 100644 --- a/src/frontends/qt4/GuiView.cpp +++ b/src/frontends/qt4/GuiView.cpp @@ -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(); } diff --git a/src/frontends/qt4/GuiView.h b/src/frontends/qt4/GuiView.h index 8ff5192218..3ca4641ce0 100644 --- a/src/frontends/qt4/GuiView.h +++ b/src/frontends/qt4/GuiView.h @@ -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 diff --git a/src/frontends/qt4/GuiWorkArea.cpp b/src/frontends/qt4/GuiWorkArea.cpp index 8bf5242b33..6df7e4e290 100644 --- a/src/frontends/qt4/GuiWorkArea.cpp +++ b/src/frontends/qt4/GuiWorkArea.cpp @@ -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 #include #include -#include #include #include #include @@ -77,8 +76,6 @@ #include #include -#include "support/bind.h" - #include #include @@ -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(*d->screen_); - pain.drawImage(rc, image, rcs); - } else { - QPixmap const & pixmap = static_cast(*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 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); diff --git a/src/frontends/qt4/GuiWorkArea.h b/src/frontends/qt4/GuiWorkArea.h index 34b5d3aa4d..39d2054217 100644 --- a/src/frontends/qt4/GuiWorkArea.h +++ b/src/frontends/qt4/GuiWorkArea.h @@ -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(); diff --git a/src/frontends/qt4/GuiWorkArea_Private.h b/src/frontends/qt4/GuiWorkArea_Private.h index 76b05c0d36..83012fa99f 100644 --- a/src/frontends/qt4/GuiWorkArea_Private.h +++ b/src/frontends/qt4/GuiWorkArea_Private.h @@ -13,28 +13,19 @@ #define WORKAREA_PRIVATE_H #include "FuncRequest.h" -#include "LyXRC.h" #include "support/FileName.h" #include "support/Timeout.h" #include -#include -#include #include -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 #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(pr * p->viewport()->width()), + static_cast(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(pixel_ratio_ * p->viewport()->width()), - static_cast(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(pixel_ratio_ * p->viewport()->width()), - static_cast(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 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_; diff --git a/src/frontends/qt4/ui/GraphicsUi.ui b/src/frontends/qt4/ui/GraphicsUi.ui index 3942651e83..83d0d0965b 100644 --- a/src/frontends/qt4/ui/GraphicsUi.ui +++ b/src/frontends/qt4/ui/GraphicsUi.ui @@ -1,109 +1,133 @@ - + + GraphicsUi - - + + 0 0 - 482 - 383 + 654 + 512 - - - 0 - 0 + + 0 0 - + - + true - - + + 9 - + + 9 + + + 9 + + + 9 + + 6 - - - + + + - + 0 - - + + &Graphics - - + + 9 - + + 9 + + + 9 + + + 9 + + 6 - - - + + + Select an image file - + &Browse... - - - + + + Output Size - + true - - + + 9 - + + 9 + + + 9 + + + 9 + + 6 - - + + - - + + - - - + + + true - - - 0 - 0 + + 0 0 - + Width of image in output - + - + Qt::Horizontal - + 61 20 @@ -111,69 +135,67 @@ - - + + - - - + + + true - - - 0 - 0 + + 0 0 - + Height of image in output - - - + + + true - + Sets height of graphic. Leave unchecked to set automatically. - + Set &height: - - - + + + &Scale graphics (%): - - - + + + true - + Sets width of graphic. Leave unchecked to set automatically. - + Set &width: - - - + + + true - + Scale image to maximum size not exceeding width and height - + &Maintain aspect ratio @@ -181,75 +203,82 @@ - - - + + + Rotate Graphics - + true - - + + 9 - + + 9 + + + 9 + + + 9 + + 6 - - - + + + Check to change the order of rotating and scaling - + Ro&tate after scaling - - - + + + The origin of the rotation - - - + + + The origin of the rotation - + Or&igin: - + origin - - - - - 0 - 0 + + + + 0 0 - + Angle to rotate image by - - - + + + Angle to rotate image by - + A&ngle (degrees): - + angle @@ -257,45 +286,54 @@ - - - + + + File name of image - + &File: - + filename - - - + + + File name of image - - - &Clipping + + + &Coordinates and Clipping - - + + 9 - + + 9 + + + 9 + + + 9 + + 6 - + - + Qt::Vertical - + 20 40 @@ -303,114 +341,121 @@ - - - - Clip to bounding box values + + + + Clip to the coordinates specified below (bounding box for DVI/PS output, viewport for PDF output) - - Clip to &bounding box + + Clip to c&oordinates - - - + + + true - - - 0 - 0 + + 0 0 - + QFrame::StyledPanel - + QFrame::Plain - - + + 9 - + + 9 + + + 9 + + + 9 + + 6 - - - + + + &Left bottom: - + lbX - - + + - - + + - - + + - - - + + + - - + + - - + + - - + + - - + + - - - + + + Right &top: - + rtX - - - + + + y: - - - + + + y: - - - + + + x: - - - + + + x: @@ -418,12 +463,12 @@ - + - + Qt::Horizontal - + 181 20 @@ -431,76 +476,70 @@ - - - - Get bounding box from the (EPS) file + + + + Read coordinates from the file (bounding box value in case of PostScript files, graphic dimensions in case of other file types) - + &Get from File - - + + LaTe&X and LyX options - - + + 9 - + + 9 + + + 9 + + + 9 + + 6 - - - - Qt::Horizontal - - - - 161 - 20 - - - - - - - - - 0 - 0 + + + + 0 0 - + Additional LaTeX options - - - + + + Additional LaTeX options - + LaTeX &options: - + latexoptions - + - + Qt::Vertical - + 354 81 @@ -508,67 +547,74 @@ - - - + + + Qt::StrongFocus - + Enable LyX to preview this graphics, if graphics previewing is not disabled at application level (see Preferences dialog). - + Sho&w in LyX - + true - + true - - + + 9 - + + 9 + + + 9 + + + 9 + + 6 - - - + + + true - - - 0 - 0 + + 0 0 - + Percentage to scale by in LyX - - - + + + Percentage to scale by in LyX - + Sca&le on screen (%): - + displayscale - + - + Qt::Horizontal - + 40 20 @@ -579,30 +625,39 @@ - - - + + + Assign the graphic to a group of graphics that share the same settings - + Graphics Group - + true - - + + 9 - + + 9 + + + 9 + + + 9 + + 6 - + - + Qt::Horizontal - + 121 51 @@ -610,43 +665,41 @@ - - - - - 0 - 0 + + + + 0 0 - + - - A&ssigned to group: + + Assigned &to group: - + groupCO - - - + + + Click to define a new graphics group. - + O&pen new group... - - - + + + Select an existing group for the current graphics. - + false @@ -654,12 +707,12 @@ - - - + + + Draft mode - + &Draft mode @@ -668,36 +721,45 @@ - - - - 0 - - + + + 6 + + 0 + + + 0 + + + 0 + + + 0 + - - + + &Restore - + false - + false - + Qt::Horizontal - + QSizePolicy::MinimumExpanding - + 20 20 @@ -706,53 +768,49 @@ - - + + &OK - + true - + true - - - - 0 - 0 + + + 0 0 - + &Apply - + false - + false - - - - 0 - 0 + + + 0 0 - + Close - + false @@ -803,7 +861,7 @@ displayGB - qt_i18n.h + qt_i18n.h diff --git a/src/frontends/qt4/ui/MathsUi.ui b/src/frontends/qt4/ui/MathsUi.ui index 8ee7c4e7ce..13f15dcc84 100644 --- a/src/frontends/qt4/ui/MathsUi.ui +++ b/src/frontends/qt4/ui/MathsUi.ui @@ -6,172 +6,15 @@ 0 0 - 500 + 569 367 - - - - - All packages: - - - - - - - Load A&utomatically - - - - - - - Load Alwa&ys - - - - - - - Do &Not Load - - - - - - - Indent displayed formulas instead of centering - - - Indent &Formulas - - - - - - - false - - - - 0 - 0 - - - - Size of the indentation - - - - - - - Qt::Horizontal - - - - 234 - 20 - - - - - - - - false - - - - - - - - - - - - - false - - - - 0 - 0 - - - - - - - - - - - Qt::Horizontal - - - - 153 - 20 - - - - - - - - - 0 - 0 - - - - - 115 - 18 - - - - Formula numbering side: - - - - - - - true - - - - 0 - 0 - - - - Side where formulas are numbered - - - - - - - Qt::Horizontal - - - - 234 - 17 - - - - - + + @@ -179,6 +22,9 @@ 0 + + true + 4 @@ -194,6 +40,171 @@ + + + + + + All packages: + + + + + + + Load A&utomatically + + + + + + + Load Alwa&ys + + + + + + + Do &Not Load + + + + + + + Indent displayed formulas instead of centering + + + Indent &Formulas + + + + + + + false + + + + 0 + 0 + + + + Size of the indentation + + + + + + + Qt::Horizontal + + + + 234 + 20 + + + + + + + + false + + + + + + + + + + + + + false + + + + 0 + 0 + + + + + + + + + + + Qt::Horizontal + + + + 153 + 20 + + + + + + + + + + + + + 0 + 0 + + + + + 115 + 18 + + + + Formula numbering side: + + + + + + + true + + + + 0 + 0 + + + + Side where formulas are numbered + + + + + + + Qt::Horizontal + + + + 234 + 17 + + + + + + diff --git a/src/frontends/qt4/ui/PreambleUi.ui b/src/frontends/qt4/ui/PreambleUi.ui index b39f26173d..1bc1c52879 100644 --- a/src/frontends/qt4/ui/PreambleUi.ui +++ b/src/frontends/qt4/ui/PreambleUi.ui @@ -1,7 +1,8 @@ - + + PreambleUi - - + + 0 0 @@ -9,19 +10,38 @@ 278 - + - - + + 11 - + + 11 + + + 11 + + + 11 + + 6 - - - + + + + + + + Find + + + + + + false @@ -29,7 +49,7 @@ - qt_i18n.h + qt_i18n.h diff --git a/src/insets/InsetBibitem.cpp b/src/insets/InsetBibitem.cpp index 08e8c76665..f9b244c460 100644 --- a/src/insets/InsetBibitem.cpp +++ b/src/insets/InsetBibitem.cpp @@ -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); } diff --git a/src/insets/InsetCitation.cpp b/src/insets/InsetCitation.cpp index 3201e83a2c..1d079c26cd 100644 --- a/src/insets/InsetCitation.cpp +++ b/src/insets/InsetCitation.cpp @@ -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_; diff --git a/src/insets/InsetCitation.h b/src/insets/InsetCitation.h index 0f914ec9a0..a8e9e1853d 100644 --- a/src/insets/InsetCitation.h +++ b/src/insets/InsetCitation.h @@ -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 const & valid_styles) const; /// std::map getQualifiedLists(docstring const p) const; + /// + static bool last_literal; private: /// tries to make a pretty label and makes a basic one if not diff --git a/src/insets/InsetGraphics.cpp b/src/insets/InsetGraphics.cpp index 94182a7326..a46d3fbc64 100644 --- a/src/insets/InsetGraphics.cpp +++ b/src/insets/InsetGraphics.cpp @@ -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(); } diff --git a/src/insets/InsetLabel.cpp b/src/insets/InsetLabel.cpp index 789a774ac6..7f3651f3a3 100644 --- a/src/insets/InsetLabel.cpp +++ b/src/insets/InsetLabel.cpp @@ -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(); } diff --git a/src/insets/RenderGraphic.cpp b/src/insets/RenderGraphic.cpp index 52e9803133..e01bcd3997 100644 --- a/src/insets/RenderGraphic.cpp +++ b/src/insets/RenderGraphic.cpp @@ -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; } diff --git a/src/mathed/InsetMathMacroTemplate.cpp b/src/mathed/InsetMathMacroTemplate.cpp index 17fc2d04a2..a2fc064c00 100644 --- a/src/mathed/InsetMathMacroTemplate.cpp +++ b/src/mathed/InsetMathMacroTemplate.cpp @@ -231,25 +231,17 @@ void InsetDisplayLabelBox::metrics(MetricsInfo & mi, Dimension & dim) const { InsetLabelBox::metrics(mi, dim); if (!parent_.editing(mi.base.bv) - && parent_.cell(parent_.displayIdx()).empty()) { - dim.wid = 0; - dim.asc = 0; - dim.des = 0; - } + && parent_.cell(parent_.displayIdx()).empty()) + dim.clear(); } void InsetDisplayLabelBox::draw(PainterInfo & pi, int x, int y) const { if (parent_.editing(pi.base.bv) - || !parent_.cell(parent_.displayIdx()).empty()) { - InsetLabelBox::draw(pi, x, y); - } else { - bool enabled = pi.pain.isDrawingEnabled(); - pi.pain.setDrawingEnabled(false); - InsetLabelBox::draw(pi, x, y); - pi.pain.setDrawingEnabled(enabled); - } + || !parent_.cell(parent_.displayIdx()).empty() + || pi.pain.isNull()) + InsetLabelBox::draw(pi, x, y); } diff --git a/src/mathed/InsetMathRoot.cpp b/src/mathed/InsetMathRoot.cpp index 8b86c5965b..348b4e9cab 100644 --- a/src/mathed/InsetMathRoot.cpp +++ b/src/mathed/InsetMathRoot.cpp @@ -13,7 +13,6 @@ #include "InsetMathRoot.h" -#include "MathData.h" #include "MathStream.h" #include "MathSupport.h" @@ -28,6 +27,7 @@ using namespace std; namespace lyx { +using namespace frontend; InsetMathRoot::InsetMathRoot(Buffer * buf) : InsetMathNest(buf, 2) @@ -40,56 +40,101 @@ Inset * InsetMathRoot::clone() const } -void InsetMathRoot::metrics(MetricsInfo & mi, Dimension & dim) const +void mathed_root_metrics(MetricsInfo & mi, MathData const & nucleus, + MathData const * root, Dimension & dim) { Changer dummy = mi.base.changeEnsureMath(); - Dimension dim0; - { + Dimension dimr; + if (root) { Changer script = mi.base.font.changeStyle(LM_ST_SCRIPTSCRIPT); - cell(0).metrics(mi, dim0); + root->metrics(mi, dimr); // make sure that the dim is high enough for any character Dimension fontDim; math_font_max_dim(mi.base.font, fontDim.asc, fontDim.des); - dim0 += fontDim; + dimr += fontDim; } - Dimension dim1; - cell(1).metrics(mi, dim1); + Dimension dimn; + nucleus.metrics(mi, dimn); // make sure that the dim is high enough for any character - Dimension fontDim; - math_font_max_dim(mi.base.font, fontDim.asc, fontDim.des); - dim1 += fontDim; + // Dimension fontDim; + // math_font_max_dim(mi.base.font, fontDim.asc, fontDim.des); + // dimn += fontDim; - dim.asc = max(dim0.ascent() + 5, dim1.ascent()) + 1; - dim.des = max(dim0.descent() - 5, dim1.descent()); - dim.wid = dim0.width() + dim1.width() + 4; + // Some room for the decoration + // The width of left decoration was 9 pixels with a 10em font + int const w = 9 * mathed_font_em(mi.base.font) / 10; + /* See rule 11 in Appendix G of Rhe TeXbook for the computation of the spacing + * above nucleus. + * FIXME more work is needed to implement properly rule 11. + * * Ideally, we should use sqrt glyphs from the math fonts. Note + that then we would get rule thickness from there. + * * The positioning of the root MathData is arbitrary. It should + * follow the definition of \root...\of... in The Texbook in + * Apprendix B page 360. + * + */ + int const t = mi.base.solidLineThickness(); + int const x_height = mathed_font_x_height(mi.base.font); + int const phi = (mi.base.font.style() == LM_ST_DISPLAY) ? x_height : t; + // first part is the spacing, second part is the line width + // itself, and last one is the spacing above. + int const space_above = (t + phi / 4) + t + t; + int const a = dimn.ascent(); + int const d = dimn.descent(); + // Not sure what the 1 stands for, it is needed to have some spacing at small sizes. + dim.asc = max(dimr.ascent() + (d - a) / 2, a + space_above) + 1; + dim.des = max(dimr.descent() - (d - a) / 2, d); + dim.wid = max(dimr.width() + 3 * w / 8, w) + dimn.width(); +} + + +void InsetMathRoot::metrics(MetricsInfo & mi, Dimension & dim) const +{ + mathed_root_metrics(mi, cell(1), &cell(0), dim); +} + + +void mathed_draw_root(PainterInfo & pi, int x, int y, MathData const & nucleus, + MathData const * root, Dimension const & dim) +{ + Changer dummy = pi.base.changeEnsureMath(); + // The width of left decoration was 9 pixels with a 10em font + int const w = 9 * mathed_font_em(pi.base.font) / 10; + // the height of the hook was 5 with a 10em font + int const h = 5 * mathed_font_em(pi.base.font) / 10; + int const a = dim.ascent(); + int const d = dim.descent(); + int const t = pi.base.solidLineThickness(); + Dimension const dimn = nucleus.dimension(*pi.base.bv); + // the width of the left part of the root + int const wl = dim.width() - dimn.width(); + // the "exponent" + if (root) { + Changer script = pi.base.font.changeStyle(LM_ST_SCRIPTSCRIPT); + Dimension const dimr = root->dimension(*pi.base.bv); + int const root_offset = wl - 3 * w / 8 - dimr.width(); + root->draw(pi, x + root_offset, y + (d - a)/2); + } + // the "base" + nucleus.draw(pi, x + wl, y); + int xp[4]; + int yp[4]; + pi.pain.line(x + dim.width(), y - a + 2 * t, + x + wl, y - a + 2 * t, pi.base.font.color(), + Painter::line_solid, t); + xp[0] = x + wl; yp[0] = y - a + 2 * t + 1; + xp[1] = x + wl - w / 2; yp[1] = y + d; + xp[2] = x + wl - w + h / 4; yp[2] = y + d - h; + xp[3] = x + wl - w; yp[3] = y + d - h + h / 4; + pi.pain.lines(xp, yp, 4, pi.base.font.color(), + Painter::fill_none, Painter::line_solid, t); } void InsetMathRoot::draw(PainterInfo & pi, int x, int y) const { - Changer dummy = pi.base.changeEnsureMath(); - Dimension const dim = dimension(*pi.base.bv); - int const a = dim.ascent(); - int const d = dim.descent(); - Dimension const & dim0 = cell(0).dimension(*pi.base.bv); - int const w = dim0.width(); - // the "exponent" - { - Changer script = pi.base.font.changeStyle(LM_ST_SCRIPTSCRIPT); - cell(0).draw(pi, x, y + (d - a)/2 - dim0.descent()); - } - // the "base" - cell(1).draw(pi, x + w + 4, y); - int xp[4]; - int yp[4]; - pi.pain.line(x + dim.width(), y - a + 1, - x + w + 4, y - a + 1, pi.base.font.color()); - xp[0] = x + w + 4; yp[0] = y - a + 1; - xp[1] = x + w; yp[1] = y + d; - xp[2] = x + w - 2; yp[2] = y + (d - a)/2 + 2; - xp[3] = x + w - 5; yp[3] = y + (d - a)/2 + 4; - pi.pain.lines(xp, yp, 4, pi.base.font.color()); + mathed_draw_root(pi, x, y, cell(1), &cell(0), dimension(*pi.base.bv)); } diff --git a/src/mathed/InsetMathRoot.h b/src/mathed/InsetMathRoot.h index 18bacbbf18..923e9a5f61 100644 --- a/src/mathed/InsetMathRoot.h +++ b/src/mathed/InsetMathRoot.h @@ -54,6 +54,11 @@ private: virtual Inset * clone() const; }; +void mathed_root_metrics(MetricsInfo & mi, MathData const & nucleus, + MathData const * root, Dimension & dim); + +void mathed_draw_root(PainterInfo & pi, int x, int y, MathData const & nucleus, + MathData const * root, Dimension const & dim); } // namespace lyx diff --git a/src/mathed/InsetMathSqrt.cpp b/src/mathed/InsetMathSqrt.cpp index a7385cc708..7416c332f1 100644 --- a/src/mathed/InsetMathSqrt.cpp +++ b/src/mathed/InsetMathSqrt.cpp @@ -12,6 +12,7 @@ #include "InsetMathSqrt.h" +#include "InsetMathRoot.h" #include "MathData.h" #include "MathStream.h" #include "MathSupport.h" @@ -38,33 +39,13 @@ Inset * InsetMathSqrt::clone() const void InsetMathSqrt::metrics(MetricsInfo & mi, Dimension & dim) const { - Changer dummy = mi.base.changeEnsureMath(); - cell(0).metrics(mi, dim); - // make sure that the dim is high enough for any character - Dimension fontDim; - math_font_max_dim(mi.base.font, fontDim.asc, fontDim.des); - dim += fontDim; - // Some room for the decoration - dim.asc += 1; - dim.wid += 7; + mathed_root_metrics(mi, cell(0), nullptr, dim); } void InsetMathSqrt::draw(PainterInfo & pi, int x, int y) const { - Changer dummy = pi.base.changeEnsureMath(); - cell(0).draw(pi, x + 9, y); - Dimension const dim = dimension(*pi.base.bv); - int const a = dim.ascent(); - int const d = dim.descent(); - int xp[3]; - int yp[3]; - pi.pain.line(x + dim.width(), y - a + 1, - x + 7, y - a + 1, pi.base.font.color()); - xp[0] = x + 7; yp[0] = y - a + 1; - xp[1] = x + 4; yp[1] = y + d - 1; - xp[2] = x; yp[2] = y + (d - a)/2; - pi.pain.lines(xp, yp, 3, pi.base.font.color()); + mathed_draw_root(pi, x, y, cell(0), nullptr, dimension(*pi.base.bv)); } diff --git a/src/mathed/MathSupport.cpp b/src/mathed/MathSupport.cpp index bb6a21c74d..dfe2438499 100644 --- a/src/mathed/MathSupport.cpp +++ b/src/mathed/MathSupport.cpp @@ -82,6 +82,7 @@ namespace { /* * Internal struct of a drawing: code n x1 y1 ... xn yn, where code is: * 0 = end, 1 = line, 2 = polyline, 3 = square line, 4 = square polyline + * 5 = rounded thick line (i.e. dot for short line) */ @@ -293,9 +294,18 @@ double const hline[] = { }; +double const dot[] = { +// 1, 0.5, 0.2, 0.5, 0.2, +// 1, 0.4, 0.4, 0.6, 0.4, +// 1, 0.5, 0.5, 0.5, 0.5, + 5, 0.4, 0.4, 0.6, 0.4, + 0 +}; + + double const ddot[] = { - 1, 0.2, 0.5, 0.3, 0.5, - 1, 0.7, 0.5, 0.8, 0.5, + 5, 0.0, 0.4, 0.3, 0.4, + 5, 0.6, 0.4, 1.0, 0.4, 0 }; @@ -333,12 +343,6 @@ double const dline3[] = { }; -double const hlinesmall[] = { - 1, 0.4, 0.5, 0.6, 0.5, - 0 -}; - - double const ring[] = { 2, 5, 0.5, 0.8, 0.8, 0.5, 0.5, 0.2, 0.2, 0.5, 0.5, 0.8, @@ -457,7 +461,7 @@ named_deco_struct deco_table[] = { {"acute", slash, 0 }, {"tilde", tilde, 0 }, {"bar", hline, 0 }, - {"dot", hlinesmall, 0 }, + {"dot", dot, 0 }, {"check", angle, 1 }, {"breve", parenth, 1 }, {"vec", arrow, 3 }, @@ -513,6 +517,12 @@ int mathed_font_em(FontInfo const & font) return theFontMetrics(font).em(); } + +int mathed_font_x_height(FontInfo const & font) +{ + return theFontMetrics(font).ascent('x'); +} + /* The math units. Quoting TeX by Topic, p.205: * * Spacing around mathematical objects is measured in mu units. A mu @@ -617,7 +627,7 @@ void mathed_draw_deco(PainterInfo & pi, int x, int y, int w, int h, for (int i = 0; d[i]; ) { int code = int(d[i++]); - if (code & 1) { // code == 1 || code == 3 + if (code & 1) { // code == 1 || code == 3 || code == 5 double xx = d[i++]; double yy = d[i++]; double x2 = d[i++]; @@ -631,6 +641,16 @@ void mathed_draw_deco(PainterInfo & pi, int x, int y, int w, int h, int(x + xx + 0.5), int(y + yy + 0.5), int(x + x2 + 0.5), int(y + y2 + 0.5), pi.base.font.color()); + if (code == 5) { // thicker, but rounded + pi.pain.line( + int(x + xx + 0.5+1), int(y + yy + 0.5-1), + int(x + x2 + 0.5-1), int(y + y2 + 0.5-1), + pi.base.font.color()); + pi.pain.line( + int(x + xx + 0.5+1), int(y + yy + 0.5+1), + int(x + x2 + 0.5-1), int(y + y2 + 0.5+1), + pi.base.font.color()); + } } else { int xp[32]; int yp[32]; diff --git a/src/mathed/MathSupport.h b/src/mathed/MathSupport.h index 3e5f67d5cf..d9301453dd 100644 --- a/src/mathed/MathSupport.h +++ b/src/mathed/MathSupport.h @@ -32,6 +32,8 @@ class LaTeXFeatures; int mathed_font_em(FontInfo const &); +int mathed_font_x_height(FontInfo const & font); + int mathed_mu(FontInfo const & font, double mu); int mathed_thinmuskip(FontInfo const & font); diff --git a/src/output_latex.cpp b/src/output_latex.cpp index 0ee9af1003..fa890f5b3c 100644 --- a/src/output_latex.cpp +++ b/src/output_latex.cpp @@ -142,11 +142,16 @@ string const getPolyglossiaEnvName(Language const * lang) string const getPolyglossiaBegin(string const & lang_begin_command, - string const & lang, string const & opts) + string const & lang, string const & opts, + bool const localswitch = false) { string result; - if (!lang.empty()) - result = subst(lang_begin_command, "$$lang", lang); + if (!lang.empty()) { + // we need to revert the upcasing done in getPolyglossiaEnvName() + // in case we have a local polyglossia command (\textarabic). + string language = localswitch ? ascii_lowercase(lang) : lang; + result = subst(lang_begin_command, "$$lang", language); + } string options = opts.empty() ? string() : "[" + opts + "]"; result = subst(result, "$$opts", options); @@ -683,7 +688,8 @@ void TeXOnePar(Buffer const & buf, otexstream & os, OutputParams const & runparams_in, string const & everypar, - int start_pos, int end_pos) + int start_pos, int end_pos, + bool const force) { BufferParams const & bparams = runparams_in.is_child ? buf.masterParams() : buf.params(); @@ -694,7 +700,7 @@ void TeXOnePar(Buffer const & buf, Layout const & style = text.inset().forcePlainLayout() ? bparams.documentClass().plainLayout() : par.layout(); - if (style.inpreamble) + if (style.inpreamble && !force) return; LYXERR(Debug::LATEX, "TeXOnePar for paragraph " << pit << " ptr " << &par << " '" @@ -728,18 +734,22 @@ void TeXOnePar(Buffer const & buf, os << '\n'; } - par.latex(bparams, outerfont, os, runparams, start_pos, end_pos); + par.latex(bparams, outerfont, os, runparams, start_pos, end_pos, force); return; } Paragraph const * nextpar = runparams.isLastPar ? 0 : ¶graphs.at(pit + 1); + bool const intitle_command = style.intitle && style.latextype == LATEX_COMMAND; + if (style.pass_thru) { Font const outerfont = text.outerFont(pit); parStartCommand(par, os, runparams, style); + if (intitle_command) + os << '{'; - par.latex(bparams, outerfont, os, runparams, start_pos, end_pos); + par.latex(bparams, outerfont, os, runparams, start_pos, end_pos, force); // I did not create a parEndCommand for this minuscule // task because in the other user of parStartCommand @@ -812,6 +822,13 @@ void TeXOnePar(Buffer const & buf, bool const using_begin_end = use_polyglossia || !lang_end_command.empty(); + // For InTitle commands, we need to switch the language inside the command + // (see #10849); thus open the command here. + if (intitle_command) { + parStartCommand(par, os, runparams, style); + os << '{'; + } + // In some insets (such as Arguments), we cannot use \selectlanguage bool const localswitch = text.inset().forceLocalFontSwitch() || (using_begin_end && text.inset().forcePlainLayout()); @@ -893,7 +910,9 @@ void TeXOnePar(Buffer const & buf, && (par_lang != openLanguageName(state) || localswitch) && !par_lang.empty()) { string bc = use_polyglossia ? - getPolyglossiaBegin(lang_begin_command, par_lang, par_language->polyglossiaOpts()) + getPolyglossiaBegin(lang_begin_command, par_lang, + par_language->polyglossiaOpts(), + localswitch) : subst(lang_begin_command, "$$lang", par_lang); os << bc; os << lang_command_termination; @@ -952,11 +971,13 @@ void TeXOnePar(Buffer const & buf, if (runparams.encoding->package() == Encoding::CJK && par_lang != openLanguageName(state) && !par_lang.empty()) { - os << from_ascii(subst( - lang_begin_command, - "$$lang", - par_lang)) - << lang_command_termination; + string bc = use_polyglossia ? + getPolyglossiaBegin(lang_begin_command, par_lang, + par_language->polyglossiaOpts(), + localswitch) + : subst(lang_begin_command, "$$lang", par_lang); + os << bc + << lang_command_termination; if (using_begin_end) pushLanguageName(par_lang, localswitch); } @@ -975,24 +996,39 @@ void TeXOnePar(Buffer const & buf, os << "\n\\appendix\n"; } - if (!par.params().spacing().isDefault() - && (pit == 0 || !priorpar->hasSameLayout(par))) - { - os << from_ascii(par.params().spacing().writeEnvirBegin(useSetSpace)) - << '\n'; - } + // InTitle commands must use switches (not environments) + // inside the commands (see #9332) + if (style.intitle) { + if (!par.params().spacing().isDefault()) + { + if (runparams.moving_arg) + os << "\\protect"; + os << from_ascii(par.params().spacing().writeCmd(useSetSpace)); + } + } else { + if (!par.params().spacing().isDefault() + && (pit == 0 || !priorpar->hasSameLayout(par))) + { + os << from_ascii(par.params().spacing().writeEnvirBegin(useSetSpace)) + << '\n'; + } - if (style.isCommand()) { - os << '\n'; + if (style.isCommand()) { + os << '\n'; + } } } - parStartCommand(par, os, runparams, style); + // For InTitle commands, we already started the command before + // the language switch + if (!intitle_command) + parStartCommand(par, os, runparams, style); + Font const outerfont = text.outerFont(pit); // FIXME UNICODE os << from_utf8(everypar); - par.latex(bparams, outerfont, os, runparams, start_pos, end_pos); + par.latex(bparams, outerfont, os, runparams, start_pos, end_pos, force); Font const font = par.empty() ? par.getLayoutFont(bparams, outerfont) @@ -1000,13 +1036,16 @@ void TeXOnePar(Buffer const & buf, bool const is_command = style.isCommand(); - if (is_command) { - os << '}'; - if (!style.postcommandargs().empty()) - latexArgInsets(par, os, runparams, style.postcommandargs(), "post:"); - if (runparams.encoding != prev_encoding) { - runparams.encoding = prev_encoding; - os << setEncoding(prev_encoding->iconvName()); + // InTitle commands need to be closed after the language has been closed. + if (!intitle_command) { + if (is_command) { + os << '}'; + if (!style.postcommandargs().empty()) + latexArgInsets(par, os, runparams, style.postcommandargs(), "post:"); + if (runparams.encoding != prev_encoding) { + runparams.encoding = prev_encoding; + os << setEncoding(prev_encoding->iconvName()); + } } } @@ -1039,12 +1078,13 @@ void TeXOnePar(Buffer const & buf, // possible // fall through default: - // we don't need it for the last paragraph!!! - if (nextpar) + // we don't need it for the last paragraph and in InTitle commands!!! + if (nextpar && !intitle_command) pending_newline = true; } - if (par.allowParagraphCustomization()) { + // InTitle commands use switches (not environments) for space settings + if (par.allowParagraphCustomization() && !style.intitle) { if (!par.params().spacing().isDefault() && (runparams.isLastPar || !nextpar->hasSameLayout(par))) { if (pending_newline) @@ -1060,9 +1100,10 @@ void TeXOnePar(Buffer const & buf, } } - // Closing the language is needed for the last paragraph; it is also - // needed if we're within an \L or \R that we may have opened above (not - // necessarily in this paragraph) and are about to close. + // Closing the language is needed for the last paragraph in a given language + // as well as for any InTitleCommand (since these set the language locally); + // it is also needed if we're within an \L or \R that we may have opened above + // (not necessarily in this paragraph) and are about to close. bool closing_rtl_ltr_environment = !using_begin_end // not for ArabTeX && (par_language->lang() != "arabic_arabtex" @@ -1074,7 +1115,8 @@ void TeXOnePar(Buffer const & buf, &&((nextpar && par_lang != nextpar_lang) || (runparams.isLastPar && par_lang != outer_lang)); - if (closing_rtl_ltr_environment + if ((intitle_command && using_begin_end) + || closing_rtl_ltr_environment || ((runparams.isLastPar || close_lang_switch) && (par_lang != outer_lang || (using_begin_end && style.isEnvironment() @@ -1102,7 +1144,8 @@ void TeXOnePar(Buffer const & buf, && current_lang != openLanguageName(state)) { string bc = use_polyglossia ? getPolyglossiaBegin(lang_begin_command, current_lang, - current_language->polyglossiaOpts()) + current_language->polyglossiaOpts(), + localswitch) : subst(lang_begin_command, "$$lang", current_lang); os << bc; pending_newline = !localswitch; @@ -1145,6 +1188,19 @@ void TeXOnePar(Buffer const & buf, if (closing_rtl_ltr_environment) os << "}"; + // InTitle commands need to be closed after the language has been closed. + if (intitle_command) { + if (is_command) { + os << '}'; + if (!style.postcommandargs().empty()) + latexArgInsets(par, os, runparams, style.postcommandargs(), "post:"); + if (runparams.encoding != prev_encoding) { + runparams.encoding = prev_encoding; + os << setEncoding(prev_encoding->iconvName()); + } + } + } + bool const last_was_separator = par.size() > 0 && par.isEnvSeparator(par.size() - 1); diff --git a/src/output_latex.h b/src/output_latex.h index 1dd9689851..cf37173604 100644 --- a/src/output_latex.h +++ b/src/output_latex.h @@ -91,7 +91,8 @@ void TeXOnePar(Buffer const & buf, otexstream & os, OutputParams const & runparams, std::string const & everypar = std::string(), - int start_pos = -1, int end_pos = -1); + int start_pos = -1, int end_pos = -1, + bool const force = false); } // namespace lyx diff --git a/src/support/filetools.cpp b/src/support/filetools.cpp index bcfd1b6c9e..37fd14f54d 100644 --- a/src/support/filetools.cpp +++ b/src/support/filetools.cpp @@ -1141,7 +1141,8 @@ cmd_ret const runCommand(string const & cmd) } -FileName const findtexfile(string const & fil, string const & /*format*/) +FileName const findtexfile(string const & fil, string const & /*format*/, + bool const onlykpse) { /* There is no problem to extend this function too use other methods to look for files. It could be setup to look @@ -1154,9 +1155,11 @@ FileName const findtexfile(string const & fil, string const & /*format*/) // If the file can be found directly, we just return a // absolute path version of it. - FileName const absfile(makeAbsPath(fil)); - if (absfile.exists()) - return absfile; + if (!onlykpse) { + FileName const absfile(makeAbsPath(fil)); + if (absfile.exists()) + return absfile; + } // Now we try to find it using kpsewhich. // It seems from the kpsewhich manual page that it is safe to use diff --git a/src/support/filetools.h b/src/support/filetools.h index 3a4c2c566d..29f807cdaa 100644 --- a/src/support/filetools.h +++ b/src/support/filetools.h @@ -305,7 +305,8 @@ bool readLink(FileName const & file, FileName & link); * \param format The file format as used by kpsewhich, e.g. "bib", "bst" etc. */ FileName const findtexfile(std::string const & fil, - std::string const & format); + std::string const & format, + bool const onlykpse = false); /** \param file1, file2 the two files to be compared. Must have absolute paths. * \returns 1 if \c file1 has a more recent timestamp than \c file2, diff --git a/src/tex2lyx/Context.cpp b/src/tex2lyx/Context.cpp index 61685180bd..cc95a1fc6d 100644 --- a/src/tex2lyx/Context.cpp +++ b/src/tex2lyx/Context.cpp @@ -247,6 +247,8 @@ void Context::dump(ostream & os, string const & desc) const os << "extrastuff=[" << extra_stuff << "] "; if (!par_extra_stuff.empty()) os << "parextrastuff=[" << par_extra_stuff << "] "; + if (!list_extra_stuff.empty()) + os << "listextrastuff=[" << list_extra_stuff << "] "; os << "textclass=" << textclass.name() << " layout=" << to_utf8(layout->name()) << " parent_layout=" << to_utf8(parent_layout->name()) << "] font=[" diff --git a/src/tex2lyx/Context.h b/src/tex2lyx/Context.h index b88a921f33..e48bdf35d6 100644 --- a/src/tex2lyx/Context.h +++ b/src/tex2lyx/Context.h @@ -128,6 +128,10 @@ public: std::string extra_stuff; /// We may need to add something after this \\begin_layout command std::string par_extra_stuff; + /// We may need to add something at the beginning of a list. + std::string list_extra_stuff; + /// A LaTeXParam to be ignored in parsing. + std::string latexparam; /// If there has been an \\begin_deeper, we'll need a matching /// \\end_deeper bool need_end_deeper; diff --git a/src/tex2lyx/Makefile.am b/src/tex2lyx/Makefile.am index 7a744abc0b..da7c3183a4 100644 --- a/src/tex2lyx/Makefile.am +++ b/src/tex2lyx/Makefile.am @@ -25,6 +25,7 @@ TEST_FILES = \ test/runtests.cmake \ test/runtests.py \ test/algo2e.tex \ + test/beamer.tex \ test/box-color-size-space-align.tex \ test/CJK.tex \ test/CJKutf8.tex \ @@ -49,6 +50,7 @@ TEST_FILES = \ TEST_RESULTS = \ test/algo2e.lyx.lyx \ + test/beamer.lyx.lyx \ test/box-color-size-space-align.lyx.lyx \ test/CJK.lyx.lyx \ test/CJKutf8.lyx.lyx \ diff --git a/src/tex2lyx/Parser.cpp b/src/tex2lyx/Parser.cpp index ec63b77da8..94da955fea 100644 --- a/src/tex2lyx/Parser.cpp +++ b/src/tex2lyx/Parser.cpp @@ -453,7 +453,7 @@ bool Parser::good() } -bool Parser::hasOpt() +bool Parser::hasOpt(string const l) { // An optional argument can occur in any of the following forms: // - \foo[bar] @@ -479,7 +479,7 @@ bool Parser::hasOpt() putback(); break; } - bool const retval = (next_token().asInput() == "["); + bool const retval = (next_token().asInput() == l); pos_ = oldpos; return retval; } @@ -494,6 +494,7 @@ Parser::Arg Parser::getFullArg(char left, char right, bool allow_escaping) if (! good()) return make_pair(false, string()); + int group_level = 0; string result; Token t = get_token(); @@ -504,6 +505,15 @@ Parser::Arg Parser::getFullArg(char left, char right, bool allow_escaping) } else { while (good()) { t = get_token(); + // honor grouping + if (left != '{' && t.cat() == catBegin) { + ++group_level; + continue; + } + if (left != '{' && t.cat() == catEnd) { + --group_level; + continue; + } // Ignore comments if (t.cat() == catComment) { if (!t.cs().empty()) @@ -511,13 +521,15 @@ Parser::Arg Parser::getFullArg(char left, char right, bool allow_escaping) continue; } if (allow_escaping) { - if (t.cat() != catEscape && t.character() == right) + if (t.cat() != catEscape && t.character() == right + && group_level == 0) break; } else { if (t.character() == right) { if (t.cat() == catEscape) result += '\\'; - break; + if (group_level == 0) + break; } } result += t.asInput(); @@ -533,11 +545,11 @@ string Parser::getArg(char left, char right, bool allow_escaping) } -string Parser::getFullOpt(bool keepws) +string Parser::getFullOpt(bool keepws, char left, char right) { - Arg arg = getFullArg('[', ']'); + Arg arg = getFullArg(left, right); if (arg.first) - return '[' + arg.second + ']'; + return left + arg.second + right; if (keepws) unskip_spaces(true); return string(); @@ -638,6 +650,27 @@ string const Parser::plainCommand(char left, char right, string const & name) } +string const Parser::getCommandLatexParam() +{ + if (!good()) + return string(); + string res; + size_t offset = 0; + while (true) { + if (pos_ + offset >= tokens_.size()) + tokenize_one(); + if (pos_ + offset >= tokens_.size()) + break; + Token t = tokens_[pos_ + offset]; + if (t.cat() == catBegin) + break; + res += t.asInput(); + ++offset; + } + return res; +} + + Parser::Arg Parser::verbatimStuff(string const & end_string, bool const allow_linebreak) { if (!good()) diff --git a/src/tex2lyx/Parser.h b/src/tex2lyx/Parser.h index cbdfcf7234..b15f95aa41 100644 --- a/src/tex2lyx/Parser.h +++ b/src/tex2lyx/Parser.h @@ -67,7 +67,8 @@ enum { FLAG_OPTION = 1 << 11, // read [...] style option FLAG_BRACED = 1 << 12, // read {...} style argument FLAG_CELL = 1 << 13, // read table cell - FLAG_TABBING = 1 << 14 // We are inside a tabbing environment + FLAG_TABBING = 1 << 14, // We are inside a tabbing environment + FLAG_RDELIM = 1 << 15, // next right delimiter ends the parsing }; @@ -213,7 +214,7 @@ public: void dump() const; /// Does an optional argument follow after the current token? - bool hasOpt(); + bool hasOpt(std::string const l = "["); /// typedef std::pair Arg; /*! @@ -240,7 +241,8 @@ public: * Like getOpt(), but distinguishes between a missing argument "" * and an empty argument "[]". */ - std::string getFullOpt(bool keepws = false); + std::string getFullOpt(bool keepws = false, + char left = '[', char right = ']'); /*! * \returns getArg('[', ']') including the brackets or the * empty string if there is no such argument. @@ -276,6 +278,11 @@ public: * This function is designed to parse verbatim commands. */ std::string const plainCommand(char left, char right, std::string const & name); + /* + * Returns everything before the main command argument. + * This is where the LaTeXParam value of a layout is output. + */ + std::string const getCommandLatexParam(); /* * Basically the same as plainEnvironment() but the parsing is * stopped at string \p end_string. Contrary to the other diff --git a/src/tex2lyx/Preamble.cpp b/src/tex2lyx/Preamble.cpp index fe722f48e2..436c961545 100644 --- a/src/tex2lyx/Preamble.cpp +++ b/src/tex2lyx/Preamble.cpp @@ -88,35 +88,51 @@ const char * const known_coded_languages[] = {"french", "afrikaans", "albanian", "vietnamese", "welsh", 0}; +/// languages with british quotes (.lyx names) +const char * const known_british_quotes_languages[] = {"british", "welsh", 0}; + +/// languages with cjk quotes (.lyx names) +const char * const known_cjk_quotes_languages[] = {"chinese-traditional", +"japanese", "japanese-cjk", 0}; + +/// languages with cjk-angle quotes (.lyx names) +const char * const known_cjkangle_quotes_languages[] = {"korean", 0}; + /// languages with danish quotes (.lyx names) const char * const known_danish_quotes_languages[] = {"danish", 0}; /// languages with english quotes (.lyx names) const char * const known_english_quotes_languages[] = {"american", "australian", "bahasa", "bahasam", "brazilian", "canadian", "chinese-simplified", "english", -"esperanto", "hebrew", "irish", "korean", "newzealand", "portuguese", "scottish", -"thai", 0}; +"esperanto", "farsi", "interlingua", "irish", "newzealand", "scottish", +"thai", "turkish", "vietnamese", 0}; /// languages with french quotes (.lyx names) -const char * const known_french_quotes_languages[] = {"albanian", -"arabic_arabi", "arabic_arabtex", "asturian", "basque", "canadien", "catalan", -"french", "friulan", "galician", "greek", "italian", "norsk", "nynorsk", -"piedmontese", "polutonikogreek", "russian", "spanish", "spanish-mexico", -"turkish", "turkmen", "ukrainian", "vietnamese", 0}; +const char * const known_french_quotes_languages[] = {"ancientgreek", +"arabic_arabi", "arabic_arabtex", "asturian", "belarusian", "breton", +"canadien", "catalan", "french", "friulan", "galician", "italian", "occitan", +"piedmontese", "portuguese", "spanish", "spanish-mexico", 0}; /// languages with german quotes (.lyx names) const char * const known_german_quotes_languages[] = {"austrian", "bulgarian", -"czech", "german", "georgian", "icelandic", "lithuanian", "lowersorbian", "macedonian", -"naustrian", "ngerman", "romansh", "serbian", "serbian-latin", "slovak", "slovene", +"czech", "estonian", "georgian", "german", "icelandic", "latvian", "lithuanian", +"lowersorbian", "macedonian", "naustrian", "ngerman", "romansh", "slovak", "slovene", "uppersorbian", 0}; /// languages with polish quotes (.lyx names) const char * const known_polish_quotes_languages[] = {"afrikaans", "bosnian", "croatian", -"dutch", "estonian", "magyar", "polish", "romanian", 0}; +"dutch", "magyar", "polish", "romanian", "serbian", "serbian-latin", 0}; + +/// languages with russian quotes (.lyx names) +const char * const known_russian_quotes_languages[] = {"russian", "ukrainian", 0}; /// languages with swedish quotes (.lyx names) -const char * const known_swedish_quotes_languages[] = {"finnish", -"swedish", 0}; +const char * const known_swedish_quotes_languages[] = {"finnish", "swedish", 0}; + +/// languages with swiss quotes (.lyx names) +const char * const known_swiss_quotes_languages[] = {"albanian", +"armenian", "basque", "german-ch", "german-ch-old", +"norsk", "nynorsk", "turkmen", "ukrainian", "vietnamese", 0}; /// known language packages from the times before babel const char * const known_old_language_packages[] = {"french", "frenchle", @@ -126,17 +142,18 @@ char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 }; const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman", "ccfonts", "chancery", "charter", "cmr", "cochineal", "crimson", "fourier", -"garamondx", "libertine", "libertine-type1", "lmodern", "mathdesign", "mathpazo", -"mathptmx", "newcent", "NotoSerif-TLF", "tgbonum", "tgchorus", "tgpagella", "tgschola", -"tgtermes", "utopia", 0}; +"garamondx", "libertine", "libertineRoman", "libertine-type1", "lmodern", "mathdesign", +"mathpazo", "mathptmx", "MinionPro", "newcent", "NotoSerif-TLF", "tgbonum", "tgchorus", +"tgpagella", "tgschola", "tgtermes", "utopia", 0 }; -const char * const known_sans_fonts[] = { "avant", "berasans", "biolinum-type1", -"cmbr", "cmss", "helvet", "iwona", "iwonac", "iwonal", "iwonalc", "kurier", -"kurierc", "kurierl", "kurierlc", "lmss", "NotoSans-TLF", "tgadventor", "tgheros", 0}; +const char * const known_sans_fonts[] = { "avant", "berasans", "biolinum", +"biolinum-type1", "cmbr", "cmss", "helvet", "iwona", "iwonac", "iwonal", "iwonalc", +"kurier", "kurierc", "kurierl", "kurierlc", "lmss", "NotoSans-TLF", +"tgadventor", "tgheros", "uop", 0 }; const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt", -"courier", "lmtt", "luximono", "fourier", "libertineMono-type1", "lmodern", -"mathpazo", "mathptmx", "newcent", "NotoMono-TLF", "tgcursor", "txtt", 0}; +"courier", "lmtt", "luximono", "fourier", "libertineMono", "libertineMono-type1", "lmodern", +"mathpazo", "mathptmx", "newcent", "NotoMono-TLF", "tgcursor", "txtt", 0 }; const char * const known_math_fonts[] = { "eulervm", "newtxmath", 0}; @@ -173,6 +190,11 @@ const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists", 0}; +/*! + * Known file extensions for TeX files as used by \\includeonly + */ +char const * const known_tex_extensions[] = {"tex", 0}; + /// packages that work only in xetex /// polyglossia is handled separately const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian", @@ -259,7 +281,7 @@ vector split_options(string const & input) p.skip_spaces(true); if (p.next_token().asInput() == "{") option += '{' + p.getArg('{', '}') + '}'; - } else if (t.cat() != catSpace) + } else if (t.cat() != catSpace && t.cat() != catComment) option += t.asInput(); } @@ -418,6 +440,15 @@ void Preamble::add_package(string const & name, vector & options) if (used_packages.find(name) == used_packages.end()) used_packages[name] = split_options(h_options); + // Insert options passed via PassOptionsToPackage + for (auto const & p : extra_package_options_) { + if (p.first == name) { + vector eo = getVectorFromString(p.second); + for (auto const & eoi : eo) + options.push_back(eoi); + } + + } vector & v = used_packages[name]; v.insert(v.end(), options.begin(), options.end()); if (name == "jurabib") { @@ -553,7 +584,7 @@ Preamble::Preamble() : one_language(true), explicit_babel(false), h_use_geometry = "false"; h_use_default_options = "false"; h_use_hyperref = "false"; - h_use_microtype = "false"; + h_use_microtype = "false"; h_use_refstyle = false; h_use_minted = false; h_use_packages["amsmath"] = "1"; @@ -719,22 +750,37 @@ void Preamble::handle_package(Parser &p, string const & name, h_font_roman[0] = "libertine"; // this automatically invokes biolinum h_font_sans[0] = "biolinum"; + // as well as libertineMono + h_font_typewriter[0] = "libertine-mono"; if (opts == "osf") h_font_osf = "true"; else if (opts == "lining") h_font_osf = "false"; } - if (name == "libertine-type1") { + if (name == "libertineRoman" || name == "libertine-type1") { h_font_roman[0] = "libertine"; - // NOTE: contrary to libertine.sty, libertine-type1 - // does not automatically invoke biolinum + // NOTE: contrary to libertine.sty, libertineRoman + // and libertine-type1 do not automatically invoke + // biolinum and libertineMono if (opts == "lining") h_font_osf = "false"; else if (opts == "osf") h_font_osf = "true"; } + if (name == "MinionPro") { + h_font_roman[0] = "minionpro"; + if (opts.find("lf") != string::npos) + h_font_osf = "false"; + else + h_font_osf = "true"; + if (opts.find("onlytext") != string::npos) + h_font_math[0] = "default"; + else + h_font_math[0] = "auto"; + } + if (name == "mathdesign") { if (opts.find("charter") != string::npos) h_font_roman[0] = "md-charter"; @@ -793,7 +839,7 @@ void Preamble::handle_package(Parser &p, string const & name, } } - if (name == "biolinum-type1") { + if (name == "biolinum" || name == "biolinum-type1") { h_font_sans[0] = "biolinum"; // biolinum can have several options, e.g. [osf,scaled=0.97] string::size_type pos = opts.find("osf"); @@ -814,9 +860,8 @@ void Preamble::handle_package(Parser &p, string const & name, } } - if (name == "libertineMono-type1") { + if (name == "libertineMono" || name == "libertineMono-type1") h_font_typewriter[0] = "libertine-mono"; - } // font uses old-style figure if (name == "eco") @@ -1033,17 +1078,67 @@ void Preamble::handle_package(Parser &p, string const & name, options.erase(it); } } + if (!options.empty()) + h_biblio_options = join(options, ","); + } + + else if (name == "biblatex") { + h_biblio_style = "plainnat"; + h_cite_engine = "biblatex"; + h_cite_engine_type = "authoryear"; + string opt; + vector::iterator it = + find(options.begin(), options.end(), "natbib"); + if (it != options.end()) { + options.erase(it); + h_cite_engine = "biblatex-natbib"; + } else { + opt = process_keyval_opt(options, "natbib"); + if (opt == "true") + h_cite_engine = "biblatex-natbib"; + } + opt = process_keyval_opt(options, "style"); + if (!opt.empty()) { + h_biblatex_citestyle = opt; + h_biblatex_bibstyle = opt; + } else { + opt = process_keyval_opt(options, "citestyle"); + if (!opt.empty()) + h_biblatex_citestyle = opt; + opt = process_keyval_opt(options, "bibstyle"); + if (!opt.empty()) + h_biblatex_bibstyle = opt; + } + opt = process_keyval_opt(options, "refsection"); + if (!opt.empty()) { + if (opt == "none" || opt == "part" + || opt == "chapter" || opt == "section" + || opt == "subsection") + h_multibib = opt; + else + cerr << "Ignoring unkown refesection value '" + << opt << "'."; + } + if (!options.empty()) { + h_biblio_options = join(options, ","); + options.clear(); + } } else if (name == "jurabib") { h_biblio_style = "jurabib"; h_cite_engine = "jurabib"; h_cite_engine_type = "authoryear"; + if (!options.empty()) + h_biblio_options = join(options, ","); } else if (name == "bibtopic") h_use_bibtopic = "true"; + else if (name == "chapterbib") + h_multibib = "child"; + else if (name == "hyperref") handle_hyperref(options); @@ -1109,33 +1204,6 @@ void Preamble::handle_if(Parser & p, bool in_lyx_preamble) bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir) { - // set the quote language - // LyX only knows the following quotes languages: - // english, swedish, german, polish, french and danish - // (quotes for "japanese" and "chinese-traditional" are missing because - // they wouldn't be useful: https://www.lyx.org/trac/ticket/6383) - // conversion list taken from - // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage - // (quotes for kazakh and interlingua are unknown) - // danish - if (is_known(h_language, known_danish_quotes_languages)) - h_quotes_style = "danish"; - // french - else if (is_known(h_language, known_french_quotes_languages)) - h_quotes_style = "french"; - // german - else if (is_known(h_language, known_german_quotes_languages)) - h_quotes_style = "german"; - // polish - else if (is_known(h_language, known_polish_quotes_languages)) - h_quotes_style = "polish"; - // swedish - else if (is_known(h_language, known_swedish_quotes_languages)) - h_quotes_style = "swedish"; - //english - else if (is_known(h_language, known_english_quotes_languages)) - h_quotes_style = "english"; - if (contains(h_float_placement, "H")) registerAutomaticallyLoadedPackage("float"); if (h_spacing != "single" && h_spacing != "default") @@ -1193,6 +1261,12 @@ bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiled os << *it << '\n'; os << "\\end_modules\n"; } + if (!h_includeonlys.empty()) { + os << "\\begin_includeonly\n"; + for (auto const & iofile : h_includeonlys) + os << iofile << '\n'; + os << "\\end_includeonly\n"; + } os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n" << "\\language " << h_language << "\n" << "\\language_package " << h_language_package << "\n" @@ -1259,8 +1333,16 @@ bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiled os << "\\cite_engine " << h_cite_engine << '\n' << "\\cite_engine_type " << h_cite_engine_type << '\n' << "\\biblio_style " << h_biblio_style << "\n" - << "\\use_bibtopic " << h_use_bibtopic << "\n" - << "\\use_indices " << h_use_indices << "\n" + << "\\use_bibtopic " << h_use_bibtopic << "\n"; + if (!h_biblio_options.empty()) + os << "\\biblio_options " << h_biblio_options << "\n"; + if (!h_biblatex_bibstyle.empty()) + os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n"; + if (!h_biblatex_citestyle.empty()) + os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n"; + if (!h_multibib.empty()) + os << "\\multibib " << h_multibib << "\n"; + os << "\\use_indices " << h_use_indices << "\n" << "\\paperorientation " << h_paperorientation << '\n' << "\\suppress_date " << h_suppress_date << '\n' << "\\justification " << h_justification << '\n' @@ -1373,14 +1455,18 @@ void Preamble::parse(Parser & p, string const & forceclass, t.cat() == catBegin || t.cat() == catEnd || t.cat() == catAlign || - t.cat() == catParameter)) + t.cat() == catParameter)) { h_preamble << t.cs(); + continue; + } - else if (!in_lyx_preamble && - (t.cat() == catSpace || t.cat() == catNewline)) + if (!in_lyx_preamble && + (t.cat() == catSpace || t.cat() == catNewline)) { h_preamble << t.asInput(); + continue; + } - else if (t.cat() == catComment) { + if (t.cat() == catComment) { static regex const islyxfile("%% LyX .* created this file"); static regex const usercommands("User specified LaTeX commands"); @@ -1405,12 +1491,22 @@ void Preamble::parse(Parser & p, string const & forceclass, in_lyx_preamble = false; else if (!in_lyx_preamble) h_preamble << t.asInput(); + continue; } - else if (t.cs() == "pagestyle") - h_paperpagestyle = p.verbatim_item(); + if (t.cs() == "PassOptionsToPackage") { + string const poptions = p.getArg('{', '}'); + string const package = p.verbatim_item(); + extra_package_options_.insert(make_pair(package, poptions)); + continue; + } - else if (t.cs() == "setdefaultlanguage") { + if (t.cs() == "pagestyle") { + h_paperpagestyle = p.verbatim_item(); + continue; + } + + if (t.cs() == "setdefaultlanguage") { xetex = true; // We don't yet care about non-language variant options // because LyX doesn't support this yet, see bug #8214 @@ -1433,22 +1529,25 @@ void Preamble::parse(Parser & p, string const & forceclass, h_language = p.verbatim_item(); //finally translate the poyglossia name to a LyX name h_language = polyglossia2lyx(h_language); + continue; } - else if (t.cs() == "setotherlanguage") { + if (t.cs() == "setotherlanguage") { // We don't yet care about the option because LyX doesn't // support this yet, see bug #8214 p.hasOpt() ? p.getOpt() : string(); p.verbatim_item(); + continue; } - else if (t.cs() == "setmainfont") { + if (t.cs() == "setmainfont") { // we don't care about the option p.hasOpt() ? p.getOpt() : string(); h_font_roman[1] = p.getArg('{', '}'); + continue; } - else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") { + if (t.cs() == "setsansfont" || t.cs() == "setmonofont") { // LyX currently only supports the scale option string scale; if (p.hasOpt()) { @@ -1472,17 +1571,19 @@ void Preamble::parse(Parser & p, string const & forceclass, h_font_tt_scale[1] = scale; h_font_typewriter[1] = p.getArg('{', '}'); } + continue; } - else if (t.cs() == "date") { + if (t.cs() == "date") { string argument = p.getArg('{', '}'); if (argument.empty()) h_suppress_date = "true"; else h_preamble << t.asInput() << '{' << argument << '}'; + continue; } - else if (t.cs() == "color") { + if (t.cs() == "color") { string const space = (p.hasOpt() ? p.getOpt() : string()); string argument = p.getArg('{', '}'); @@ -1503,9 +1604,10 @@ void Preamble::parse(Parser & p, string const & forceclass, // is parsed before this h_fontcolor = ""; } + continue; } - else if (t.cs() == "pagecolor") { + if (t.cs() == "pagecolor") { string argument = p.getArg('{', '}'); // check the case that a standard color is used if (is_known(argument, known_basic_colors)) { @@ -1520,24 +1622,28 @@ void Preamble::parse(Parser & p, string const & forceclass, // is parsed before this h_backgroundcolor = ""; } + continue; } - else if (t.cs() == "makeatletter") { + if (t.cs() == "makeatletter") { // LyX takes care of this p.setCatcode('@', catLetter); + continue; } - else if (t.cs() == "makeatother") { + if (t.cs() == "makeatother") { // LyX takes care of this p.setCatcode('@', catOther); + continue; } - else if (t.cs() == "makeindex") { + if (t.cs() == "makeindex") { // LyX will re-add this if a print index command is found p.skip_spaces(); + continue; } - else if (t.cs() == "newindex") { + if (t.cs() == "newindex") { string const indexname = p.getArg('[', ']'); string const shortcut = p.verbatim_item(); if (!indexname.empty()) @@ -1547,9 +1653,21 @@ void Preamble::parse(Parser & p, string const & forceclass, h_shortcut[index_number] = shortcut; index_number += 1; p.skip_spaces(); + continue; } - else if (t.cs() == "RS@ifundefined") { + if (t.cs() == "addbibresource") { + biblatex_bibliographies.push_back(removeExtension(p.getArg('{', '}'))); + continue; + } + + if (t.cs() == "bibliography") { + vector bibs = getVectorFromString(p.getArg('{', '}')); + biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end()); + continue; + } + + if (t.cs() == "RS@ifundefined") { string const name = p.verbatim_item(); string const body1 = p.verbatim_item(); string const body2 = p.verbatim_item(); @@ -1565,9 +1683,10 @@ void Preamble::parse(Parser & p, string const & forceclass, << '{' << body2 << '}'; h_preamble << ss.str(); } + continue; } - else if (t.cs() == "AtBeginDocument") { + if (t.cs() == "AtBeginDocument") { string const name = p.verbatim_item(); // only non-lyxspecific stuff if (in_lyx_preamble && @@ -1593,15 +1712,16 @@ void Preamble::parse(Parser & p, string const & forceclass, ss << '{' << name << '}'; h_preamble << ss.str(); } + continue; } - else if (t.cs() == "newcommand" || t.cs() == "newcommandx" - || t.cs() == "renewcommand" || t.cs() == "renewcommandx" - || t.cs() == "providecommand" || t.cs() == "providecommandx" - || t.cs() == "DeclareRobustCommand" - || t.cs() == "DeclareRobustCommandx" - || t.cs() == "ProvideTextCommandDefault" - || t.cs() == "DeclareMathAccent") { + if (t.cs() == "newcommand" || t.cs() == "newcommandx" + || t.cs() == "renewcommand" || t.cs() == "renewcommandx" + || t.cs() == "providecommand" || t.cs() == "providecommandx" + || t.cs() == "DeclareRobustCommand" + || t.cs() == "DeclareRobustCommandx" + || t.cs() == "ProvideTextCommandDefault" + || t.cs() == "DeclareMathAccent") { bool star = false; if (p.next_token().character() == '*') { p.get_token(); @@ -1670,9 +1790,10 @@ void Preamble::parse(Parser & p, string const & forceclass, } // restore the in_lyx_preamble setting in_lyx_preamble = was_in_lyx_preamble; + continue; } - else if (t.cs() == "documentclass") { + if (t.cs() == "documentclass") { vector::iterator it; vector opts = split_options(p.getArg('[', ']')); handle_opt(opts, known_fontsizes, h_paperfontsize); @@ -1743,9 +1864,10 @@ void Preamble::parse(Parser & p, string const & forceclass, // different name in LyX than in LaTeX h_textclass = p.getArg('{', '}'); p.skip_spaces(); + continue; } - else if (t.cs() == "usepackage") { + if (t.cs() == "usepackage") { string const options = p.getArg('[', ']'); string const name = p.getArg('{', '}'); vector vecnames; @@ -1755,9 +1877,10 @@ void Preamble::parse(Parser & p, string const & forceclass, for (; it != end; ++it) handle_package(p, trimSpaceAndEol(*it), options, in_lyx_preamble, detectEncoding); + continue; } - else if (t.cs() == "inputencoding") { + if (t.cs() == "inputencoding") { string const encoding = p.getArg('{','}'); Encoding const * const enc = encodings.fromLaTeXName( encoding, Encoding::inputenc, true); @@ -1770,9 +1893,10 @@ void Preamble::parse(Parser & p, string const & forceclass, h_inputencoding = enc->name(); p.setEncoding(enc->iconvName()); } + continue; } - else if (t.cs() == "newenvironment") { + if (t.cs() == "newenvironment") { string const name = p.getArg('{', '}'); string const opt1 = p.getFullOpt(); string const opt2 = p.getFullOpt(); @@ -1785,10 +1909,10 @@ void Preamble::parse(Parser & p, string const & forceclass, } add_known_environment(name, opt1, !opt2.empty(), from_utf8(beg), from_utf8(end)); - + continue; } - else if (t.cs() == "newtheorem") { + if (t.cs() == "newtheorem") { bool star = false; if (p.next_token().character() == '*') { p.get_token(); @@ -1808,9 +1932,10 @@ void Preamble::parse(Parser & p, string const & forceclass, if (!in_lyx_preamble) h_preamble << complete; + continue; } - else if (t.cs() == "def") { + if (t.cs() == "def") { string name = p.get_token().cs(); // In fact, name may be more than the name: // In the test case of bug 8116 @@ -1821,9 +1946,10 @@ void Preamble::parse(Parser & p, string const & forceclass, if (!in_lyx_preamble) h_preamble << "\\def\\" << name << '{' << p.verbatim_item() << "}"; + continue; } - else if (t.cs() == "newcolumntype") { + if (t.cs() == "newcolumntype") { string const name = p.getArg('{', '}'); trimSpaceAndEol(name); int nargs = 0; @@ -1837,9 +1963,10 @@ void Preamble::parse(Parser & p, string const & forceclass, if (nargs) h_preamble << "[" << nargs << "]"; h_preamble << "{" << p.verbatim_item() << "}"; + continue; } - else if (t.cs() == "setcounter") { + if (t.cs() == "setcounter") { string const name = p.getArg('{', '}'); string const content = p.getArg('{', '}'); if (name == "secnumdepth") @@ -1848,9 +1975,10 @@ void Preamble::parse(Parser & p, string const & forceclass, h_tocdepth = content; else h_preamble << "\\setcounter{" << name << "}{" << content << "}"; + continue; } - else if (t.cs() == "setlength") { + if (t.cs() == "setlength") { string const name = p.verbatim_item(); string const content = p.verbatim_item(); // the paragraphs are only not indented when \parindent is set to zero @@ -1872,18 +2000,25 @@ void Preamble::parse(Parser & p, string const & forceclass, h_mathindentation = translate_len(content); } else h_preamble << "\\setlength{" << name << "}{" << content << "}"; + continue; } - else if (t.cs() == "onehalfspacing") + if (t.cs() == "onehalfspacing") { h_spacing = "onehalf"; + continue; + } - else if (t.cs() == "doublespacing") + if (t.cs() == "doublespacing") { h_spacing = "double"; + continue; + } - else if (t.cs() == "setstretch") + if (t.cs() == "setstretch") { h_spacing = "other " + p.verbatim_item(); + continue; + } - else if (t.cs() == "synctex") { + if (t.cs() == "synctex") { // the scheme is \synctex=value // where value can only be "1" or "-1" h_output_sync = "1"; @@ -1894,21 +2029,24 @@ void Preamble::parse(Parser & p, string const & forceclass, if (value == "-") value += p.get_token().asInput(); h_output_sync_macro = "\\synctex=" + value; + continue; } - else if (t.cs() == "begin") { + if (t.cs() == "begin") { string const name = p.getArg('{', '}'); if (name == "document") break; h_preamble << "\\begin{" << name << "}"; + continue; } - else if (t.cs() == "geometry") { + if (t.cs() == "geometry") { vector opts = split_options(p.getArg('{', '}')); handle_geometry(opts); + continue; } - else if (t.cs() == "definecolor") { + if (t.cs() == "definecolor") { string const color = p.getArg('{', '}'); string const space = p.getArg('{', '}'); string const value = p.getArg('{', '}'); @@ -1929,12 +2067,15 @@ void Preamble::parse(Parser & p, string const & forceclass, << "}{" << space << "}{" << value << '}'; } + continue; } - else if (t.cs() == "bibliographystyle") + if (t.cs() == "bibliographystyle") { h_biblio_style = p.verbatim_item(); + continue; + } - else if (t.cs() == "jurabibsetup") { + if (t.cs() == "jurabibsetup") { // FIXME p.getArg('{', '}') is most probably wrong (it // does not handle nested braces). // Use p.verbatim_item() instead. @@ -1946,9 +2087,10 @@ void Preamble::parse(Parser & p, string const & forceclass, h_preamble << "\\jurabibsetup{" << join(jurabibsetup, ",") << '}'; } + continue; } - else if (t.cs() == "hypersetup") { + if (t.cs() == "hypersetup") { vector hypersetup = split_options(p.verbatim_item()); // add hypersetup to the hyperref package options @@ -1957,9 +2099,38 @@ void Preamble::parse(Parser & p, string const & forceclass, h_preamble << "\\hypersetup{" << join(hypersetup, ",") << '}'; } + continue; } - else if (is_known(t.cs(), known_if_3arg_commands)) { + if (t.cs() == "includeonly") { + vector includeonlys = getVectorFromString(p.getArg('{', '}')); + for (auto & iofile : includeonlys) { + string filename(normalize_filename(iofile)); + string const path = getMasterFilePath(true); + // We want to preserve relative/absolute filenames, + // therefore path is only used for testing + if (!makeAbsPath(filename, path).exists()) { + // The file extension is probably missing. + // Now try to find it out. + string const tex_name = + find_file(filename, path, + known_tex_extensions); + if (!tex_name.empty()) + filename = tex_name; + } + string outname; + if (makeAbsPath(filename, path).exists()) + fix_child_filename(filename); + else + cerr << "Warning: Could not find included file '" + << filename << "'." << endl; + outname = changeExtension(filename, "lyx"); + h_includeonlys.push_back(outname); + } + continue; + } + + if (is_known(t.cs(), known_if_3arg_commands)) { // prevent misparsing of \usepackage if it is used // as an argument (see e.g. our own output of // \@ifundefined above) @@ -1996,18 +2167,22 @@ void Preamble::parse(Parser & p, string const & forceclass, << '{' << arg2 << '}' << '{' << arg3 << '}'; } + continue; } - else if (is_known(t.cs(), known_if_commands)) { + if (is_known(t.cs(), known_if_commands)) { // must not parse anything in conditional code, since // LyX would output the parsed contents unconditionally if (!in_lyx_preamble) h_preamble << t.asInput(); handle_if(p, in_lyx_preamble); + continue; } - else if (!t.cs().empty() && !in_lyx_preamble) + if (!t.cs().empty() && !in_lyx_preamble) { h_preamble << '\\' << t.cs(); + continue; + } } // remove the whitespace @@ -2053,6 +2228,47 @@ void Preamble::parse(Parser & p, string const & forceclass, h_options += ',' + lyx2babel(default_language); } } + + // Finally, set the quote style. + // LyX knows the following quotes styles: + // british, cjk, cjkangle, danish, english, french, german, + // polish, russian, swedish and swiss + // conversion list taken from + // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage + // (quotes for kazakh are unknown) + // british + if (is_known(h_language, known_british_quotes_languages)) + h_quotes_style = "british"; + // cjk + else if (is_known(h_language, known_cjk_quotes_languages)) + h_quotes_style = "cjk"; + // cjkangle + else if (is_known(h_language, known_cjkangle_quotes_languages)) + h_quotes_style = "cjkangle"; + // danish + else if (is_known(h_language, known_danish_quotes_languages)) + h_quotes_style = "danish"; + // french + else if (is_known(h_language, known_french_quotes_languages)) + h_quotes_style = "french"; + // german + else if (is_known(h_language, known_german_quotes_languages)) + h_quotes_style = "german"; + // polish + else if (is_known(h_language, known_polish_quotes_languages)) + h_quotes_style = "polish"; + // russian + else if (is_known(h_language, known_russian_quotes_languages)) + h_quotes_style = "russian"; + // swedish + else if (is_known(h_language, known_swedish_quotes_languages)) + h_quotes_style = "swedish"; + // swiss + else if (is_known(h_language, known_swiss_quotes_languages)) + h_quotes_style = "swiss"; + // english + else if (is_known(h_language, known_english_quotes_languages)) + h_quotes_style = "english"; } diff --git a/src/tex2lyx/Preamble.h b/src/tex2lyx/Preamble.h index 5dcc6718d7..0d3ff0113f 100644 --- a/src/tex2lyx/Preamble.h +++ b/src/tex2lyx/Preamble.h @@ -54,6 +54,8 @@ public: std::string docLanguage() const { return h_language; } /// The language of text which is not explicitly marked std::string defaultLanguage() const { return default_language; } + /// The quotation marks style + std::string quotesStyle() const { return h_quotes_style; } /// bool usePolyglossia() const; /// @@ -104,6 +106,8 @@ public: static const char * const polyglossia_languages[]; /// the same as polyglossia_languages with .lyx names static const char * const coded_polyglossia_languages[]; + /// + std::vector biblatex_bibliographies; private: /// @@ -131,7 +135,11 @@ private: std::ostringstream h_preamble; std::string h_backgroundcolor; + std::string h_multibib; std::string h_biblio_style; + std::string h_biblio_options; + std::string h_biblatex_bibstyle; + std::string h_biblatex_citestyle; std::string h_bibtex_command; std::string h_boxbgcolor; std::string h_cite_engine; @@ -217,6 +225,7 @@ private: std::map h_use_packages; std::string h_use_default_options; std::string h_use_hyperref; + std::vector h_includeonlys; bool h_use_refstyle; bool h_use_minted; @@ -241,6 +250,8 @@ private: AuthorList authors_; /// special table column types std::map special_columns_; + /// + std::map extra_package_options_; }; diff --git a/src/tex2lyx/TODO.txt b/src/tex2lyx/TODO.txt index c448a45569..be5d770021 100644 --- a/src/tex2lyx/TODO.txt +++ b/src/tex2lyx/TODO.txt @@ -24,7 +24,6 @@ Format LaTeX feature LyX feature is just a string or if the file should be included and as what type) 254 esint.sty \use_esint 267 XeTeX utf8 encoding -270 \alert, \structure (beamer) layout 281 ? modules 293 ? InsetInfo 322 ? local layout @@ -33,124 +32,32 @@ Format LaTeX feature LyX feature 332 ? InsetGraphics groupId 343 ? \use_default_options 358 custom bibtex command \bibtex_command + Maybe via the + % !BIB program = + comment understood by some TeX editors 358 custom makeindex command \index_command 363 horizontal longtable alignment InsetTabular 364 branch file name suffix \filename_suffix 371 automatic mhchem loading \use_mhchem -375 \includeonly \{begin,end}_includeonly -376 update .aux of unincluded children \maintain_unincluded_children 377 multirow.sty InsetTabular 378 revision info InsetInfo 380 ? InsetPreview 386 LyX version InsetInfo 390 forward/reverse search \forward_search, \forward_macro 391 decimal alignment in tables InsetTabular -392 new beamer format InsetLayout 399 automatic mathdots loading \use_mathdots 407 vertical offset for multirows InsetTabular 411 support for polyglossia \language_package (the cases of no package, of babel and of custom package is supported) 415 automatic undertilde loading \use_package undertilde -438 \t*{ } InsetTIPA -439 MinionPro.sty \font_roman, \font_osf -440 MinionPro.sty, \font_math 443 unicode-math.sty InsetMath* -445 URW Classico LaTeX font \font_sans uop - \renewcommand{\sffamily}{uop} -446 Optional and required arguments InsetArgument - now numbered by order 448 -451 beamer overlay arguments InsetArgument - \command, \begin{env} -452 beamer block arguments InsetArgument - \begin{block}{title} 453 automatic stmaryrd loading \use_package stmaryrd -454 beamer overprint environment InsetArgument, layout Overprint - \begin{overprint}[maxlength] - \onslide text ... - \end{overprint} -455 beamer frametitle command \begin_layout FrameTitle - \frametitle[short}{long} -456 memoir: \epigraph{text}{source} layout Epigraph, InsetArgument 457 automatic stackrel loading \use_package stackrel -459 beamer: \begin{frame}, \begin_layout Frame - \begin{frame}[plain], \begin_layout PlainFrame - \begin{frame}[fragile] \begin_layout FragileFrame -466 Powerdot updates: - \pause[] layout Pause - \onslide{}{} InsetFlex, InsetArgument - \onslide*{}{} InsetFlex, InsetArgument - \onslide+{}{} InsetFlex, InsetArgument - \twocolumn[]{}{} Layout Twocolumn, InsetArgument - \item[]<> InsetArgument - \begin{enumerate|itemize|...}[] InsetArgument -520 Plain InsetQuote Style: - \textquotesingle \begin_inset Quotes qls, \begin_inset Quotes qrs - \textquotedbl \begin_inset Quotes qld, \begin_inset Quotes qrd -521 New Quote Styles InsetQuote - - british \begin_inset Quotes b.. - - swiss \begin_inset Quotes c.. - - swedishg \begin_inset Quotes w.. - - frenchin \begin_inset Quotes i.. - - russian \begin_inset Quotes r.. - Change default behavior \begin_inset Quotes f.. - of French quote style: - - Inner quotes are now ``...''. - - Former french style is now - called "swiss" -523 CJK Quote Styles InsetQuote - - cjk (corner brackets) \begin_inset Quotes j.. - - cjkangle (angle brackets) \begin_inset Quotes k.. -526 - Plural and capitalized refstyles InsetRef -528 Biblatex - \usepackage{biblatex} \cite_engine biblatex - ...[...natbib=true...]... \cite_engine biblatex-natbib - ...[...style=...]... \biblatex_bibstyle - \biblatex_citestyle - ...[...bibstyle=...]... \biblatex_bibstyle - ...[...citestyle=...]... \biblatex_citestyle - ...[......]... \biblio_options - \printbibliography[] \begin_inset CommandInset bibtex - biblatexopts "" - \addbibresource{file.bib} \begin_inset CommandInset bibtex - [multiple possible!] bibfiles "...,file,..." [NB: strip ext!] - \bibliography{file1,file2,...} \begin_inset CommandInset bibtex - bibfiles "...,file1,file2,..." - \begin_inset CommandInset citation - \Cite LatexCmd Cite - \cite* LatexCmd citeyear - \citeyear LatexCmd citebyear - \{T,t}extcite LatexCmd {C,c}itet - \{P,p}arencite LatexCmd {C,c}itep - \parencite* LatexCmd citeyearpar - \{S,s}martcite LatexCmd {F,f}ootcite - \{F,f}ootcite LatexCmd {F,f}ootcite - \{A,a}utocite LatexCmd {A,a}utocite - \citecite[*] LatexCmd citecite[*] - \fullcite LatexCmd fullcite - \footfullcite LatexCmd footfullcite - \supercite LatexCmd supercite -531 Biblatex "qualified citation lists" - \cites(pre)(post)[pre1][post1]{key1}[pre2][post2]{key2}... - \begin_inset CommandInset citation - LatexCmd cite - after "post" - before "pre" - key "key1,key2..." - pretextlist "key1 pre1\tab key2 pre2..." - posttextlist "key1 post1\tab key2 post2..." - Same for: - \Cites, \textcites, \Textcites, \parencites, \Parencites, \smartcites, \Smartcites, \autocites, Autocites +526 Plural and capitalized refstyles InsetRef 533 Multibib support - \begin{btUnit}...\end{btUnit} \multibib {none|part|chapter|section|subsetion} + \begin{btUnit}...\end{btUnit} \multibib {none|part|chapter|section|subsection} (if a part, chapter, section etc. follows the \begin...) - \usepackage[refsection= \multibib - \bibbysection[] \begin_inset CommandInset bibtex - biblatexopts "" - btprint "bibbysection" -534 Chapterbib support - \usepackage{chapterbib} \multibib child diff --git a/src/tex2lyx/test/CJK.lyx.lyx b/src/tex2lyx/test/CJK.lyx.lyx index 59d3729c3a..2ec5f9c0a5 100644 --- a/src/tex2lyx/test/CJK.lyx.lyx +++ b/src/tex2lyx/test/CJK.lyx.lyx @@ -74,7 +74,7 @@ \paragraph_indentation default \is_math_indent 0 \math_numbering_side default -\quotes_style english +\quotes_style cjk \dynamic_quotes 0 \papercolumns 1 \papersides 1 @@ -143,7 +143,27 @@ status collapsed \end_inset -texttt{hei} mapping, +texttt +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout +{ +\end_layout + +\end_inset + +hei +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout +} +\end_layout + +\end_inset + + mapping, and the first mapping is empty. \begin_inset ERT status collapsed diff --git a/src/tex2lyx/test/CJKutf8.lyx.lyx b/src/tex2lyx/test/CJKutf8.lyx.lyx index 7eeec41b14..a3a6856e47 100644 --- a/src/tex2lyx/test/CJKutf8.lyx.lyx +++ b/src/tex2lyx/test/CJKutf8.lyx.lyx @@ -74,7 +74,7 @@ \paragraph_indentation default \is_math_indent 0 \math_numbering_side default -\quotes_style english +\quotes_style cjk \dynamic_quotes 0 \papercolumns 1 \papersides 1 diff --git a/src/tex2lyx/test/CMakeLists.txt b/src/tex2lyx/test/CMakeLists.txt index 643b017bb2..a6f6789f85 100644 --- a/src/tex2lyx/test/CMakeLists.txt +++ b/src/tex2lyx/test/CMakeLists.txt @@ -12,6 +12,7 @@ project(testTex2lyx) set(_tex_tests test.ltx algo2e.tex + beamer.tex box-color-size-space-align.tex CJK.tex CJKutf8.tex diff --git a/src/tex2lyx/test/XeTeX-polyglossia.lyx.lyx b/src/tex2lyx/test/XeTeX-polyglossia.lyx.lyx index fac9a9573a..6938846714 100644 --- a/src/tex2lyx/test/XeTeX-polyglossia.lyx.lyx +++ b/src/tex2lyx/test/XeTeX-polyglossia.lyx.lyx @@ -73,7 +73,7 @@ \paragraph_indentation default \is_math_indent 0 \math_numbering_side default -\quotes_style english +\quotes_style british \dynamic_quotes 0 \papercolumns 1 \papersides 1 diff --git a/src/tex2lyx/test/beamer.lyx.lyx b/src/tex2lyx/test/beamer.lyx.lyx new file mode 100644 index 0000000000..f3ba80fc5c --- /dev/null +++ b/src/tex2lyx/test/beamer.lyx.lyx @@ -0,0 +1,4411 @@ +#LyX file created by tex2lyx 2.3 +\lyxformat 544 +\begin_document +\begin_header +\save_transient_properties true +\origin roundtrip +\textclass beamer +\begin_preamble + + +\usepackage{babel} + + +\usetheme[left,width=3.45em]{Berkeley} + + +\end_preamble +\use_default_options false +\maintain_unincluded_children false +\language english +\language_package default +\inputencoding iso8859-15 +\fontencoding T1 +\font_roman "default" "default" +\font_sans "default" "default" +\font_typewriter "default" "default" +\font_math "auto" "auto" +\font_default_family default +\use_non_tex_fonts false +\font_sc false +\font_osf false +\font_sf_scale 100 100 +\font_tt_scale 100 100 +\use_microtype false +\use_dash_ligatures true +\graphics default +\default_output_format default +\output_sync 0 +\bibtex_command default +\index_command default +\paperfontsize default +\spacing single +\use_hyperref false +\papersize default +\use_geometry false +\use_package amsmath 1 +\use_package amssymb 0 +\use_package cancel 0 +\use_package esint 1 +\use_package mathdots 0 +\use_package mathtools 0 +\use_package mhchem 0 +\use_package stackrel 0 +\use_package stmaryrd 0 +\use_package undertilde 0 +\cite_engine basic +\cite_engine_type default +\biblio_style plain +\use_bibtopic false +\use_indices false +\paperorientation portrait +\suppress_date false +\justification true +\use_refstyle 0 +\use_minted 0 +\index Index +\shortcut idx +\color #008000 +\end_index +\secnumdepth 3 +\tocdepth 3 +\paragraph_separation indent +\paragraph_indentation default +\is_math_indent 0 +\math_numbering_side default +\quotes_style english +\dynamic_quotes 0 +\papercolumns 1 +\papersides 1 +\paperpagestyle default +\tracking_changes false +\output_changes false +\html_math_output 0 +\html_css_as_file 0 +\html_be_strict false +\end_header + +\begin_body + +\begin_layout Title +Presentations with Beamer +\end_layout + +\begin_layout Subtitle +An Introduction to the Basics +\end_layout + +\begin_layout Author +John Doe +\end_layout + +\begin_layout Date +Version +\begin_inset space ~ + +\end_inset + +2.3 +\end_layout + +\begin_layout Frame + +\end_layout + +\begin_deeper +\begin_layout FrameTitle + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +presentation +\end_layout + +\end_inset + +Contents +\end_layout + +\end_deeper +\begin_layout Frame + +\begin_inset CommandInset toc +LatexCommand tableofcontents + +\end_inset + + +\end_layout + +\begin_layout Section +Purposes +\end_layout + +\begin_layout Frame + +\begin_inset Argument 2 +status collapsed + + +\begin_layout Standard ++- +\end_layout + +\end_inset + + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Purpose of the Beamer class +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame +With the +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Beamer +\end_layout + +\end_inset + + class, you can produce presentation slides, which +\end_layout + +\begin_deeper +\begin_layout Itemize +are visually highly customizable +\end_layout + +\begin_layout Itemize +can be very well structured +\end_layout + +\begin_layout Itemize +can be constructed step-by-step ( +\begin_inset Quotes eld +\end_inset + +overlay +\begin_inset Quotes erd +\end_inset + + concept) +\end_layout + +\begin_layout Itemize +may contain different navigation paths (note that the slides contain all sorts of hyperlinks) +\end_layout + +\begin_layout Itemize +use \SpecialChar LaTeX +'s superb output quality +\end_layout + +\begin_layout Itemize +might embed multimedia content (audio, video) +\end_layout + +\begin_layout Itemize +can easily be transformed to accompanying material (such as an article-like handout) +\end_layout + +\begin_layout Itemize +and much more \SpecialChar ldots + +\end_layout + +\end_deeper +\begin_layout Standard + +\begin_inset Separator plain + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Purpose of this presentation +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame +This presentation +\end_layout + +\begin_deeper +\begin_layout Itemize + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard ++- +\end_layout + +\end_inset + +describes some basic features of +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Beamer +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Itemize +especially how they can be used +\end_layout + +\end_deeper +\begin_layout Frame +For more general and comprehensive information on +\begin_inset Flex Flex:Structure +status collapsed + +\begin_layout Plain Layout + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +presentation +\end_layout + +\end_inset + +Beamer +\end_layout + +\end_inset + + itself, please refer to the extensive class manual +\begin_inset CommandInset citation +LatexCommand cite +after "" +key "beamer-ug" +literal "false" + +\end_inset + + +\end_layout + +\begin_layout Section* +Segments of a presentation +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +The global structure +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame +A presentation usually consists of +\end_layout + +\begin_deeper +\begin_layout Itemize + +\begin_inset Argument item:2 +status collapsed + + +\begin_layout Standard ++- +\end_layout + +\end_inset + +a title page +\end_layout + +\begin_layout Itemize + +\begin_inset Argument item:2 +status collapsed + + +\begin_layout Standard ++- +\end_layout + +\end_inset + +slides that might be grouped to sections/parts +\end_layout + +\begin_layout Itemize + +\begin_inset Argument item:1 +status collapsed + + +\begin_layout Standard +extra +\end_layout + +\end_inset + + +\begin_inset Argument item:2 +status collapsed + + +\begin_layout Standard ++- +\end_layout + +\end_inset + +an appendix with additional information, such as a bibliography +\end_layout + +\end_deeper +\begin_layout Frame +We describe these global segments in what follows. +\end_layout + +\begin_layout Standard + +\begin_inset Separator plain + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +The title page +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame +A title page is constructed by the layouts +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Title +\end_layout + +\end_inset + +, +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Subtitle +\end_layout + +\end_inset + +, +\begin_inset Flex Flex:Structure +status collapsed + +\begin_layout Plain Layout + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +2 +\end_layout + +\end_inset + +Author +\end_layout + +\end_inset + +, +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Institute +\end_layout + +\end_inset + +, +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Date +\end_layout + +\end_inset + + and +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +TitleGraphic +\end_layout + +\end_inset + +. +\end_layout + +\begin_deeper +\begin_layout Itemize +None of these elements is mandatory, but at least one must be given +\end_layout + +\begin_layout Itemize +The order of insertion does not matter (the real order is defined in the +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Beamer +\end_layout + +\end_inset + + theme) +\end_layout + +\begin_layout Itemize +For +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Title +\end_layout + +\end_inset + +, +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Subtitle +\end_layout + +\end_inset + +, +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Author +\end_layout + +\end_inset + +, +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Institute +\end_layout + +\end_inset + + and +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Date +\end_layout + +\end_inset + +, you can define +\begin_inset Quotes eld +\end_inset + +short +\begin_inset Quotes erd +\end_inset + + forms via +\begin_inset Flex Alert +status collapsed + +\begin_layout Plain Layout +Insert →Short Title\SpecialChar breakableslash +Date\SpecialChar breakableslash +\SpecialChar ldots + +\end_layout + +\end_inset + + These are used in the sidebar\SpecialChar breakableslash +heading (given the theme actually provides a sidebar\SpecialChar breakableslash +heading) +\end_layout + +\begin_layout Itemize +If you select +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Title (Plain Frame) +\end_layout + +\end_inset + + instead of +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Title +\end_layout + +\end_inset + +, the title page will have no sidebar or heading +\end_layout + +\end_deeper +\begin_layout Standard + +\begin_inset Separator plain + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +1-2 +\end_layout + +\end_inset + + +\begin_inset Argument 3 +status collapsed + + +\begin_layout Standard +label=myframe +\end_layout + +\end_inset + + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Frames can be repeated +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame +Frames can be repeated fully or only in terms of selected sub-slides, multiple times at any later point of the presentation. +\end_layout + +\begin_layout Frame +You just need to give the respective frame a label name via the frame option +\begin_inset Quotes eld +\end_inset + +label +\begin_inset Quotes erd +\end_inset + + (as done here). +\end_layout + +\begin_deeper +\begin_layout Pause + +\end_layout + +\end_deeper +\begin_layout Frame +Then you can repeat this frame by means of the +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +AgainFrame +\end_layout + +\end_inset + + layout later in the presentation. Just enter the label name in the +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +AgainFrame +\end_layout + +\end_inset + + layout and specify, if required, which sub-slides you want to be repeated via +\begin_inset Flex Alert +status collapsed + +\begin_layout Plain Layout +Insert →Overlay Specifications +\end_layout + +\end_inset + + (again, see below for the concept of +\begin_inset Quotes eld +\end_inset + +overlays +\begin_inset Quotes erd +\end_inset + +). +\end_layout + +\begin_deeper +\begin_layout Proof + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +3 +\end_layout + +\end_inset + + +\begin_inset Flex Alert +status collapsed + +\begin_layout Plain Layout +Here's the proof! +\end_layout + +\end_inset + + (This text is only shown on sub-slide 3 which is itself only shown when this frame is repeated later on) +\end_layout + +\end_deeper +\begin_layout Standard + +\begin_inset Separator plain + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Keeping frames together +\end_layout + +\end_inset + + +\end_layout + +\begin_deeper +\begin_layout FrameSubtitle +Use nesting! +\end_layout + +\end_deeper +\begin_deeper +\begin_layout Itemize +Note that all frame content, if the style is not +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Frame +\end_layout + +\end_inset + +, must be nested to the frame environment (via +\begin_inset Flex Alert +status collapsed + +\begin_layout Plain Layout +Edit →Increase List Depth +\end_layout + +\end_inset + + or +\family sans +Alt+Shift+Right +\family default +). This is done automatically if you insert new frame paragraphs. +\end_layout + +\begin_layout Itemize +Nested content is marked by a red bar in the margin of the LyX workarea +\end_layout + +\end_deeper +\begin_layout Itemize +Non-nested content (such as this) will also be displayed in the presentation (on a separate slide), but not properly aligned +\end_layout + +\begin_layout Itemize +So please avoid this +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Separating frames +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame +\noindent +Consecutive frames have to be separated from each other. This is done by means of the +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +\noindent +Separator +\end_layout + +\end_inset + + inset, which can be produced by hitting return in an empty Standard paragraph right below the frame (see UserGuide, sec. +\begin_inset space ~ + +\end_inset + +3.4.6). +\end_layout + +\begin_deeper +\begin_layout Block + +\begin_inset Argument 2 +status collapsed + + +\begin_layout Standard +Tip +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Block +There is a simple and much more convenient way to start a new frame: Issue +\begin_inset Flex Alert +status collapsed + +\begin_layout Plain Layout +Insert →Separated Frame Below +\end_layout + +\end_inset + + ( +\family sans +undefiniert +\family default + if you are in a non-nested +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Frame +\end_layout + +\end_inset + + paragraph, or +\family sans +Alt+A Shift+Return +\family default +, respectively, if you are in a nested paragraph within the frame). If you are in the frame heading, +\begin_inset Flex Alert +status collapsed + +\begin_layout Plain Layout +Insert →Separated Frame Above +\end_layout + +\end_inset + + inserts a new, properly separated frame above the current one! +\end_layout + +\end_deeper +\begin_layout AgainFrame + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +3 +\end_layout + +\end_inset + +myframe +\end_layout + +\begin_layout Frame + +\begin_inset Argument 3 +status collapsed + + +\begin_layout Standard +plain +\end_layout + +\end_inset + + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Special frame types +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame +LyX provides two special frame types: +\end_layout + +\begin_deeper +\begin_layout Enumerate + +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Frame (plain) +\end_layout + +\end_inset + + is a frame without a sidebar/header (such as this one). This is useful for slides with much content\SpecialChar breakableslash +wide tables +\end_layout + +\begin_layout Enumerate + +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Frame (fragile) +\end_layout + +\end_inset + + is to be used if the frame consists of +\begin_inset Quotes eld +\end_inset + +fragile +\begin_inset Quotes erd +\end_inset + + content, especially verbatim stuff such as program listings +\end_layout + +\end_deeper +\begin_layout Frame +If you want a fragile plain frame, pass the option +\begin_inset Quotes eld +\end_inset + +plain +\begin_inset Quotes erd +\end_inset + + to a fragile frame or the option +\begin_inset Quotes eld +\end_inset + +fragile +\begin_inset Quotes erd +\end_inset + + to a plain frame. +\end_layout + +\begin_layout Standard + +\begin_inset Separator plain + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Re-arranging frames +\end_layout + +\end_inset + + +\end_layout + +\begin_deeper +\begin_layout Block + +\begin_inset Argument 2 +status collapsed + + +\begin_layout Standard +Tip +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Block +Did you know that you can easily move and re-arrange whole frames via the outliner ( +\begin_inset Flex Alert +status collapsed + +\begin_layout Plain Layout +View →Outline Pane +\end_layout + +\end_inset + +)? +\end_layout + +\begin_layout Block +Also, you can navigate to a specific frame via the +\begin_inset Flex Alert +status collapsed + +\begin_layout Plain Layout +Navigate +\end_layout + +\end_inset + + menu! +\end_layout + +\end_deeper +\begin_layout Section* + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +article +\end_layout + +\end_inset + +The overlay concept +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +What are overlays? +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame +Basically, the overlay concept allows to change the slide content dynamically. You can uncover things/text piecewise, fade out content, highlight things, replace text, images etc. +\end_layout + +\begin_deeper +\begin_layout Pause + +\end_layout + +\end_deeper +\begin_deeper +\begin_layout Itemize + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard ++- +\end_layout + +\end_inset + +Overlays are useful to build up slides as you speak +\end_layout + +\begin_layout Itemize +They help you to shift your audience's focus on specific things +\end_layout + +\begin_layout Itemize +And they help your audience to follow you +\end_layout + +\begin_layout Itemize +So use overlays! +\begin_inset Flex Flex:Alert +status collapsed + +\begin_layout Plain Layout + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +6 +\end_layout + +\end_inset + +Really, use them! +\end_layout + +\end_inset + + +\end_layout + +\end_deeper +\begin_layout Standard + +\begin_inset Separator plain + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Overlay types +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Beamer +\end_layout + +\end_inset + + provides many different overlay types. The most important ones are: +\end_layout + +\begin_deeper +\begin_layout Description +Hidden +\begin_inset space ~ + +\end_inset + +content: Stuff that is completely invisible up to a point +\end_layout + +\begin_layout Description +Covered +\begin_inset space ~ + +\end_inset + +content: Stuff that is faded out (not completely invisible) +\end_layout + +\begin_layout Description +Highlighted +\begin_inset space ~ + +\end_inset + +content: Stuff that is somehow emphasized at a certain point +\end_layout + +\end_deeper +\begin_layout Frame +We give examples for these types in what follows, but begin with some general remarks on overlay possibilities +\end_layout + +\begin_layout Standard + +\begin_inset Separator plain + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +General overlay/action possibilities +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame +Many +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Beamer +\end_layout + +\end_inset + + elements provide overlay settings. Basically, you can define on which sub-slide(s) a given content appears ( +\begin_inset Quotes eld +\end_inset + +2 +\begin_inset Quotes erd +\end_inset + +, +\begin_inset Quotes eld +\end_inset + +2-4 +\begin_inset Quotes erd +\end_inset + +, +\begin_inset Quotes eld +\end_inset + +3- +\begin_inset Quotes erd +\end_inset + +, +\begin_inset Quotes eld +\end_inset + +1,3 +\begin_inset Quotes erd +\end_inset + + etc.), or in which output mode ( +\begin_inset Quotes eld +\end_inset + +presentation +\begin_inset Quotes erd +\end_inset + +, +\begin_inset Quotes eld +\end_inset + +article +\begin_inset Quotes erd +\end_inset + + etc.) +\end_layout + +\begin_deeper +\begin_layout Itemize +In LyX, these settings are generally accessible via +\begin_inset Flex Alert +status collapsed + +\begin_layout Plain Layout +Insert →Overlay Specifications +\end_layout + +\end_inset + + or +\begin_inset Flex Alert +status collapsed + +\begin_layout Plain Layout +Insert →Action Specifications +\end_layout + +\end_inset + + +\end_layout + +\end_deeper +\begin_deeper +\begin_layout Overprint + +\begin_inset Argument item:1 +status collapsed + + +\begin_layout Standard +2 +\end_layout + +\end_inset + + +\end_layout + +\begin_deeper +\begin_layout Definition + +\begin_inset Quotes eld +\end_inset + +Action +\begin_inset Quotes erd +\end_inset + + is a more general concept, which does not only include what we have called +\begin_inset Quotes eld +\end_inset + +overlays +\begin_inset Quotes erd +\end_inset + + ( +\begin_inset Quotes eld +\end_inset + +on which sub-slide[s] is this to be shown\SpecialChar breakableslash +hidden\SpecialChar breakableslash +highlighted +\begin_inset Quotes erd +\end_inset + +), but also tasks such as +\begin_inset Quotes eld +\end_inset + +only show this in the presentation, not on the handout +\begin_inset Quotes erd +\end_inset + + or +\begin_inset Quotes eld +\end_inset + +show this on the second screen only +\begin_inset Quotes erd +\end_inset + + (so-called +\begin_inset Quotes eld +\end_inset + +modes +\begin_inset Quotes erd +\end_inset + +). +\end_layout + +\end_deeper +\begin_layout Overprint + +\begin_inset Argument item:1 +status collapsed + + +\begin_layout Standard +3 +\end_layout + +\end_inset + + +\end_layout + +\begin_deeper +\begin_layout AlertBlock + +\begin_inset Argument 2 +status collapsed + + +\begin_layout Standard +Note to the \SpecialChar LaTeX + aficionados +\end_layout + +\end_inset + + +\end_layout + +\begin_layout AlertBlock +The mentioned overlay/action settings conform to those command/environment options embraced by +\begin_inset Flex Alert +status collapsed + +\begin_layout Plain Layout + +\begin_inset space \space{} + +\end_inset + +<\SpecialChar ldots +> +\end_layout + +\end_inset + + and +\begin_inset Flex Alert +status collapsed + +\begin_layout Plain Layout +[<\SpecialChar ldots +>] +\end_layout + +\end_inset + + in the \SpecialChar LaTeX + output. +\end_layout + +\begin_layout AlertBlock +Note that LyX adds those braces on export, so you must not enter them yourself. In other words, enter +\begin_inset Quotes eld +\end_inset + +1 +\begin_inset Quotes erd +\end_inset + + or +\begin_inset Quotes eld +\end_inset + ++- +\begin_inset Quotes erd +\end_inset + + to the overlay/action insets, not +\begin_inset Quotes eld +\end_inset + +<1> +\begin_inset Quotes erd +\end_inset + + or +\begin_inset Quotes eld +\end_inset + +[<+->] +\begin_inset Quotes erd +\end_inset + +! +\end_layout + +\end_deeper +\end_deeper +\begin_layout Standard + +\begin_inset Separator plain + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +An example +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame +Take for example a quote. In a +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Quote +\end_layout + +\end_inset + + environment, you can specify the overlay settings via +\begin_inset Flex Alert +status collapsed + +\begin_layout Plain Layout +Insert →Overlay Specifications +\end_layout + +\end_inset + +. If you do this and enter +\begin_inset Quotes eld +\end_inset + +2 +\begin_inset Quotes erd +\end_inset + +, the quote will only appear on (sub-)slide 2: +\end_layout + +\begin_deeper +\begin_layout Quote + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +2 +\end_layout + +\end_inset + +Fear no more the heat o +\begin_inset Quotes ers +\end_inset + + the sun +\end_layout + +\begin_layout Quote +Nor the furious winter +\begin_inset Quotes ers +\end_inset + +s rages +\end_layout + +\begin_layout Quote +Thou thy worldly task hast done +\end_layout + +\begin_layout Quote +Home art gone, and ta +\begin_inset Quotes ers +\end_inset + +en thy wages +\end_layout + +\end_deeper +\begin_layout Frame +This is how the concept works, basically. +\end_layout + +\begin_layout Standard + +\begin_inset Separator plain + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Covering vs. hiding +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame +The difference between +\begin_inset Quotes eld +\end_inset + +covering +\begin_inset Quotes erd +\end_inset + + and +\begin_inset Quotes eld +\end_inset + +hiding +\begin_inset Quotes erd +\end_inset + + is that hidden content is treated as if it isn't there, while covered content is just covered (and the space is reserved). If we would have hidden the quote on the last slide and not covered, it would only have taken space on appearance: +\end_layout + +\begin_deeper +\begin_layout Quote + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +only@2 +\end_layout + +\end_inset + +Fear no more the heat o +\begin_inset Quotes ers +\end_inset + + the sun +\end_layout + +\begin_layout Quote +Nor the furious winter +\begin_inset Quotes ers +\end_inset + +s rages +\end_layout + +\begin_layout Quote +Thou thy worldly task hast done +\end_layout + +\begin_layout Quote +Home art gone, and ta +\begin_inset Quotes ers +\end_inset + +en thy wages +\end_layout + +\end_deeper +\begin_layout Frame +You can see how this text moves when the quote is un-hidden. +\end_layout + +\begin_layout Standard + +\begin_inset Separator plain + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Coverage degrees +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout + +\backslash +setbeamercovered +\end_layout + +\end_inset + + +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout +{ +\end_layout + +\end_inset + +transparent +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout +} +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Beamer +\end_layout + +\end_inset + + offers several degrees of +\begin_inset Quotes eld +\end_inset + +coverage +\begin_inset Quotes erd +\end_inset + +, which can be set via the command +\begin_inset Flex Alert +status collapsed + +\begin_layout Plain Layout + +\backslash +setbeamercovered +\end_layout + +\end_inset + + either globally (for the whole presentation) or locally (e. +\begin_inset space \thinspace{} + +\end_inset + +g. for a single frame, as here). By default, content is completely covered. In +\begin_inset Quotes eld +\end_inset + +transparent +\begin_inset Quotes erd +\end_inset + + mode, you can see covered text greyed-out: +\end_layout + +\begin_deeper +\begin_layout Quote + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +2 +\end_layout + +\end_inset + +Fear no more the heat o +\begin_inset Quotes ers +\end_inset + + the sun +\end_layout + +\begin_layout Quote +Nor the furious winter +\begin_inset Quotes ers +\end_inset + +s rages +\end_layout + +\begin_layout Quote +Thou thy worldly task hast done +\end_layout + +\begin_layout Quote +Home art gone, and ta +\begin_inset Quotes ers +\end_inset + +en thy wages +\end_layout + +\end_deeper +\begin_layout Frame +Check the +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Beamer +\end_layout + +\end_inset + + manual for more possibilities. +\end_layout + +\begin_layout Standard + +\begin_inset Separator plain + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Default overlay/action specifications vs. +\begin_inset Newline newline +\end_inset + + (normal) overlay/action specifications +\end_layout + +\end_inset + + +\end_layout + +\begin_deeper +\begin_layout Itemize +For some environments (such as lists and also frames), you can set +\begin_inset Quotes eld +\end_inset + +default specifications +\begin_inset Quotes erd +\end_inset + + additionally to normal overlay/action specifications (or in the case of lists: +\begin_inset Quotes eld +\end_inset + +overlay specifications +\begin_inset Quotes erd +\end_inset + + for the whole list and +\begin_inset Quotes eld +\end_inset + +item overlay specifications +\begin_inset Quotes erd +\end_inset + + for singular items) +\end_layout + +\begin_layout Itemize +Default specifications apply to all content of the given environment, if not individually specified otherwise +\end_layout + +\begin_layout Itemize +They use a placeholder syntax. E. +\begin_inset space \thinspace{} + +\end_inset + +g., +\begin_inset Quotes eld +\end_inset + ++(1)- +\begin_inset Quotes erd +\end_inset + + will uncover all items in a list step by step (with a start offset of 1) if they have no individual item specification: +\end_layout + +\begin_deeper +\begin_layout Itemize + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard ++(1)- +\end_layout + +\end_inset + +One +\end_layout + +\begin_layout Itemize +Two +\end_layout + +\begin_layout Itemize +Three +\end_layout + +\begin_layout Itemize + +\begin_inset Argument item:2 +status collapsed + + +\begin_layout Standard +1- +\end_layout + +\end_inset + +Always +\end_layout + +\end_deeper +\end_deeper +\begin_layout Frame +Please consult the +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Beamer +\end_layout + +\end_inset + + manual for details on this syntax. +\end_layout + +\begin_layout Standard + +\begin_inset Separator plain + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 2 +status collapsed + + +\begin_layout Standard ++- +\end_layout + +\end_inset + + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Default overlay/action specifications vs. +\begin_inset Newline newline +\end_inset + + (normal) overlay/action specifications +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame +\noindent +This frame uses a specific default overlay specification +\end_layout + +\begin_layout Frame +which causes each overlay-aware paragraph \SpecialChar ldots + +\end_layout + +\begin_deeper +\begin_layout Itemize +\SpecialChar ldots + or list item \SpecialChar ldots + +\end_layout + +\begin_layout Itemize +\SpecialChar ldots + to appear \SpecialChar ldots + +\end_layout + +\begin_layout Itemize +\SpecialChar ldots + on a subsequent sub-slide \SpecialChar ldots + +\end_layout + +\end_deeper +\begin_deeper +\begin_layout Block + +\begin_inset Argument 2 +status collapsed + + +\begin_layout Standard +A block +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Block +\SpecialChar ldots + one after the other +\end_layout + +\end_deeper +\begin_layout Standard + +\begin_inset Separator plain + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 2 +status collapsed + + +\begin_layout Standard +alert@+ +\end_layout + +\end_inset + + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Default overlay/action specifications vs. +\begin_inset Newline newline +\end_inset + + (normal) overlay/action specifications +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame +\noindent +And this frame uses a specific default overlay specification \SpecialChar ldots + +\end_layout + +\begin_deeper +\begin_layout Itemize +\SpecialChar ldots + which causes each overlay-aware list item \SpecialChar ldots + +\end_layout + +\begin_layout Itemize +\SpecialChar ldots + to be highlighted \SpecialChar ldots + +\end_layout + +\begin_layout Itemize +\SpecialChar ldots + on respective sub-slides +\end_layout + +\end_deeper +\begin_layout Standard + +\begin_inset Separator plain + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Pause +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame +The +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Pause +\end_layout + +\end_inset + + layout lets you mark a point where all following content will be covered (by default for one slide, with regard to the content preceding the pause): +\end_layout + +\begin_deeper +\begin_layout Pause + +\end_layout + +\end_deeper +\begin_layout Frame +After first pause +\end_layout + +\begin_deeper +\begin_layout Pause + +\end_layout + +\end_deeper +\begin_layout Frame +After second pause +\end_layout + +\begin_deeper +\begin_layout Pause + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +2 +\end_layout + +\end_inset + + +\end_layout + +\end_deeper +\begin_layout Frame +By default, consecutive pauses also end consecutively. +\end_layout + +\begin_layout Frame +Via +\begin_inset Flex Alert +status collapsed + +\begin_layout Plain Layout +Insert →Pause Number +\end_layout + +\end_inset + +, however, you can specify a specific sub-slide at which the given pause ends, independent from the number of pauses inserted before this one. +\end_layout + +\begin_layout Standard + +\begin_inset Separator plain + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Paragraph-wide overlays +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Beamer +\end_layout + +\end_inset + + and LyX provide you with paragraph layouts whose purpose it is to show/hide whole paragraphs or sequences of paragraphs on specific slides. These are particularly: +\end_layout + +\begin_deeper +\begin_layout Uncover + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +2- +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Uncover +The +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Uncovered +\end_layout + +\end_inset + + layout which uncovers all content on the specified slides \SpecialChar ldots + +\end_layout + +\begin_deeper +\begin_layout Itemize +\SpecialChar ldots + including nested paragraphs of other layout. +\end_layout + +\end_deeper +\end_deeper +\begin_deeper +\begin_layout Only + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +3- +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Only +The +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Only +\end_layout + +\end_inset + + layout which un-hides content (note again how the surrounding text +\begin_inset Quotes eld +\end_inset + +moves +\begin_inset Quotes erd +\end_inset + + when this gets visible). +\end_layout + +\end_deeper +\begin_deeper +\begin_layout Overprint + +\begin_inset Argument item:1 +status collapsed + + +\begin_layout Standard +4 +\end_layout + +\end_inset + + +\end_layout + +\begin_deeper +\begin_layout Standard +And the +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Overprint +\end_layout + +\end_inset + + environment which lets you enter \SpecialChar ldots + +\end_layout + +\end_deeper +\begin_layout Overprint + +\begin_inset Argument item:1 +status collapsed + + +\begin_layout Standard +5 +\end_layout + +\end_inset + + +\end_layout + +\begin_deeper +\begin_layout Standard +\SpecialChar ldots + alternative text taking a specific space on specified slides. +\end_layout + +\end_deeper +\end_deeper +\begin_layout Frame +as demonstrated here. +\end_layout + +\begin_layout Standard + +\begin_inset Separator plain + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Inline overlays +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout + +\backslash +setbeamercovered +\end_layout + +\end_inset + + +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout +{ +\end_layout + +\end_inset + +transparent +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout +} +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Beamer +\end_layout + +\end_inset + + also supports inline overlays for text parts (as opposed to whole paragraphs), which are accessible via +\begin_inset Flex Alert +status collapsed + +\begin_layout Plain Layout +Edit →Text Style +\end_layout + +\end_inset + + in LyX: +\end_layout + +\begin_deeper +\begin_layout Itemize +You can +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +uncover +\end_layout + +\end_inset + + +\begin_inset Flex Flex:Uncover +status collapsed + +\begin_layout Plain Layout + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +2- +\end_layout + +\end_inset + +text +\end_layout + +\end_inset + + on specific slides +\end_layout + +\begin_layout Itemize +You can make +\begin_inset Flex Flex:Visible +status collapsed + +\begin_layout Plain Layout + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +3- +\end_layout + +\end_inset + +text +\end_layout + +\end_inset + + +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +visible +\end_layout + +\end_inset + + (which makes a difference to +\begin_inset Quotes eld +\end_inset + +uncover +\begin_inset Quotes erd +\end_inset + + only with +\begin_inset Quotes eld +\end_inset + +transparent +\begin_inset Quotes erd +\end_inset + + coverage setting, as used locally on this slide) +\end_layout + +\begin_layout Itemize +You can show +\begin_inset Flex Flex:Only +status collapsed + +\begin_layout Plain Layout + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +4- +\end_layout + +\end_inset + +text +\end_layout + +\end_inset + + +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +only +\end_layout + +\end_inset + + on specific slides +\end_layout + +\begin_layout Itemize +You can make +\begin_inset Flex Flex:Invisible +status collapsed + +\begin_layout Plain Layout + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +5- +\end_layout + +\end_inset + +text +\end_layout + +\end_inset + + +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +invisible +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Itemize +And you can show +\begin_inset Flex Flex:Alternative +status collapsed + +\begin_layout Plain Layout + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +6- +\end_layout + +\end_inset + + +\begin_inset Argument 2 +status collapsed + + +\begin_layout Standard +different +\end_layout + +\end_inset + + +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +alternative +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + text +\end_layout + +\end_deeper +\begin_layout Frame +As for the paragraph layouts, the overlay settings can be accessed via the +\begin_inset Flex Alert +status collapsed + +\begin_layout Plain Layout +Insert +\end_layout + +\end_inset + + menu. +\end_layout + +\begin_layout Standard + +\begin_inset Separator plain + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Overlay-aware commands +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame +Many +\begin_inset Quotes eld +\end_inset + +inline +\begin_inset Quotes erd +\end_inset + + commands (also to be found at +\begin_inset Flex Alert +status collapsed + +\begin_layout Plain Layout +Edit →Text Style +\end_layout + +\end_inset + +) are overlay-aware. +\end_layout + +\begin_deeper +\begin_layout Itemize +Thus, you can make for instance text on specific slides +\begin_inset Flex Flex:Emphasize +status collapsed + +\begin_layout Plain Layout + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +2 +\end_layout + +\end_inset + +emphasized +\end_layout + +\end_inset + +, +\begin_inset Flex Flex:Bold +status collapsed + +\begin_layout Plain Layout + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +3 +\end_layout + +\end_inset + +bold +\end_layout + +\end_inset + +, shown in +\begin_inset Flex Flex:Alert +status collapsed + +\begin_layout Plain Layout + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +4 +\end_layout + +\end_inset + +alert +\end_layout + +\end_inset + + or +\begin_inset Flex Flex:Structure +status collapsed + +\begin_layout Plain Layout + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +5 +\end_layout + +\end_inset + +structure +\end_layout + +\end_inset + + color. +\end_layout + +\end_deeper +\begin_deeper +\begin_layout Block + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +6 +\end_layout + +\end_inset + + +\begin_inset Argument 2 +status collapsed + + +\begin_layout Standard +Tip +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Block +Use these Emphasize and Bold insets (instead of the usual respective font settings) also if you do not need overlay specifications. Due to the way emphasized and bold is defined in +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Beamer +\end_layout + +\end_inset + +, normal emphasizing and boldface can lead to \SpecialChar LaTeX + errors, e. +\begin_inset space \thinspace{} + +\end_inset + +g. when used in section headings. +\end_layout + +\end_deeper +\begin_layout Section +Specific environments +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Specific environments +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame +Specific environments, particularly suited for presentations are: +\end_layout + +\begin_deeper +\begin_layout Itemize +Diverse +\begin_inset Quotes eld +\end_inset + +blocks +\begin_inset Quotes erd +\end_inset + + +\end_layout + +\begin_layout Itemize +Theorem-style environments +\end_layout + +\begin_layout Itemize +Columns +\end_layout + +\end_deeper +\begin_layout Frame +We sketch them briefly in what follows. +\end_layout + +\begin_layout Standard + +\begin_inset Separator plain + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Blocks +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame +Blocks can contain all sorts of information. We used them here for +\begin_inset Quotes eld +\end_inset + +tips +\begin_inset Quotes erd +\end_inset + + and +\begin_inset Quotes eld +\end_inset + +hints +\begin_inset Quotes erd +\end_inset + +. The class provides 3 pre-defined blocks with different look: +\end_layout + +\begin_deeper +\begin_layout Block + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +2- +\end_layout + +\end_inset + + +\begin_inset Argument 2 +status collapsed + + +\begin_layout Standard +Block +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Block +A general-purpose block +\end_layout + +\end_deeper +\begin_deeper +\begin_layout ExampleBlock + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +3- +\end_layout + +\end_inset + + +\begin_inset Argument 2 +status collapsed + + +\begin_layout Standard +Example Block +\end_layout + +\end_inset + + +\end_layout + +\begin_layout ExampleBlock +A block for +\begin_inset Quotes eld +\end_inset + +examples +\begin_inset Quotes erd +\end_inset + + +\end_layout + +\end_deeper +\begin_deeper +\begin_layout AlertBlock + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +4- +\end_layout + +\end_inset + + +\begin_inset Argument 2 +status collapsed + + +\begin_layout Standard +Alert Block +\end_layout + +\end_inset + + +\end_layout + +\begin_layout AlertBlock +And an +\begin_inset Quotes eld +\end_inset + +alert +\begin_inset Quotes erd +\end_inset + + block for important remarks. +\end_layout + +\end_deeper +\begin_layout Standard + +\begin_inset Separator plain + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Handling Blocks +\end_layout + +\end_inset + + +\end_layout + +\begin_deeper +\begin_layout Itemize +In LyX, blocks have a similar user interface to frames, which means that +\end_layout + +\begin_deeper +\begin_layout Itemize +Content inside blocks needs to be nested (if the paragraph layout is not +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Block +\end_layout + +\end_inset + +) +\end_layout + +\begin_layout Itemize +Consecutive blocks of the same type must be separated by the +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Separator +\end_layout + +\end_inset + + paragraph style +\end_layout + +\begin_deeper +\begin_layout Block + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +only@2 +\end_layout + +\end_inset + + +\begin_inset Argument 2 +status collapsed + + +\begin_layout Standard +Tip +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Block +Use +\begin_inset Flex Alert +status collapsed + +\begin_layout Plain Layout +Edit →Start New Environment +\end_layout + +\end_inset + + ( +\family sans +undefiniert +\family default +) to quickly start a new block from within a previous block! +\end_layout + +\end_deeper +\end_deeper +\begin_layout Itemize +Blocks are overlay-aware +\end_layout + +\end_deeper +\begin_layout Standard + +\begin_inset Separator plain + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Theorem-style environments +\end_layout + +\end_inset + + +\end_layout + +\begin_deeper +\begin_layout FrameSubtitle +(Theorem, Corollary, Definition, Definitions, Example, Examples, Fact, Proof) +\end_layout + +\end_deeper +\begin_layout Frame +Theorems look similar to blocks in the output, but they have a fixed title (depending on the type). +\end_layout + +\begin_deeper +\begin_layout Theorem +This is a theorem! +\end_layout + +\end_deeper +\begin_deeper +\begin_layout Fact +This is a fact! +\end_layout + +\end_deeper +\begin_deeper +\begin_layout Pause + +\end_layout + +\end_deeper +\begin_layout Frame +Via +\begin_inset Flex Alert +status collapsed + +\begin_layout Plain Layout +Insert →Additional Theorem Text +\end_layout + +\end_inset + +, you can add some extra text to this fixed title +\end_layout + +\begin_deeper +\begin_layout Example + +\begin_inset Argument 2 +status collapsed + + +\begin_layout Standard +a bad one! +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Example +An example with additional text (brackets added automatically) +\end_layout + +\end_deeper +\begin_layout Standard + +\begin_inset Separator plain + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Columns +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame +Sometimes it is useful to divide a presentation into columns +\end_layout + +\begin_deeper +\begin_layout Columns + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +t +\end_layout + +\end_inset + + +\end_layout + +\begin_deeper +\begin_layout Column +.4 +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout + +\backslash +textwidth +\end_layout + +\end_inset + + +\end_layout + +\end_deeper +\begin_layout Columns +To do this, first select +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Columns +\end_layout + +\end_inset + + (note the plural) to start the columns +\end_layout + +\begin_deeper +\begin_layout Pause + +\end_layout + +\end_deeper +\begin_deeper +\begin_layout Column +.4 +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout + +\backslash +textwidth +\end_layout + +\end_inset + + +\end_layout + +\end_deeper +\begin_layout Columns +And then, in the following paragraph, select +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Column +\end_layout + +\end_inset + + (singular) to start a specific column +\end_layout + +\end_deeper +\begin_deeper +\begin_layout Pause + +\end_layout + +\end_deeper +\begin_layout Frame + +\begin_inset VSpace medskip +\end_inset + + +\end_layout + +\begin_layout Frame +Note: +\end_layout + +\begin_deeper +\begin_layout Itemize +In the +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Column +\end_layout + +\end_inset + + (singular) environment, you need to specify the width using \SpecialChar LaTeX + syntax (but also something like +\begin_inset Quotes eld +\end_inset + +3.5cm +\begin_inset Quotes erd +\end_inset + + will work) +\end_layout + +\begin_layout Itemize +Any (singular) +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Column +\end_layout + +\end_inset + + must be nested to the (plural) +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Columns +\end_layout + +\end_inset + +. Likewise, column content can be any paragraph style that is nested to a singular +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Column +\end_layout + +\end_inset + + +\end_layout + +\end_deeper +\begin_layout Section +Short remarks on modes +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Modes +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame +In +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Beamer +\end_layout + +\end_inset + + terms, a +\begin_inset Quotes eld +\end_inset + +mode +\begin_inset Quotes erd +\end_inset + + is a specific output route. There are several modes for different purposes. We just want to highlight three: +\end_layout + +\begin_deeper +\begin_layout Enumerate +The +\begin_inset Quotes eld +\end_inset + +beamer +\begin_inset Quotes erd +\end_inset + + mode +\end_layout + +\begin_layout Enumerate +The +\begin_inset Quotes eld +\end_inset + +presentation +\begin_inset Quotes erd +\end_inset + + mode +\end_layout + +\begin_layout Enumerate +The +\begin_inset Quotes eld +\end_inset + +article +\begin_inset Quotes erd +\end_inset + + mode +\end_layout + +\end_deeper +\begin_layout Frame +The beamer mode is the default. Unless explicitly specified otherwise, your +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Beamer +\end_layout + +\end_inset + + document is in +\begin_inset Quotes eld +\end_inset + +beamer +\begin_inset Quotes erd +\end_inset + + mode. +\end_layout + +\begin_layout Standard + +\begin_inset Separator plain + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +presentation +\end_layout + +\end_inset + + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Switching Modes +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame +However, you can switch document parts, frames, headings and all +\begin_inset Quotes eld +\end_inset + +action +\begin_inset Quotes erd +\end_inset + +-aware environments to a different mode. For instance, we have switched this frame to +\begin_inset Quotes eld +\end_inset + +presentation +\begin_inset Quotes erd +\end_inset + + mode. +\end_layout + +\begin_deeper +\begin_layout Itemize +What does this mean? +\end_layout + +\begin_deeper +\begin_layout Itemize +It means that this frame will only be visible in the presentation, not in the accompanying +\begin_inset Quotes eld +\end_inset + +article +\begin_inset Quotes erd +\end_inset + +, if you produce such an article (we will elaborate on this a bit below) +\end_layout + +\end_deeper +\end_deeper +\begin_layout Standard + +\begin_inset Separator plain + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +article +\end_layout + +\end_inset + + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Switching Modes +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame +This frame will not be visible in the presentation, but only in the article, since it is in +\begin_inset Quotes eld +\end_inset + +article +\begin_inset Quotes erd +\end_inset + + mode. +\end_layout + +\begin_layout Standard + +\begin_inset Separator plain + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +So what? +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame +This is actually pretty useful! You can set up a single document and produce both a presentation and – using the article mode – a handout. +\end_layout + +\begin_deeper +\begin_layout Itemize +And we mean a +\emph on +real +\emph default +, useful handout, not one of those scaled slide printouts that are so common nowadays (but if you insist, you can produce one of those as well) +\end_layout + +\begin_layout Itemize +Modes allow you to add extra text to the handout or hide parts from it +\end_layout + +\begin_layout Itemize +You can use for instance different graphics for the presentation and the handout +\end_layout + +\begin_layout Itemize +and so on \SpecialChar ldots + +\end_layout + +\end_deeper +\begin_layout Standard + +\begin_inset Separator plain + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Examples +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame +As said, many elements are mode-aware. +\end_layout + +\begin_deeper +\begin_layout Itemize +You can show particular text +\begin_inset Flex Flex:Only +status collapsed + +\begin_layout Plain Layout + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +presentation +\end_layout + +\end_inset + +only in the presentation +\end_layout + +\end_inset + + +\begin_inset Flex Flex:Only +status collapsed + +\begin_layout Plain Layout + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +article +\end_layout + +\end_inset + +only in the article +\end_layout + +\end_inset + + via +\begin_inset Flex Alert +status collapsed + +\begin_layout Plain Layout +\noindent +Edit →Text Style →Only +\end_layout + +\end_inset + + +\end_layout + +\end_deeper +\begin_layout Frame + +\begin_inset Flex ArticleMode +status collapsed + +\begin_layout Itemize +Or put all sorts of complex contents via +\begin_inset Flex Alert +status collapsed + +\begin_layout Plain Layout +Insert →Custom Insets →ArticleMode +\end_layout + +\end_inset + + in an inset that will only be output in article mode +\end_layout + +\end_inset + + +\begin_inset Flex PresentationMode +status collapsed + +\begin_layout Itemize +Or put all sorts of complex contents via +\begin_inset Flex Alert +status collapsed + +\begin_layout Plain Layout +Insert →Custom Insets →PresentationMode +\end_layout + +\end_inset + + in an inset that will only be output in presentation mode +\end_layout + +\end_inset + + +\end_layout + +\begin_deeper +\begin_layout Itemize +Or you can define that an +\begin_inset Flex Flex:Emphasize +status collapsed + +\begin_layout Plain Layout + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +presentation +\end_layout + +\end_inset + +emphasizing +\end_layout + +\end_inset + + should only apply to the presentation, +\begin_inset Flex Flex:Bold +status collapsed + +\begin_layout Plain Layout + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +article +\end_layout + +\end_inset + +a bold face +\end_layout + +\end_inset + + only to article +\end_layout + +\begin_layout Itemize +You can also show section headings or frame titles\SpecialChar breakableslash +subtitles only in the presentation\SpecialChar breakableslash +article (like we do for the +\begin_inset Quotes eld +\end_inset + +Contents +\begin_inset Quotes erd +\end_inset + + and +\begin_inset Quotes eld +\end_inset + +References +\begin_inset Quotes erd +\end_inset + + frame titles in this presentation) +\end_layout + +\begin_layout Itemize +And much more of this sort \SpecialChar ldots + +\end_layout + +\end_deeper +\begin_layout Standard + +\begin_inset Separator plain + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Setting up an article +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame +Setting up a beamer article with LyX is easy. +\end_layout + +\begin_deeper +\begin_layout Itemize +Just create a new document with the class +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +\noindent +Beamer Article (Standard Class) +\end_layout + +\end_inset + + or +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +\noindent +Beamer Article (KOMA-Script) +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Itemize +Then add the presentation to this document as a child (via +\begin_inset Flex Alert +status collapsed + +\begin_layout Plain Layout +Insert →File →Child Document\SpecialChar ldots + +\end_layout + +\end_inset + +) +\end_layout + +\begin_layout Itemize +And that's it. Now you can produce the handout and the presentation by compiling one of these two documents, while you only need to edit one, namely the presentation +\end_layout + +\end_deeper +\begin_layout Frame +Check out the accompanying beamer-article example document for this presentation. You can find it in the same folder as this document. +\end_layout + +\begin_layout Section +Changing the look +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Themes +\end_layout + +\end_inset + + +\end_layout + +\begin_deeper +\begin_layout Itemize + +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Beamer +\end_layout + +\end_inset + + presentations are themeable. Themes determine the colors used, the macro structure (use of sidebars, headlines etc.), the fonts, the look of list items, blocks and in general the whole look and feel of a presentation +\end_layout + +\begin_layout Itemize + +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Beamer +\end_layout + +\end_inset + + itself ships a number of different-looking themes to chose from (we use the +\begin_inset Quotes eld +\end_inset + +Berkeley +\begin_inset Quotes erd +\end_inset + + theme in this presentation; see +\begin_inset Flex Alert +status collapsed + +\begin_layout Plain Layout +Document →Settings →LaTeX +\begin_inset space ~ + +\end_inset + +Preamble +\end_layout + +\end_inset + + for how we activated and slightly tweaked the theme) +\end_layout + +\begin_layout Itemize +In addition to this standard set, you can get more themes from +\begin_inset CommandInset href +LatexCommand href +name "CTAN" +target "http://www.ctan.org" +literal "false" + +\end_inset + +and other places at the Internet +\end_layout + +\begin_layout Itemize +If you still are not satisified or if you need a theme matching to your University's or company's corporate design, the +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Beamer +\end_layout + +\end_inset + + manual +\begin_inset CommandInset citation +LatexCommand cite +after "" +key "beamer-ug" +literal "false" + +\end_inset + + explains how you can setup your own theme +\end_layout + +\end_deeper +\begin_layout Standard + +\begin_inset Separator plain + +\end_inset + + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +Themes can be modified +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame +But you do not need to write a theme from scratch if you want to alter the look. +\end_layout + +\begin_deeper +\begin_layout Itemize +Existing themes can be modified both in details and in major areas (such as the coloring) +\end_layout + +\begin_layout Itemize +Consult the +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Beamer +\end_layout + +\end_inset + + manual +\begin_inset CommandInset citation +LatexCommand cite +after "" +key "beamer-ug" +literal "false" + +\end_inset + + for details +\end_layout + +\end_deeper +\begin_layout Section +And more \SpecialChar ldots + +\end_layout + +\begin_layout Frame + +\begin_inset Argument 4 +status collapsed + + +\begin_layout Standard +\SpecialChar ldots + much more! +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Frame +Note that +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Beamer +\end_layout + +\end_inset + + can do much more than we have described here. The +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Beamer +\end_layout + +\end_inset + + manual +\begin_inset CommandInset citation +LatexCommand cite +after "" +key "beamer-ug" +literal "false" + +\end_inset + + provides a comprehensive documentation. +\end_layout + +\begin_layout Frame +Also, have a look at the +\begin_inset Flex Structure +status collapsed + +\begin_layout Plain Layout +Beamer +\end_layout + +\end_inset + + examples and templates shipped with LyX! +\end_layout + +\begin_layout Standard +\start_of_appendix + +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout +%dummy comment inserted by tex2lyx to ensure that this paragraph is not empty +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Section +Appendix +\end_layout + +\begin_layout Frame + +\end_layout + +\begin_deeper +\begin_layout FrameTitle + +\begin_inset Argument 1 +status collapsed + + +\begin_layout Standard +presentation +\end_layout + +\end_inset + +References +\end_layout + +\end_deeper +\begin_deeper +\begin_layout Bibliography + +\begin_inset CommandInset bibitem +LatexCommand bibitem +label "" +key "beamer-ug" +literal "false" + +\end_inset + +Tantau, Till et al.: +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout + +\backslash +newblock +\end_layout + +\end_inset + + The beamer class. +\begin_inset Flex URL +status collapsed + +\begin_layout Plain Layout +https://ctan.org/tex-archive/macros/latex/contrib/beamer/doc/beameruserguide.pdf +\end_layout + +\end_inset + +. +\end_layout + +\end_deeper +\end_body +\end_document diff --git a/src/tex2lyx/test/beamer.tex b/src/tex2lyx/test/beamer.tex new file mode 100644 index 0000000000..c5424fee4c --- /dev/null +++ b/src/tex2lyx/test/beamer.tex @@ -0,0 +1,706 @@ +\documentclass[english]{beamer} +\usepackage[T1]{fontenc} +\usepackage[latin9]{inputenc} + +\usepackage{babel} + +\usetheme[left,width=3.45em]{Berkeley} + +\begin{document} + +\title{Presentations with Beamer} + +\subtitle{An Introduction to the Basics} + +\author{John Doe} + +\date{Version~2.3} + +\maketitle + +\begin{frame} + +\frametitle{Contents} + +\tableofcontents +\end{frame} + +\section{Purposes} + +\begin{frame}[<+->]{Purpose of the Beamer class} + +With the \structure{Beamer} class, you can produce presentation slides, +which +\begin{itemize} +\item are visually highly customizable +\item can be very well structured +\item can be constructed step-by-step (``overlay'' concept) +\item may contain different navigation paths (note that the slides contain +all sorts of hyperlinks) +\item use \LaTeX 's superb output quality +\item might embed multimedia content (audio, video) +\item can easily be transformed to accompanying material (such as an article-like +handout) +\item and much more \ldots{} +\end{itemize} +\end{frame} + +\begin{frame}{Purpose of this presentation} + +This presentation +\begin{itemize}[<+->] +\item describes some basic features of \structure{Beamer} +\item especially how they can be used +\end{itemize} +For more general and comprehensive information on \structure{Beamer} +itself, please refer to the extensive class manual \cite{beamer-ug} +\end{frame} + +\section*{Segments of a presentation} + +\begin{frame}{The global structure} + +A presentation usually consists of +\begin{itemize} +\item<+-> a title page +\item<+-> slides that might be grouped to sections/parts +\item[extra]<+-> an appendix with additional information, such as a bibliography +\end{itemize} +We describe these global segments in what follows. +\end{frame} + +\begin{frame}{The title page} + +A title page is constructed by the layouts \structure{Title}, +\structure{Subtitle}, \structure<2>{Author}, \structure{Institute}, +\structure{Date} and \structure{TitleGraphic}. +\begin{itemize} +\item None of these elements is mandatory, but at least one must be given +\item The order of insertion does not matter (the real order is defined +in the \structure{Beamer} theme) +\item For \structure{Title}, \structure{Subtitle}, \structure{Author}, +\structure{Institute} and \structure{Date}, you can define ``short'' +forms via \alert{Insert \textrightarrow Short Title\slash Date\slash\ldots} +These are used in the sidebar\slash heading (given the theme actually +provides a sidebar\slash heading) +\item If you select \structure{Title (Plain Frame)} instead of \structure{Title}, +the title page will have no sidebar or heading +\end{itemize} +\end{frame} + +\begin{frame}<1-2>[label=myframe]{Frames can be repeated} + +Frames can be repeated fully or only in terms of selected sub-slides, +multiple times at any later point of the presentation. + +You just need to give the respective frame a label name via the frame +option ``label'' (as done here). + +\pause{} + +Then you can repeat this frame by means of the \structure{AgainFrame} +layout later in the presentation. Just enter the label name in the +\structure{AgainFrame} layout and specify, if required, which sub-slides +you want to be repeated via \alert{Insert \textrightarrow Overlay Specifications} +(again, see below for the concept of ``overlays''). +\begin{proof}<3> +\alert{Here's the proof!} (This text is only shown on sub-slide +3 which is itself only shown when this frame is repeated later on) +\end{proof} + +\end{frame} + +\begin{frame}{Keeping frames together} + +\framesubtitle{Use nesting!} +\begin{itemize} +\item Note that all frame content, if the style is not \structure{Frame}, +must be nested to the frame environment (via \alert{Edit \textrightarrow Increase List Depth} +or \textsf{Alt+Shift+Right}). This is done automatically if you +insert new frame paragraphs. +\item Nested content is marked by a red bar in the margin of the LyX workarea +\end{itemize} +\end{frame} +\begin{itemize} +\item Non-nested content (such as this) will also be displayed in the presentation +(on a separate slide), but not properly aligned +\item So please avoid this +\end{itemize} + +\begin{frame}{Separating frames} + +\noindent Consecutive frames have to be separated from each other. +This is done by means of the \structure{\noindent Separator} inset, +which can be produced by hitting return in an empty Standard paragraph +right below the frame (see UserGuide, sec.~3.4.6). +\begin{block}{Tip} + +There is a simple and much more convenient way to start a new frame: +Issue \alert{Insert \textrightarrow Separated Frame Below} (\textsf{undefiniert} +if you are in a non-nested \structure{Frame} paragraph, or \textsf{Alt+A Shift+Return}, +respectively, if you are in a nested paragraph within the frame). +If you are in the frame heading, \alert{Insert \textrightarrow Separated Frame Above} +inserts a new, properly separated frame above the current one! +\end{block} +\end{frame} + +\againframe<3>{myframe} + +\begin{frame}[plain]{Special frame types} + +LyX provides two special frame types: +\begin{enumerate} +\item \structure{Frame (plain)} is a frame without a sidebar/header (such +as this one). This is useful for slides with much content\slash wide +tables +\item \structure{Frame (fragile)} is to be used if the frame consists of +``fragile'' content, especially verbatim stuff such as program listings +\end{enumerate} +If you want a fragile plain frame, pass the option ``plain'' to +a fragile frame or the option ``fragile'' to a plain frame. +\end{frame} + +\begin{frame}{Re-arranging frames} +\begin{block}{Tip} + +Did you know that you can easily move and re-arrange whole frames +via the outliner (\alert{View \textrightarrow Outline Pane})? + +Also, you can navigate to a specific frame via the \alert{Navigate} +menu! +\end{block} +\end{frame} + +\section
*{The overlay concept} + +\begin{frame}{What are overlays?} + +Basically, the overlay concept allows to change the slide content +dynamically. You can uncover things/text piecewise, fade out content, +highlight things, replace text, images etc. + +\pause{} +\begin{itemize}[<+->] +\item Overlays are useful to build up slides as you speak +\item They help you to shift your audience's focus on specific things +\item And they help your audience to follow you +\item So use overlays! \alert<6>{Really, use them!} +\end{itemize} +\end{frame} +% +\begin{frame}{Overlay types} + +\structure{Beamer} provides many different overlay types. The most +important ones are: +\begin{description} +\item [{Hidden~content:}] Stuff that is completely invisible up to a point +\item [{Covered~content:}] Stuff that is faded out (not completely invisible) +\item [{Highlighted~content:}] Stuff that is somehow emphasized at a certain +point +\end{description} +We give examples for these types in what follows, but begin with some +general remarks on overlay possibilities +\end{frame} + +\begin{frame}{General overlay/action possibilities} + +Many \structure{Beamer} elements provide overlay settings. Basically, +you can define on which sub-slide(s) a given content appears (``2'', +``2-4'', ``3-'', ``1,3'' etc.), or in which output mode (``presentation'', +``article'' etc.) +\begin{itemize} +\item In LyX, these settings are generally accessible via \alert{Insert \textrightarrow Overlay Specifications} +or \alert{Insert \textrightarrow Action Specifications} +\end{itemize} +\begin{overprint} +\onslide<2> +\begin{definition} +``Action'' is a more general concept, which does not only include +what we have called ``overlays'' (``on which sub-slide{[}s{]} is +this to be shown\slash hidden\slash highlighted''), but also tasks +such as ``only show this in the presentation, not on the handout'' +or ``show this on the second screen only'' (so-called ``modes''). +\end{definition} + +\onslide<3> +\begin{alertblock}{Note to the \LaTeX{} aficionados} + +The mentioned overlay/action settings conform to those command/environment +options embraced by\alert{\ <\ldots >} and \alert{{[}<\ldots >{]}} +in the \LaTeX{} output. + +Note that LyX adds those braces on export, so you must not enter +them yourself. In other words, enter ``1'' or ``+-'' to the overlay/action +insets, not ``<1>'' or ``{[}<+->{]}''! +\end{alertblock} +\end{overprint} +\end{frame} +% +\begin{frame}{An example} + +Take for example a quote. In a \structure{Quote} environment, you +can specify the overlay settings via \alert{Insert \textrightarrow Overlay Specifications}. +If you do this and enter ``2'', the quote will only appear on (sub-)slide +2: +\begin{quote}<2> +Fear no more the heat o\textquoteright{} the sun + +Nor the furious winter\textquoteright s rages + +Thou thy worldly task hast done + +Home art gone, and ta\textquoteright en thy wages +\end{quote} +This is how the concept works, basically. +\end{frame} +% +\begin{frame}{Covering vs. hiding} + +The difference between ``covering'' and ``hiding'' is that hidden +content is treated as if it isn't there, while covered content is +just covered (and the space is reserved). If we would have hidden +the quote on the last slide and not covered, it would only have taken +space on appearance: +\begin{quote} +Fear no more the heat o\textquoteright{} the sun + +Nor the furious winter\textquoteright s rages + +Thou thy worldly task hast done + +Home art gone, and ta\textquoteright en thy wages +\end{quote} + +You can see how this text moves when the quote is un-hidden. +\end{frame} +% +\begin{frame}{Coverage degrees} + +\setbeamercovered{transparent} + +\structure{Beamer} offers several degrees of ``coverage'', which +can be set via the command \alert{\textbackslash setbeamercovered} +either globally (for the whole presentation) or locally (e.\,g. for +a single frame, as here). By default, content is completely covered. +In ``transparent'' mode, you can see covered text greyed-out: +\begin{quote}<2> +Fear no more the heat o\textquoteright{} the sun + +Nor the furious winter\textquoteright s rages + +Thou thy worldly task hast done + +Home art gone, and ta\textquoteright en thy wages +\end{quote} +Check the \structure{Beamer} manual for more possibilities. +\end{frame} +% +\begin{frame}{Default overlay/action specifications vs.\\ +(normal) overlay/action specifications} +\begin{itemize} +\item For some environments (such as lists and also frames), you can set +``default specifications'' additionally to normal overlay/action +specifications (or in the case of lists: ``overlay specifications'' +for the whole list and ``item overlay specifications'' for singular +items) +\item Default specifications apply to all content of the given environment, +if not individually specified otherwise +\item They use a placeholder syntax. E.\,g., ``+(1)-'' will uncover all +items in a list step by step (with a start offset of 1) if they have +no individual item specification: +\begin{itemize}[<+(1)->] +\item One +\item Two +\item Three +\item<1-> Always +\end{itemize} +\end{itemize} +Please consult the \structure{Beamer} manual for details on this +syntax. + +\end{frame} +% +\begin{frame}[<+->]{Default overlay/action specifications vs.\\ +(normal) overlay/action specifications} + +\noindent This frame uses a specific default overlay specification + +which causes each overlay-aware paragraph \ldots{} +\begin{itemize} +\item \ldots{} or list item \ldots{} +\item \ldots{} to appear \ldots{} +\item \ldots{} on a subsequent sub-slide \ldots{} +\end{itemize} +\begin{block}{A block} + +\ldots{} one after the other +\end{block} +\end{frame} +% +\begin{frame}[]{Default overlay/action specifications vs.\\ +(normal) overlay/action specifications} + +\noindent And this frame uses a specific default overlay specification +\ldots{} +\begin{itemize} +\item \ldots{} which causes each overlay-aware list item \ldots{} +\item \ldots{} to be highlighted \ldots{} +\item \ldots{} on respective sub-slides +\end{itemize} +\end{frame} +% +\begin{frame}{Pause} + +The \structure{Pause} layout lets you mark a point where all following +content will be covered (by default for one slide, with regard to +the content preceding the pause): + +\pause{} + +After first pause + +\pause{} + +After second pause + +\pause[2]{} + +By default, consecutive pauses also end consecutively. + +Via \alert{Insert \textrightarrow Pause Number}, however, you can specify +a specific sub-slide at which the given pause ends, independent from +the number of pauses inserted before this one. +\end{frame} +% +\begin{frame}{Paragraph-wide overlays} + +\structure{Beamer} and LyX provide you with paragraph layouts whose +purpose it is to show/hide whole paragraphs or sequences of paragraphs +on specific slides. These are particularly: +\begin{uncoverenv}<2-> + +The \structure{Uncovered} layout which uncovers all content on the +specified slides \ldots{} +\begin{itemize} +\item \ldots{} including nested paragraphs of other layout. +\end{itemize} +\end{uncoverenv} + +\begin{onlyenv}<3-> + +The \structure{Only} layout which un-hides content (note again how +the surrounding text ``moves'' when this gets visible). +\end{onlyenv} + +\begin{overprint} +\onslide<4> + +And the \structure{Overprint} environment which lets you enter \ldots{} +\onslide<5> + +\ldots{} alternative text taking a specific space on specified slides. + +\end{overprint} +as demonstrated here. +\end{frame} +% +\begin{frame}{Inline overlays} + +\setbeamercovered{transparent} + +\structure{Beamer} also supports inline overlays for text parts (as +opposed to whole paragraphs), which are accessible via \alert{Edit \textrightarrow Text Style} +in LyX: +\begin{itemize} +\item You can \structure{uncover} \uncover<2->{text} on specific slides +\item You can make \visible<3->{text} \structure{visible} (which makes +a difference to ``uncover'' only with ``transparent'' coverage +setting, as used locally on this slide) +\item You can show \only<4->{text }\structure{only} on specific slides +\item You can make \invisible<5->{text} \structure{invisible} +\item And you can show \alt<6->{different}{\structure{alternative}} text +\end{itemize} +As for the paragraph layouts, the overlay settings can be accessed +via the \alert{Insert} menu. +\end{frame} +% +\begin{frame}{Overlay-aware commands} + +Many ``inline'' commands (also to be found at \alert{Edit \textrightarrow Text Style}) +are overlay-aware. +\begin{itemize} +\item Thus, you can make for instance text on specific slides \emph<2>{emphasized}, +\textbf<3>{bold}, shown in \alert<4>{alert} or \structure<5>{structure} +color. +\end{itemize} +\begin{block}<6>{Tip} + +Use these Emphasize and Bold insets (instead of the usual respective +font settings) also if you do not need overlay specifications. Due +to the way emphasized and bold is defined in \structure{Beamer}, +normal emphasizing and boldface can lead to \LaTeX{} errors, e.\,g. +when used in section headings. +\end{block} +\end{frame} + +\section{Specific environments} +\begin{frame}{Specific environments} + +Specific environments, particularly suited for presentations are: +\begin{itemize} +\item Diverse ``blocks'' +\item Theorem-style environments +\item Columns +\end{itemize} +We sketch them briefly in what follows. +\end{frame} +% +\begin{frame}{Blocks} + +Blocks can contain all sorts of information. We used them here for +``tips'' and ``hints''. The class provides 3 pre-defined blocks +with different look: +\begin{block}<2->{Block} + +A general-purpose block +\end{block} +\begin{exampleblock}<3->{Example Block} + +A block for ``examples'' +\end{exampleblock} +\begin{alertblock}<4->{Alert Block} + +And an ``alert'' block for important remarks. +\end{alertblock} +\end{frame} +% +\begin{frame}{Handling Blocks} +\begin{itemize} +\item In LyX, blocks have a similar user interface to frames, which means +that +\begin{itemize} +\item Content inside blocks needs to be nested (if the paragraph layout +is not \structure{Block}) +\item Consecutive blocks of the same type must be separated by the \structure{Separator} +paragraph style +\begin{block}{Tip} + +Use \alert{Edit \textrightarrow Start New Environment} (\textsf{undefiniert}) +to quickly start a new block from within a previous block! +\end{block} +\end{itemize} +\item Blocks are overlay-aware +\end{itemize} +\end{frame} +% +\begin{frame}{Theorem-style environments} + +\framesubtitle{(Theorem, Corollary, Definition, Definitions, Example, Examples, +Fact, Proof)} + +Theorems look similar to blocks in the output, but they have a fixed +title (depending on the type). +\begin{theorem} +This is a theorem! +\end{theorem} + +\begin{fact} +This is a fact! +\end{fact} + + +\pause{} + +Via \alert{Insert \textrightarrow Additional Theorem Text}, you can add +some extra text to this fixed title +\begin{example}[a bad one!] + +An example with additional text (brackets added automatically) +\end{example} + +\end{frame} +% +\begin{frame}{Columns} + +Sometimes it is useful to divide a presentation into columns +\begin{columns}[t] + +\column{.4\textwidth} + +To do this, first select \structure{Columns} (note the plural) to +start the columns + +\pause{} + +\column{.4\textwidth} + +And then, in the following paragraph, select \structure{Column} (singular) +to start a specific column +\end{columns} + + +\pause{} + +\medskip{} + +Note: +\begin{itemize} +\item In the \structure{Column} (singular) environment, you need to specify +the width using \LaTeX{} syntax (but also something like ``3.5cm'' +will work) +\item Any (singular) \structure{Column} must be nested to the (plural) +\structure{Columns}. Likewise, column content can be any paragraph +style that is nested to a singular \structure{Column} +\end{itemize} +\end{frame} + +\section{Short remarks on modes} +\begin{frame}{Modes} + +In \structure{Beamer} terms, a ``mode'' is a specific output route. +There are several modes for different purposes. We just want to highlight +three: +\begin{enumerate} +\item The ``beamer'' mode +\item The ``presentation'' mode +\item The ``article'' mode +\end{enumerate} +The beamer mode is the default. Unless explicitly specified otherwise, +your \structure{Beamer} document is in ``beamer'' mode. +\end{frame} +% +\begin{frame}{Switching Modes} + +However, you can switch document parts, frames, headings and all ``action''-aware +environments to a different mode. For instance, we have switched this +frame to ``presentation'' mode. +\begin{itemize} +\item What does this mean? +\begin{itemize} +\item It means that this frame will only be visible in the presentation, +not in the accompanying ``article'', if you produce such an article +(we will elaborate on this a bit below) +\end{itemize} +\end{itemize} +\end{frame} +% +\begin{frame}
{Switching Modes} + +This frame will not be visible in the presentation, but only in the +article, since it is in ``article'' mode. +\end{frame} +% +\begin{frame}{So what?} + +This is actually pretty useful! You can set up a single document and +produce both a presentation and \textendash{} using the article mode +\textendash{} a handout. +\begin{itemize} +\item And we mean a \emph{real}, useful handout, not one of those scaled +slide printouts that are so common nowadays (but if you insist, you +can produce one of those as well) +\item Modes allow you to add extra text to the handout or hide parts from +it +\item You can use for instance different graphics for the presentation and +the handout +\item and so on \ldots{} +\end{itemize} +\end{frame} +% +\begin{frame}{Examples} + +As said, many elements are mode-aware. +\begin{itemize} +\item You can show particular text \only{only in the presentation}\only
{only in the article} +via \alert{\noindent Edit \textrightarrow Text Style \textrightarrow Only} +\end{itemize} +\mode
{\begin{itemize} +\item Or put all sorts of complex contents via \alert{Insert \textrightarrow Custom Insets \textrightarrow ArticleMode} +in an inset that will only be output in article mode +\end{itemize} +}\mode{\begin{itemize} +\item Or put all sorts of complex contents via \alert{Insert \textrightarrow Custom Insets \textrightarrow PresentationMode} +in an inset that will only be output in presentation mode +\end{itemize} +} +\begin{itemize} +\item Or you can define that an \emph{emphasizing} should +only apply to the presentation, \textbf
{a bold face} only +to article +\item You can also show section headings or frame titles\slash subtitles +only in the presentation\slash article (like we do for the ``Contents'' +and ``References'' frame titles in this presentation) +\item And much more of this sort \ldots{} +\end{itemize} +\end{frame} +% +\begin{frame}{Setting up an article} + +Setting up a beamer article with LyX is easy. +\begin{itemize} +\item Just create a new document with the class \structure{\noindent Beamer Article (Standard Class)} +or \structure{\noindent Beamer Article (KOMA-Script)} +\item Then add the presentation to this document as a child (via \alert{Insert \textrightarrow File \textrightarrow Child Document\ldots}) +\item And that's it. Now you can produce the handout and the presentation +by compiling one of these two documents, while you only need to edit +one, namely the presentation +\end{itemize} +Check out the accompanying beamer-article example document for this +presentation. You can find it in the same folder as this document. +\end{frame} + +\section{Changing the look} +\begin{frame}{Themes} +\begin{itemize} +\item \structure{Beamer} presentations are themeable. Themes determine +the colors used, the macro structure (use of sidebars, headlines etc.), +the fonts, the look of list items, blocks and in general the whole +look and feel of a presentation +\item \structure{Beamer} itself ships a number of different-looking themes +to chose from (we use the ``Berkeley'' theme in this presentation; +see \alert{Document \textrightarrow Settings \textrightarrow LaTeX~Preamble} for +how we activated and slightly tweaked the theme) +\item In addition to this standard set, you can get more themes from \href{http://www.ctan.org}{CTAN} +and other places at the Internet +\item If you still are not satisified or if you need a theme matching to +your University's or company's corporate design, the \structure{Beamer} +manual \cite{beamer-ug} explains how you can setup your own theme +\end{itemize} +\end{frame} +% +\begin{frame}{Themes can be modified} + +But you do not need to write a theme from scratch if you want to alter +the look. +\begin{itemize} +\item Existing themes can be modified both in details and in major areas +(such as the coloring) +\item Consult the \structure{Beamer} manual \cite{beamer-ug} for details +\end{itemize} +\end{frame} + +\section{And more \ldots} +\begin{frame}{\ldots{} much more!} + +Note that \structure{Beamer} can do much more than we have described +here. The \structure{Beamer} manual \cite{beamer-ug} provides a +comprehensive documentation. + +Also, have a look at the \structure{Beamer} examples and templates +shipped with LyX! +\end{frame} + +\appendix + +\section{Appendix} + +\begin{frame} + +\frametitle{References} +\begin{thebibliography}{1} +\bibitem{beamer-ug}Tantau, Till et al.:\newblock The beamer class. +\url{https://ctan.org/tex-archive/macros/latex/contrib/beamer/doc/beameruserguide.pdf}. +\end{thebibliography} +\end{frame} + +\end{document} diff --git a/src/tex2lyx/test/box-color-size-space-align.lyx.lyx b/src/tex2lyx/test/box-color-size-space-align.lyx.lyx index 30a2b15551..b6926c7eab 100644 --- a/src/tex2lyx/test/box-color-size-space-align.lyx.lyx +++ b/src/tex2lyx/test/box-color-size-space-align.lyx.lyx @@ -2878,7 +2878,47 @@ Characters \begin_layout Standard \size normal -from package "ascii" and "ifsym": ®↨◻◼◙ +from package +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout +" +\end_layout + +\end_inset + +ascii +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout +" +\end_layout + +\end_inset + + and +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout +" +\end_layout + +\end_inset + +ifsym +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout +" +\end_layout + +\end_inset + +: ®↨◻◼◙ \end_layout \begin_layout Standard diff --git a/src/tex2lyx/test/runtests.py b/src/tex2lyx/test/runtests.py index 74f5cf7972..7c82909ded 100755 --- a/src/tex2lyx/test/runtests.py +++ b/src/tex2lyx/test/runtests.py @@ -74,6 +74,7 @@ def main(argv): else: files = ['test.ltx', \ 'algo2e.tex', \ + 'beamer.tex', \ 'box-color-size-space-align.tex', \ 'CJK.tex', \ 'CJKutf8.tex', \ diff --git a/src/tex2lyx/test/test-insets-basic.lyx.lyx b/src/tex2lyx/test/test-insets-basic.lyx.lyx index 3e29da2602..540c80c5d6 100644 --- a/src/tex2lyx/test/test-insets-basic.lyx.lyx +++ b/src/tex2lyx/test/test-insets-basic.lyx.lyx @@ -375,7 +375,7 @@ LatexCommand citep after "after" before "" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -387,7 +387,7 @@ LatexCommand citep after "after" before "" key "whole-set,article-crossref" -literal "true" +literal "false" \end_inset @@ -407,7 +407,7 @@ LatexCommand citet after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -419,7 +419,7 @@ LatexCommand citet* after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -431,7 +431,7 @@ LatexCommand Citet after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -443,7 +443,7 @@ LatexCommand Citet* after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -459,7 +459,7 @@ LatexCommand citep after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -471,7 +471,7 @@ LatexCommand citep* after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -483,7 +483,7 @@ LatexCommand Citep after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -495,7 +495,7 @@ LatexCommand Citep* after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -511,7 +511,7 @@ LatexCommand citealt after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -523,7 +523,7 @@ LatexCommand citealt* after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -535,7 +535,7 @@ LatexCommand Citealt after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -547,7 +547,7 @@ LatexCommand Citealt* after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -563,7 +563,7 @@ LatexCommand citealp after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -575,7 +575,7 @@ LatexCommand citealp* after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -587,7 +587,7 @@ LatexCommand Citep after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -599,7 +599,7 @@ LatexCommand Citealp* after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -615,7 +615,7 @@ LatexCommand citeauthor after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -627,7 +627,7 @@ LatexCommand citeauthor* after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -639,7 +639,7 @@ LatexCommand Citeauthor after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -651,7 +651,7 @@ LatexCommand Citeauthor* after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -667,7 +667,7 @@ LatexCommand citeyear after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -683,7 +683,7 @@ LatexCommand citeyearpar after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -698,7 +698,7 @@ nocite: LatexCommand nocite after "" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -775,7 +775,7 @@ LatexCommand citet after "" before "" key "gur+04" -literal "true" +literal "false" \end_inset @@ -788,7 +788,7 @@ literal "true" LatexCommand bibitem label "Möstl et~al.(2010)" key "Mostl2010" -literal "true" +literal "false" \end_inset @@ -811,7 +811,7 @@ A. 2004, ApJ, 604, 632 LatexCommand bibitem label "{{Gürkan et~al.}(2004)}" key "gur+04" -literal "true" +literal "false" \end_inset @@ -954,7 +954,7 @@ LatexCommand nomenclature prefix "www" symbol "URL" description "uniform resource locator" -literal "true" +literal "false" \end_inset @@ -963,7 +963,7 @@ literal "true" \begin_layout Standard An URL: -\begin_inset Flex Flex:URL +\begin_inset Flex URL status collapsed \begin_layout Plain Layout @@ -977,7 +977,7 @@ http://www.lyx.org LatexCommand nomenclature symbol "URL2" description "uniform resource locator" -literal "true" +literal "false" \end_inset @@ -986,7 +986,7 @@ literal "true" \begin_layout Standard An URL with strange characters: -\begin_inset Flex Flex:URL +\begin_inset Flex URL status collapsed \begin_layout Plain Layout @@ -1003,7 +1003,7 @@ link: \begin_inset CommandInset href LatexCommand href target "www.test.test" -literal "true" +literal "false" \end_inset @@ -1015,7 +1015,7 @@ link2: \begin_inset CommandInset href LatexCommand href target "http://www.test.test" -literal "true" +literal "false" \end_inset @@ -1029,7 +1029,7 @@ LatexCommand href name "name" target "www.test.test" type "mailto:" -literal "true" +literal "false" \end_inset @@ -1042,7 +1042,7 @@ file: LatexCommand href target "www.test.test" type "file:" -literal "true" +literal "false" \end_inset @@ -1054,7 +1054,7 @@ ftp: \begin_inset CommandInset href LatexCommand href target "ftp://www.test.test" -literal "true" +literal "false" \end_inset @@ -1067,7 +1067,7 @@ ftp2: LatexCommand href name "www.test.test" target "ftp://www.test.test" -literal "true" +literal "false" \end_inset @@ -1111,7 +1111,7 @@ Inline: \begin_inset listings lstparams "language={C++},keywordstyle={\color{green}}" inline true -status collapsed +status open \begin_layout Plain Layout int a=5; @@ -1131,7 +1131,7 @@ int a=5; \begin_inset listings lstparams "caption={Example Listing float},label={lst:Example-Listing},language=Python" inline false -status collapsed +status open \begin_layout Plain Layout # Example listing float @@ -1215,7 +1215,7 @@ symbol \begin_inset listings lstparams "extendedchars=true,firstline=3,language=Python,lastline=8,numbers=left,showspaces=true,stepnumber=3" inline false -status collapsed +status open \begin_layout Plain Layout def func(param): @@ -1263,7 +1263,7 @@ Special cases: \begin_inset listings lstparams "abovecaptionskip=2em,basicstyle={\large\ttfamily},breaklines=true,extendedchars=true,firstline=2,float=h,language={[R/3 3.1]ABAP},lastline=5,numbers=left,numberstyle={\scriptsize},showspaces=true,showstringspaces=false,stepnumber=3,tabsize=4" inline false -status collapsed +status open \begin_layout Plain Layout hello @@ -1279,7 +1279,7 @@ hello \begin_inset listings lstparams "language=TeX" inline true -status collapsed +status open \begin_layout Plain Layout @@ -6469,22 +6469,12 @@ tz \begin_inset IPA \begin_layout Standard -:;eˈˌ|‖.͡* -\begin_inset ERT -status collapsed - -\begin_layout Plain Layout -{ -\end_layout - -\end_inset +:;eˈˌ|‖. +\begin_inset IPADeco bottomtiebar +status open +\begin_layout Standard -\begin_inset ERT -status collapsed - -\begin_layout Plain Layout -} \end_layout \end_inset @@ -7063,7 +7053,27 @@ literal "true" \end_layout \begin_layout Standard -index "idx": +index +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout +" +\end_layout + +\end_inset + +idx +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout +" +\end_layout + +\end_inset + +: \end_layout \begin_layout Standard @@ -7079,7 +7089,27 @@ literal "true" \end_layout \begin_layout Standard -index "new": +index +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout +" +\end_layout + +\end_inset + +new +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout +" +\end_layout + +\end_inset + +: \end_layout \begin_layout Standard @@ -7095,7 +7125,27 @@ literal "true" \end_layout \begin_layout Standard -subindex "new": +subindex +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout +" +\end_layout + +\end_inset + +new +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout +" +\end_layout + +\end_inset + +: \end_layout \begin_layout Standard diff --git a/src/tex2lyx/test/test-insets.lyx.lyx b/src/tex2lyx/test/test-insets.lyx.lyx index 0aa2ee886a..cebb68e2d9 100644 --- a/src/tex2lyx/test/test-insets.lyx.lyx +++ b/src/tex2lyx/test/test-insets.lyx.lyx @@ -355,7 +355,7 @@ LatexCommand citep after "after" before "" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -367,7 +367,7 @@ LatexCommand citep after "after" before "" key "whole-set,article-crossref" -literal "true" +literal "false" \end_inset @@ -387,7 +387,7 @@ LatexCommand citet after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -399,7 +399,7 @@ LatexCommand citet* after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -411,7 +411,7 @@ LatexCommand Citet after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -423,7 +423,7 @@ LatexCommand Citet* after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -439,7 +439,7 @@ LatexCommand citep after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -451,7 +451,7 @@ LatexCommand citep* after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -463,7 +463,7 @@ LatexCommand Citep after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -475,7 +475,7 @@ LatexCommand Citep* after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -491,7 +491,7 @@ LatexCommand citealt after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -503,7 +503,7 @@ LatexCommand citealt* after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -515,7 +515,7 @@ LatexCommand Citealt after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -527,7 +527,7 @@ LatexCommand Citealt* after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -543,7 +543,7 @@ LatexCommand citealp after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -555,7 +555,7 @@ LatexCommand citealp* after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -567,7 +567,7 @@ LatexCommand Citep after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -579,7 +579,7 @@ LatexCommand Citealp* after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -595,7 +595,7 @@ LatexCommand citeauthor after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -607,7 +607,7 @@ LatexCommand citeauthor* after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -619,7 +619,7 @@ LatexCommand Citeauthor after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -631,7 +631,7 @@ LatexCommand Citeauthor* after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -647,7 +647,7 @@ LatexCommand citeyear after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -663,7 +663,7 @@ LatexCommand citeyearpar after "after" before "before" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -678,7 +678,7 @@ nocite: LatexCommand nocite after "" key "article-crossref" -literal "true" +literal "false" \end_inset @@ -755,7 +755,7 @@ LatexCommand citet after "" before "" key "gur+04" -literal "true" +literal "false" \end_inset @@ -768,7 +768,7 @@ literal "true" LatexCommand bibitem label "Möstl et~al.(2010)" key "Mostl2010" -literal "true" +literal "false" \end_inset @@ -791,7 +791,7 @@ A. 2004, ApJ, 604, 632 LatexCommand bibitem label "{{Gürkan et~al.}(2004)}" key "gur+04" -literal "true" +literal "false" \end_inset @@ -987,7 +987,7 @@ LatexCommand nomenclature prefix "www" symbol "URL" description "uniform resource locator" -literal "true" +literal "false" \end_inset @@ -996,7 +996,7 @@ literal "true" \begin_layout Standard An URL: -\begin_inset Flex Flex:URL +\begin_inset Flex URL status collapsed \begin_layout Plain Layout @@ -1010,7 +1010,7 @@ http://www.lyx.org LatexCommand nomenclature symbol "URL2" description "uniform resource locator" -literal "true" +literal "false" \end_inset @@ -1019,7 +1019,7 @@ literal "true" \begin_layout Standard An URL with strange characters: -\begin_inset Flex Flex:URL +\begin_inset Flex URL status collapsed \begin_layout Plain Layout @@ -1036,7 +1036,7 @@ link: \begin_inset CommandInset href LatexCommand href target "www.test.test" -literal "true" +literal "false" \end_inset @@ -1048,7 +1048,7 @@ link2: \begin_inset CommandInset href LatexCommand href target "http://www.test.test" -literal "true" +literal "false" \end_inset @@ -1062,7 +1062,7 @@ LatexCommand href name "name" target "www.test.test" type "mailto:" -literal "true" +literal "false" \end_inset @@ -1075,7 +1075,7 @@ file: LatexCommand href target "www.test.test" type "file:" -literal "true" +literal "false" \end_inset @@ -1087,7 +1087,7 @@ ftp: \begin_inset CommandInset href LatexCommand href target "ftp://www.test.test" -literal "true" +literal "false" \end_inset @@ -1100,7 +1100,7 @@ ftp2: LatexCommand href name "www.test.test" target "ftp://www.test.test" -literal "true" +literal "false" \end_inset @@ -1113,7 +1113,7 @@ parser test (stupid, but valid): LatexCommand href name "}" target "http://www.test.test" -literal "true" +literal "false" \end_inset @@ -1126,7 +1126,7 @@ parser test (escaped): LatexCommand href name "a brace } and another one { and something" target "http://www.test.test" -literal "true" +literal "false" \end_inset @@ -1170,7 +1170,7 @@ Inline: \begin_inset listings lstparams "language={C++},keywordstyle={\color{green}}" inline true -status collapsed +status open \begin_layout Plain Layout int a=5; @@ -1190,7 +1190,7 @@ int a=5; \begin_inset listings lstparams "caption={Example Listing float},label={lst:Example-Listing},language=Python" inline false -status collapsed +status open \begin_layout Plain Layout # Example listing float @@ -1274,7 +1274,7 @@ symbol \begin_inset listings lstparams "extendedchars=true,firstline=3,language=Python,lastline=8,numbers=left,showspaces=true,stepnumber=3" inline false -status collapsed +status open \begin_layout Plain Layout def func(param): @@ -1322,7 +1322,7 @@ Special cases: \begin_inset listings lstparams "abovecaptionskip=2em,basicstyle={\large\ttfamily},breaklines=true,extendedchars=true,firstline=2,float=h,language={[R/3 3.1]ABAP},lastline=5,numbers=left,numberstyle={\scriptsize},showspaces=true,showstringspaces=false,stepnumber=3,tabsize=4" inline false -status collapsed +status open \begin_layout Plain Layout hello @@ -1338,7 +1338,7 @@ hello \begin_inset listings lstparams "language=TeX" inline true -status collapsed +status open \begin_layout Plain Layout @@ -6914,22 +6914,12 @@ tz \begin_inset IPA \begin_layout Standard -:;eˈˌ|‖.͡* -\begin_inset ERT -status collapsed - -\begin_layout Plain Layout -{ -\end_layout - -\end_inset +:;eˈˌ|‖. +\begin_inset IPADeco bottomtiebar +status open +\begin_layout Standard -\begin_inset ERT -status collapsed - -\begin_layout Plain Layout -} \end_layout \end_inset @@ -7516,7 +7506,27 @@ literal "true" \end_layout \begin_layout Standard -index "idx": +index +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout +" +\end_layout + +\end_inset + +idx +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout +" +\end_layout + +\end_inset + +: \end_layout \begin_layout Standard @@ -7532,7 +7542,27 @@ literal "true" \end_layout \begin_layout Standard -index "new": +index +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout +" +\end_layout + +\end_inset + +new +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout +" +\end_layout + +\end_inset + +: \end_layout \begin_layout Standard @@ -7548,7 +7578,27 @@ literal "true" \end_layout \begin_layout Standard -subindex "new": +subindex +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout +" +\end_layout + +\end_inset + +new +\begin_inset ERT +status collapsed + +\begin_layout Plain Layout +" +\end_layout + +\end_inset + +: \end_layout \begin_layout Standard diff --git a/src/tex2lyx/test/test-minted.lyx.lyx b/src/tex2lyx/test/test-minted.lyx.lyx index 0af108d2d3..426902b5bc 100644 --- a/src/tex2lyx/test/test-minted.lyx.lyx +++ b/src/tex2lyx/test/test-minted.lyx.lyx @@ -102,7 +102,7 @@ Inline: \begin_inset listings lstparams "style=bw,language=C++" inline true -status collapsed +status open \begin_layout Plain Layout int a=5; @@ -137,7 +137,7 @@ noprefix "false" \begin_inset listings lstparams "language=Python,float=h" inline false -status collapsed +status open \begin_layout Plain Layout # Example listing float @@ -216,7 +216,7 @@ symbol \begin_inset listings lstparams "firstline=4,numbers=left,showspaces=true,language=Python" inline false -status collapsed +status open \begin_layout Plain Layout def func(param): @@ -252,7 +252,7 @@ A floating one-liner with [h] placement and without caption: \begin_inset listings lstparams "fontfamily=tt,fontsize={\large},bgcolor=lightgray,language=ABAP,float=h" inline false -status collapsed +status open \begin_layout Plain Layout hello @@ -269,7 +269,7 @@ Another inline listing: \begin_inset listings lstparams "language=TeX" inline true -status collapsed +status open \begin_layout Plain Layout @@ -298,11 +298,11 @@ noprefix "false" \end_inset -): +): \begin_inset listings lstparams "numbers=left,frame=lines,language=C" inline false -status collapsed +status open \begin_layout Plain Layout @@ -372,7 +372,7 @@ noprefix "false" \begin_inset listings lstparams "numbers=left,frame=lines,language=Fortran" inline false -status collapsed +status open \begin_layout Plain Layout subroutine incr(i) @@ -417,10 +417,6 @@ A Fortran subroutine \end_inset - -\end_layout - -\begin_layout Standard A framed floating listing with a caption and a label (Listing \begin_inset space ~ @@ -440,7 +436,7 @@ noprefix "false" \begin_inset listings lstparams "frame=single,language=Python,float=h" inline false -status collapsed +status open \begin_layout Plain Layout def boring(args = None): @@ -495,7 +491,7 @@ noprefix "false" ): \begin_inset listings lstparams "frame=single,language=Python,float=h" inline false -status collapsed +status open \begin_layout Plain Layout def boring(args = None): diff --git a/src/tex2lyx/test/test-structure.lyx.lyx b/src/tex2lyx/test/test-structure.lyx.lyx index b1cb6808a8..56a549ed85 100644 --- a/src/tex2lyx/test/test-structure.lyx.lyx +++ b/src/tex2lyx/test/test-structure.lyx.lyx @@ -215,7 +215,7 @@ A section with optional argument \begin_layout Standard This causes the -\begin_inset Flex Flex:Strong +\begin_inset Flex Strong status collapsed \begin_layout Plain Layout @@ -604,7 +604,6 @@ klöä \end_layout - \end_inset @@ -651,7 +650,6 @@ te%st \end_layout - \end_inset @@ -686,7 +684,6 @@ subfigure 2ö \end_layout - \end_inset @@ -895,9 +892,10 @@ the second item \begin_layout Itemize \begin_inset Argument item:1 -status open +status collapsed -\begin_layout Plain Layout + +\begin_layout Standard custom label \end_layout @@ -1103,7 +1101,7 @@ and bibliography: LatexCommand bibitem label "" key "FOO" -literal "true" +literal "false" \end_inset @@ -1120,7 +1118,7 @@ The Foo Book LatexCommand bibitem label "" key "FO2" -literal "true" +literal "false" \end_inset diff --git a/src/tex2lyx/tex2lyx.cpp b/src/tex2lyx/tex2lyx.cpp index fc6ea1efc8..416f1268f3 100644 --- a/src/tex2lyx/tex2lyx.cpp +++ b/src/tex2lyx/tex2lyx.cpp @@ -172,30 +172,32 @@ void add_known_theorem(string const & theorem, string const & o1, } -Layout const * findLayoutWithoutModule(TextClass const & textclass, - string const & name, bool command) +Layout const * findLayoutWithoutModule(TextClass const & tc, + string const & name, bool command, + string const & latexparam) { - DocumentClass::const_iterator it = textclass.begin(); - DocumentClass::const_iterator en = textclass.end(); - for (; it != en; ++it) { - if (it->latexname() == name && - ((command && it->isCommand()) || (!command && it->isEnvironment()))) - return &*it; + for (auto const & lay : tc) { + if (lay.latexname() == name && + (latexparam.empty() || + (!lay.latexparam().empty() && suffixIs(latexparam, lay.latexparam()))) && + ((command && lay.isCommand()) || (!command && lay.isEnvironment()))) + return &lay; } return 0; } -InsetLayout const * findInsetLayoutWithoutModule(TextClass const & textclass, - string const & name, bool command) +InsetLayout const * findInsetLayoutWithoutModule(TextClass const & tc, + string const & name, bool command, + string const & latexparam) { - DocumentClass::InsetLayouts::const_iterator it = textclass.insetLayouts().begin(); - DocumentClass::InsetLayouts::const_iterator en = textclass.insetLayouts().end(); - for (; it != en; ++it) { - if (it->second.latexname() == name && - ((command && it->second.latextype() == InsetLayout::COMMAND) || - (!command && it->second.latextype() == InsetLayout::ENVIRONMENT))) - return &(it->second); + for (auto const & ilay : tc.insetLayouts()) { + if (ilay.second.latexname() == name && + (latexparam.empty() || + (!ilay.second.latexparam().empty() && suffixIs(latexparam, ilay.second.latexparam()))) && + ((command && ilay.second.latextype() == InsetLayout::COMMAND) || + (!command && ilay.second.latextype() == InsetLayout::ENVIRONMENT))) + return &(ilay.second); } return 0; } @@ -878,6 +880,8 @@ bool tex2lyx(idocstream & is, ostream & os, string const & encoding, context.font.language = preamble.defaultLanguage(); // parse the main text parse_text(p, ss, FLAG_END, true, context); + // check if we need a commented bibtex inset (biblatex) + check_comment_bib(ss, context); if (Context::empty) // Empty document body. LyX needs at least one paragraph. context.check_layout(ss); diff --git a/src/tex2lyx/tex2lyx.h b/src/tex2lyx/tex2lyx.h index 5c3595f447..1791c2f414 100644 --- a/src/tex2lyx/tex2lyx.h +++ b/src/tex2lyx/tex2lyx.h @@ -48,7 +48,15 @@ extern std::string rgbcolor2code(std::string const & name); std::string translate_len(std::string const &); void parse_text(Parser & p, std::ostream & os, unsigned flags, bool outer, - Context & context); + Context & context, std::string const rdelim = std::string()); +void check_comment_bib(std::ostream & os, Context & context); + +void fix_child_filename(std::string & name); + +std::string const normalize_filename(std::string const & name); + +std::string find_file(std::string const & name, std::string const & path, + char const * const * extensions); /*! * Parses a subdocument, usually useful in insets (whence the name). @@ -59,7 +67,8 @@ void parse_text(Parser & p, std::ostream & os, unsigned flags, bool outer, */ void parse_text_in_inset(Parser & p, std::ostream & os, unsigned flags, bool outer, Context const & context, - InsetLayout const * layout = 0); + InsetLayout const * layout = 0, + std::string const rdelim = std::string()); /// Guess document language from \p p if CJK is used. /// \p lang is used for all non-CJK contents. @@ -103,10 +112,12 @@ extern void add_known_environment(std::string const & environment, docstring const & end); extern void add_known_theorem(std::string const & theorem, std::string const & o1, bool o2, docstring const & definition); -extern Layout const * findLayoutWithoutModule(TextClass const & textclass, - std::string const & name, bool command); -extern InsetLayout const * findInsetLayoutWithoutModule( - TextClass const & textclass, std::string const & name, bool command); +extern Layout const * findLayoutWithoutModule(TextClass const & tc, + std::string const & name, bool command, + std::string const & latexparam = std::string()); +extern InsetLayout const * findInsetLayoutWithoutModule(TextClass const & tc, + std::string const & name, bool command, + std::string const & latexparam = std::string()); /*! * Check whether a module provides command (if \p command is true) or * environment (if \p command is false) \p name, and add the module to the diff --git a/src/tex2lyx/text.cpp b/src/tex2lyx/text.cpp index a3206797f7..f4002b044a 100644 --- a/src/tex2lyx/text.cpp +++ b/src/tex2lyx/text.cpp @@ -47,14 +47,15 @@ namespace lyx { namespace { -void output_arguments(ostream &, Parser &, bool, bool, bool, Context &, +void output_arguments(ostream &, Parser &, bool, bool, string, Context &, Layout::LaTeXArgMap const &); } void parse_text_in_inset(Parser & p, ostream & os, unsigned flags, bool outer, - Context const & context, InsetLayout const * layout) + Context const & context, InsetLayout const * layout, + string const rdelim) { bool const forcePlainLayout = layout ? layout->forcePlainLayout() : false; @@ -64,11 +65,18 @@ void parse_text_in_inset(Parser & p, ostream & os, unsigned flags, bool outer, else newcontext.font = context.font; if (layout) - output_arguments(os, p, outer, false, false, newcontext, + output_arguments(os, p, outer, false, string(), newcontext, layout->latexargs()); - parse_text(p, os, flags, outer, newcontext); + // If we have a latex param, we eat it here. + if (!context.latexparam.empty()) { + ostringstream oss; + Context dummy(true, context.textclass); + parse_text(p, oss, FLAG_RDELIM, outer, dummy, + string(1, context.latexparam.back())); + } + parse_text(p, os, flags, outer, newcontext, rdelim); if (layout) - output_arguments(os, p, outer, false, true, newcontext, + output_arguments(os, p, outer, false, "post", newcontext, layout->postcommandargs()); newcontext.check_end_layout(os); } @@ -77,14 +85,15 @@ void parse_text_in_inset(Parser & p, ostream & os, unsigned flags, bool outer, namespace { void parse_text_in_inset(Parser & p, ostream & os, unsigned flags, bool outer, - Context const & context, string const & name) + Context const & context, string const & name, + string const rdelim = string()) { InsetLayout const * layout = 0; DocumentClass::InsetLayouts::const_iterator it = context.textclass.insetLayouts().find(from_ascii(name)); if (it != context.textclass.insetLayouts().end()) layout = &(it->second); - parse_text_in_inset(p, os, flags, outer, context, layout); + parse_text_in_inset(p, os, flags, outer, context, layout, rdelim); } /// parses a paragraph snippet, useful for example for \\emph{...} @@ -184,17 +193,31 @@ char const * const known_jurabib_commands[] = { "cite", "citet", "citep", // "footciteauthor", "footciteyear", "footciteyearpar", "citefield", "citetitle", 0 }; +/*! + * biblatex commands. + * Known starred forms: \cite*, \citeauthor*, \Citeauthor*, \parencite*, \citetitle*. + */ +char const * const known_biblatex_commands[] = { "cite", "Cite", "textcite", "Textcite", +"parencite", "Parencite", "citeauthor", "Citeauthor", "citeyear", "smartcite", "Smartcite", + "footcite", "Footcite", "autocite", "Autocite", "citetitle", "fullcite", "footfullcite", +"supercite", "cites", "Cites", "textcites", "Textcites", "parencites", "Parencites", +"smartcites", "Smartcites", "autocites", "Autocites", 0 }; + +// Whether we need to insert a bibtex inset in a comment +bool need_commentbib = false; + /// LaTeX names for quotes char const * const known_quotes[] = { "dq", "guillemotleft", "flqq", "og", "guillemotright", "frqq", "fg", "glq", "glqq", "textquoteleft", "grq", "grqq", "quotedblbase", "textquotedblleft", "quotesinglbase", "textquoteright", "flq", -"guilsinglleft", "frq", "guilsinglright", 0}; +"guilsinglleft", "frq", "guilsinglright", "textquotedblright", "textquotesingle", +"textquotedbl", 0}; /// the same as known_quotes with .lyx names -char const * const known_coded_quotes[] = { "prd", "ard", "ard", "ard", -"ald", "ald", "ald", "gls", "gld", "els", "els", "grd", -"gld", "grd", "gls", "ers", "fls", -"fls", "frs", "frs", 0}; +char const * const known_coded_quotes[] = { "qrd", "ard", "ard", "ard", +"ald", "ald", "ald", "gls", "gld", "els", "els", "eld", +"gld", "eld", "gls", "ers", "ars", +"ars", "als", "als", "erd", "qrs", "qrd", 0}; /// LaTeX names for font sizes char const * const known_sizes[] = { "tiny", "scriptsize", "footnotesize", @@ -330,6 +353,10 @@ bool minted_float_has_caption = false; // The caption for non-floating minted listings string minted_nonfloat_caption = ""; +// Characters that have to be escaped by \\ in LaTeX +char const * const known_escaped_chars[] = { + "&", "_", "$", "%", "#", "^", "{", "}", 0}; + /// splits "x=z, y=b" into a map and an ordered keyword vector void split_map(string const & s, map & res, vector & keys) @@ -429,6 +456,78 @@ bool translate_len(string const & length, string & valstring, string & unit) return true; } + +/// If we have ambiguous quotation marks, make a smart guess +/// based on main quote style +string guessQuoteStyle(string in, bool const opening) +{ + string res = in; + if (prefixIs(in, "qr")) {// straight quote + if (!opening) + res = subst(res, "r", "l"); + } else if (in == "eld") {// `` + if (preamble.quotesStyle() == "german") + res = "grd"; + else if (preamble.quotesStyle() == "british") + res = "bls"; + else if (preamble.quotesStyle() == "french") + res = "fls"; + else if (preamble.quotesStyle() == "russian") + res = "rrs"; + } else if (in == "erd") {// '' + if (preamble.quotesStyle() == "polish") + res = "prd"; + else if (preamble.quotesStyle() == "british") + res = "brs"; + else if (preamble.quotesStyle() == "french") + res = "frs"; + else if (preamble.quotesStyle() == "swedish") + res = opening ? "sld" : "srd"; + } else if (in == "els") {// ` + if (preamble.quotesStyle() == "german") + res = "grs"; + else if (preamble.quotesStyle() == "british") + res = "bld"; + } else if (in == "ers") {// ' + if (preamble.quotesStyle() == "polish") + res = "prs"; + else if (preamble.quotesStyle() == "british") + res = "brd"; + else if (preamble.quotesStyle() == "swedish") + res = opening ? "sls" : "srs"; + } else if (in == "ard") {// >> + if (preamble.quotesStyle() == "swiss") + res = "cld"; + else if (preamble.quotesStyle() == "french") + res = "fld"; + else if (preamble.quotesStyle() == "russian") + res = "rld"; + } else if (in == "ald") {// << + if (preamble.quotesStyle() == "swiss") + res = "crd"; + else if (preamble.quotesStyle() == "french") + res = "frd"; + else if (preamble.quotesStyle() == "russian") + res = "rrd"; + } else if (in == "ars") {// > + if (preamble.quotesStyle() == "swiss") + res = "cls"; + } else if (in == "als") {// < + if (preamble.quotesStyle() == "swiss") + res = "crs"; + } else if (in == "gld") {// ,, + if (preamble.quotesStyle() == "polish") + res = "pld"; + else if (preamble.quotesStyle() == "russian") + res = "rls"; + } else if (in == "gls") {// , + if (preamble.quotesStyle() == "polish") + res = "pls"; + } + return res; +} + + } // namespace @@ -469,22 +568,6 @@ void translate_box_len(string const & length, string & value, string & unit, str } -/*! - * Find a file with basename \p name in path \p path and an extension - * in \p extensions. - */ -string find_file(string const & name, string const & path, - char const * const * extensions) -{ - for (char const * const * what = extensions; *what; ++what) { - string const trial = addExtension(name, *what); - if (makeAbsPath(trial, path).exists()) - return trial; - } - return string(); -} - - void begin_inset(ostream & os, string const & name) { os << "\n\\begin_inset " << name; @@ -521,8 +604,9 @@ bool skip_braces(Parser & p) /// replace LaTeX commands in \p s from the unicodesymbols file with their /// unicode points -docstring convert_unicodesymbols(docstring s) +pair convert_unicodesymbols(docstring s) { + bool res = true; odocstringstream os; for (size_t i = 0; i < s.size();) { if (s[i] != '\\') { @@ -543,24 +627,42 @@ docstring convert_unicodesymbols(docstring s) s = rem; if (s.empty() || s[0] != '\\') i = 0; - else + else { + res = false; + for (auto const & c : known_escaped_chars) + if (c != 0 && prefixIs(s, from_ascii("\\") + c)) + res = true; i = 1; + } } - return os.str(); + return make_pair(res, os.str()); } /// try to convert \p s to a valid InsetCommand argument -string convert_command_inset_arg(string s) +/// return whether this succeeded. If not, these command insets +/// get the "literate" flag. +pair convert_latexed_command_inset_arg(string s) { - if (isAscii(s)) + bool success = false; + if (isAscii(s)) { // since we don't know the input encoding we can't use from_utf8 - s = to_utf8(convert_unicodesymbols(from_ascii(s))); + pair res = convert_unicodesymbols(from_ascii(s)); + success = res.first; + s = to_utf8(res.second); + } + // LyX cannot handle newlines in a latex command + return make_pair(success, subst(s, "\n", " ")); +} + +/// try to convert \p s to a valid InsetCommand argument +/// without trying to recode macros. +string convert_literate_command_inset_arg(string s) +{ // LyX cannot handle newlines in a latex command return subst(s, "\n", " "); } - void output_ert(ostream & os, string const & s, Context & context) { context.check_layout(os); @@ -602,24 +704,27 @@ void output_comment(Parser & p, ostream & os, string const & s, } -Layout const * findLayout(TextClass const & textclass, string const & name, bool command) +Layout const * findLayout(TextClass const & textclass, string const & name, bool command, + string const & latexparam = string()) { - Layout const * layout = findLayoutWithoutModule(textclass, name, command); + Layout const * layout = findLayoutWithoutModule(textclass, name, command, latexparam); if (layout) return layout; if (checkModule(name, command)) - return findLayoutWithoutModule(textclass, name, command); + return findLayoutWithoutModule(textclass, name, command, latexparam); return layout; } -InsetLayout const * findInsetLayout(TextClass const & textclass, string const & name, bool command) +InsetLayout const * findInsetLayout(TextClass const & textclass, string const & name, bool command, + string const & latexparam = string()) { - InsetLayout const * insetlayout = findInsetLayoutWithoutModule(textclass, name, command); + InsetLayout const * insetlayout = + findInsetLayoutWithoutModule(textclass, name, command, latexparam); if (insetlayout) return insetlayout; if (checkModule(name, command)) - return findInsetLayoutWithoutModule(textclass, name, command); + return findInsetLayoutWithoutModule(textclass, name, command, latexparam); return insetlayout; } @@ -654,14 +759,16 @@ void skip_spaces_braces(Parser & p, bool keepws = false) } -void output_arguments(ostream & os, Parser & p, bool outer, bool need_layout, bool post, +void output_arguments(ostream & os, Parser & p, bool outer, bool need_layout, string const prefix, Context & context, Layout::LaTeXArgMap const & latexargs) { - if (need_layout) { - context.check_layout(os); - need_layout = false; - } else - need_layout = true; + if (context.layout->latextype != LATEX_ITEM_ENVIRONMENT || !prefix.empty()) { + if (need_layout) { + context.check_layout(os); + need_layout = false; + } else + need_layout = true; + } int i = 0; Layout::LaTeXArgMap::const_iterator lait = latexargs.begin(); Layout::LaTeXArgMap::const_iterator const laend = latexargs.end(); @@ -671,31 +778,50 @@ void output_arguments(ostream & os, Parser & p, bool outer, bool need_layout, bo if (lait->second.mandatory) { if (p.next_token().cat() != catBegin) break; - p.get_token(); // eat '{' + string ldelim = to_utf8(lait->second.ldelim); + string rdelim = to_utf8(lait->second.rdelim); + if (ldelim.empty()) + ldelim = "{"; + if (rdelim.empty()) + rdelim = "}"; + p.get_token(); // eat ldelim + if (ldelim.size() > 1) + p.get_token(); // eat ldelim if (need_layout) { context.check_layout(os); need_layout = false; } begin_inset(os, "Argument "); - if (post) - os << "post:"; + if (!prefix.empty()) + os << prefix << ':'; os << i << "\nstatus collapsed\n\n"; - parse_text_in_inset(p, os, FLAG_BRACE_LAST, outer, context); + parse_text_in_inset(p, os, FLAG_RDELIM, outer, context, 0, rdelim); end_inset(os); } else { - if (p.next_token().cat() == catEscape || - p.next_token().character() != '[') + string ldelim = to_utf8(lait->second.ldelim); + string rdelim = to_utf8(lait->second.rdelim); + if (ldelim.empty()) + ldelim = "["; + if (rdelim.empty()) + rdelim = "]"; + string tok = p.next_token().asInput(); + // we only support delimiters with max 2 chars for now. + if (ldelim.size() > 1) + tok += p.next_next_token().asInput(); + if (p.next_token().cat() == catEscape || tok != ldelim) continue; - p.get_token(); // eat '[' + p.get_token(); // eat ldelim + if (ldelim.size() > 1) + p.get_token(); // eat ldelim if (need_layout) { context.check_layout(os); need_layout = false; } begin_inset(os, "Argument "); - if (post) - os << "post:"; + if (!prefix.empty()) + os << prefix << ':'; os << i << "\nstatus collapsed\n\n"; - parse_text_in_inset(p, os, FLAG_BRACK_LAST, outer, context); + parse_text_in_inset(p, os, FLAG_RDELIM, outer, context, 0, rdelim); end_inset(os); } eat_whitespace(p, os, context, false); @@ -726,10 +852,17 @@ void output_command_layout(ostream & os, Parser & p, bool outer, context.need_end_deeper = true; } context.check_deeper(os); - output_arguments(os, p, outer, true, false, context, + output_arguments(os, p, outer, true, string(), context, context.layout->latexargs()); + // If we have a latex param, we eat it here. + if (!parent_context.latexparam.empty()) { + ostringstream oss; + Context dummy(true, parent_context.textclass); + parse_text(p, oss, FLAG_RDELIM, outer, dummy, + string(1, parent_context.latexparam.back())); + } parse_text(p, os, FLAG_ITEM, outer, context); - output_arguments(os, p, outer, false, true, context, + output_arguments(os, p, outer, false, "post", context, context.layout->postcommandargs()); context.check_end_layout(os); if (parent_context.deeper_paragraph) { @@ -1350,7 +1483,7 @@ void parse_listings(Parser & p, ostream & os, Context & parent_context, os << "inline true\n"; else os << "inline false\n"; - os << "status collapsed\n"; + os << "status open\n"; Context context(true, parent_context.textclass); context.layout = &parent_context.textclass.plainLayout(); if (use_minted && prefixIs(minted_nonfloat_caption, "[t]")) { @@ -1700,10 +1833,10 @@ void parse_environment(Parser & p, ostream & os, bool outer, // things like comments are completely wrong. string const s = p.plainEnvironment("CJK"); for (string::const_iterator it = s.begin(), et = s.end(); it != et; ++it) { - if (*it == '\\') - output_ert_inset(os, "\\", parent_context); - else if (*it == '$') - output_ert_inset(os, "$", parent_context); + string snip; + snip += *it; + if (snip == "\\" || is_known(snip, known_escaped_chars)) + output_ert_inset(os, snip, parent_context); else if (*it == '\n' && it + 1 != et && s.begin() + 1 != it) os << "\n "; else @@ -1868,6 +2001,7 @@ void parse_environment(Parser & p, ostream & os, bool outer, parse_text_snippet(p, FLAG_ITEM, false, parent_context); minted_nonfloat_caption = "[b]" + caption; + eat_whitespace(p, os, parent_context, true); } } p.popPosition(); @@ -1883,12 +2017,18 @@ void parse_environment(Parser & p, ostream & os, bool outer, // Alignment and spacing settings // FIXME (bug xxxx): These settings can span multiple paragraphs and // therefore are totally broken! - // Note that \centering, raggedright, and raggedleft cannot be handled, as + // Note that \centering, \raggedright, and \raggedleft cannot be handled, as // they are commands not environments. They are furthermore switches that // can be ended by another switches, but also by commands like \footnote or // \parbox. So the only safe way is to leave them untouched. + // However, we support the pseudo-environments + // \begin{centering} ... \end{centering} + // \begin{raggedright} ... \end{raggedright} + // \begin{raggedleft} ... \end{raggedleft} + // since they are used by LyX in floats (for spacing reasons) else if (name == "center" || name == "centering" || - name == "flushleft" || name == "flushright" || + name == "flushleft" || name == "raggedright" || + name == "flushright" || name == "raggedleft" || name == "singlespace" || name == "onehalfspace" || name == "doublespace" || name == "spacing") { eat_whitespace(p, os, parent_context, false); @@ -1897,9 +2037,9 @@ void parse_environment(Parser & p, ostream & os, bool outer, parent_context.check_end_layout(os); parent_context.new_paragraph(os); } - if (name == "flushleft") + if (name == "flushleft" || name == "raggedright") parent_context.add_extra_stuff("\\align left\n"); - else if (name == "flushright") + else if (name == "flushright" || name == "raggedleft") parent_context.add_extra_stuff("\\align right\n"); else if (name == "center" || name == "centering") parent_context.add_extra_stuff("\\align center\n"); @@ -1960,16 +2100,25 @@ void parse_environment(Parser & p, ostream & os, bool outer, break; } context.check_deeper(os); + if (newlayout->keepempty) { + // We need to start a new paragraph + // even if it is empty. + context.new_paragraph(os); + context.check_layout(os); + } // handle known optional and required arguments - // Unfortunately LyX can't handle arguments of list arguments (bug 7468): - // It is impossible to place anything after the environment name, - // but before the first \\item. if (context.layout->latextype == LATEX_ENVIRONMENT) - output_arguments(os, p, outer, false, false, context, + output_arguments(os, p, outer, false, string(), context, context.layout->latexargs()); + else if (context.layout->latextype == LATEX_ITEM_ENVIRONMENT) { + ostringstream oss; + output_arguments(oss, p, outer, false, string(), context, + context.layout->latexargs()); + context.list_extra_stuff = oss.str(); + } parse_text(p, os, FLAG_END, outer, context); if (context.layout->latextype == LATEX_ENVIRONMENT) - output_arguments(os, p, outer, false, true, context, + output_arguments(os, p, outer, false, "post", context, context.layout->postcommandargs()); context.check_end_layout(os); if (parent_context.deeper_paragraph) { @@ -2132,113 +2281,23 @@ void parse_text_attributes(Parser & p, ostream & os, unsigned flags, bool outer, /// get the arguments of a natbib or jurabib citation command void get_cite_arguments(Parser & p, bool natbibOrder, - string & before, string & after) + string & before, string & after, bool const qualified = false) { // We need to distinguish "" and "[]", so we can't use p.getOpt(). // text before the citation before.clear(); // text after the citation - after = p.getFullOpt(); + after = qualified ? p.getFullOpt(false, '(', ')') : p.getFullOpt(); if (!after.empty()) { - before = p.getFullOpt(); + before = qualified ? p.getFullOpt(false, '(', ')') : p.getFullOpt(); if (natbibOrder && !before.empty()) swap(before, after); } } -/// Convert filenames with TeX macros and/or quotes to something LyX -/// can understand -string const normalize_filename(string const & name) -{ - Parser p(name); - ostringstream os; - while (p.good()) { - Token const & t = p.get_token(); - if (t.cat() != catEscape) - os << t.asInput(); - else if (t.cs() == "lyxdot") { - // This is used by LyX for simple dots in relative - // names - os << '.'; - p.skip_spaces(); - } else if (t.cs() == "space") { - os << ' '; - p.skip_spaces(); - } else if (t.cs() == "string") { - // Convert \string" to " and \string~ to ~ - Token const & n = p.next_token(); - if (n.asInput() != "\"" && n.asInput() != "~") - os << t.asInput(); - } else - os << t.asInput(); - } - // Strip quotes. This is a bit complicated (see latex_path()). - string full = os.str(); - if (!full.empty() && full[0] == '"') { - string base = removeExtension(full); - string ext = getExtension(full); - if (!base.empty() && base[base.length()-1] == '"') - // "a b" - // "a b".tex - return addExtension(trim(base, "\""), ext); - if (full[full.length()-1] == '"') - // "a b.c" - // "a b.c".tex - return trim(full, "\""); - } - return full; -} - - -/// Convert \p name from TeX convention (relative to master file) to LyX -/// convention (relative to .lyx file) if it is relative -void fix_child_filename(string & name) -{ - string const absMasterTeX = getMasterFilePath(true); - bool const isabs = FileName::isAbsolute(name); - // convert from "relative to .tex master" to absolute original path - if (!isabs) - name = makeAbsPath(name, absMasterTeX).absFileName(); - bool copyfile = copyFiles(); - string const absParentLyX = getParentFilePath(false); - string abs = name; - if (copyfile) { - // convert from absolute original path to "relative to master file" - string const rel = to_utf8(makeRelPath(from_utf8(name), - from_utf8(absMasterTeX))); - // re-interpret "relative to .tex file" as "relative to .lyx file" - // (is different if the master .lyx file resides in a - // different path than the master .tex file) - string const absMasterLyX = getMasterFilePath(false); - abs = makeAbsPath(rel, absMasterLyX).absFileName(); - // Do not copy if the new path is impossible to create. Example: - // absMasterTeX = "/foo/bar/" - // absMasterLyX = "/bar/" - // name = "/baz.eps" => new absolute name would be "/../baz.eps" - if (contains(name, "/../")) - copyfile = false; - } - if (copyfile) { - if (isabs) - name = abs; - else { - // convert from absolute original path to - // "relative to .lyx file" - name = to_utf8(makeRelPath(from_utf8(abs), - from_utf8(absParentLyX))); - } - } - else if (!isabs) { - // convert from absolute original path to "relative to .lyx file" - name = to_utf8(makeRelPath(from_utf8(name), - from_utf8(absParentLyX))); - } -} - - void copy_file(FileName const & src, string dstname) { if (!copyFiles()) @@ -2463,8 +2522,114 @@ void registerExternalTemplatePackages(string const & name) } // anonymous namespace +/*! + * Find a file with basename \p name in path \p path and an extension + * in \p extensions. + */ +string find_file(string const & name, string const & path, + char const * const * extensions) +{ + for (char const * const * what = extensions; *what; ++what) { + string const trial = addExtension(name, *what); + if (makeAbsPath(trial, path).exists()) + return trial; + } + return string(); +} + + +/// Convert filenames with TeX macros and/or quotes to something LyX +/// can understand +string const normalize_filename(string const & name) +{ + Parser p(name); + ostringstream os; + while (p.good()) { + Token const & t = p.get_token(); + if (t.cat() != catEscape) + os << t.asInput(); + else if (t.cs() == "lyxdot") { + // This is used by LyX for simple dots in relative + // names + os << '.'; + p.skip_spaces(); + } else if (t.cs() == "space") { + os << ' '; + p.skip_spaces(); + } else if (t.cs() == "string") { + // Convert \string" to " and \string~ to ~ + Token const & n = p.next_token(); + if (n.asInput() != "\"" && n.asInput() != "~") + os << t.asInput(); + } else + os << t.asInput(); + } + // Strip quotes. This is a bit complicated (see latex_path()). + string full = os.str(); + if (!full.empty() && full[0] == '"') { + string base = removeExtension(full); + string ext = getExtension(full); + if (!base.empty() && base[base.length()-1] == '"') + // "a b" + // "a b".tex + return addExtension(trim(base, "\""), ext); + if (full[full.length()-1] == '"') + // "a b.c" + // "a b.c".tex + return trim(full, "\""); + } + return full; +} + + +/// Convert \p name from TeX convention (relative to master file) to LyX +/// convention (relative to .lyx file) if it is relative +void fix_child_filename(string & name) +{ + string const absMasterTeX = getMasterFilePath(true); + bool const isabs = FileName::isAbsolute(name); + // convert from "relative to .tex master" to absolute original path + if (!isabs) + name = makeAbsPath(name, absMasterTeX).absFileName(); + bool copyfile = copyFiles(); + string const absParentLyX = getParentFilePath(false); + string abs = name; + if (copyfile) { + // convert from absolute original path to "relative to master file" + string const rel = to_utf8(makeRelPath(from_utf8(name), + from_utf8(absMasterTeX))); + // re-interpret "relative to .tex file" as "relative to .lyx file" + // (is different if the master .lyx file resides in a + // different path than the master .tex file) + string const absMasterLyX = getMasterFilePath(false); + abs = makeAbsPath(rel, absMasterLyX).absFileName(); + // Do not copy if the new path is impossible to create. Example: + // absMasterTeX = "/foo/bar/" + // absMasterLyX = "/bar/" + // name = "/baz.eps" => new absolute name would be "/../baz.eps" + if (contains(name, "/../")) + copyfile = false; + } + if (copyfile) { + if (isabs) + name = abs; + else { + // convert from absolute original path to + // "relative to .lyx file" + name = to_utf8(makeRelPath(from_utf8(abs), + from_utf8(absParentLyX))); + } + } + else if (!isabs) { + // convert from absolute original path to "relative to .lyx file" + name = to_utf8(makeRelPath(from_utf8(name), + from_utf8(absParentLyX))); + } +} + + void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, - Context & context) + Context & context, string const rdelim) { Layout const * newlayout = 0; InsetLayout const * newinsetlayout = 0; @@ -2478,6 +2643,11 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, string bibliographystyle; bool const use_natbib = isProvided("natbib"); bool const use_jurabib = isProvided("jurabib"); + bool const use_biblatex = isProvided("biblatex") + && preamble.citeEngine() != "biblatex-natbib"; + bool const use_biblatex_natbib = isProvided("biblatex-natbib") + || (isProvided("biblatex") && preamble.citeEngine() == "biblatex-natbib"); + need_commentbib = use_biblatex || use_biblatex_natbib; string last_env; // it is impossible to determine the correct encoding for non-CJK Japanese. @@ -2502,7 +2672,14 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, is_nonCJKJapanese = false; } + bool have_cycled = false; while (p.good()) { + // Leave here only after at least one cycle + if (have_cycled && flags & FLAG_LEAVE) { + flags &= ~FLAG_LEAVE; + break; + } + Token const & t = p.get_token(); #ifdef FILEDEBUG debugToken(cerr, t, flags); @@ -2529,6 +2706,16 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, return; if (t.cat() == catEnd && (flags & FLAG_BRACE_LAST)) return; + string tok = t.asInput(); + // we only support delimiters with max 2 chars for now. + if (rdelim.size() > 1) + tok += p.next_token().asInput(); + if (t.cat() != catEscape && !rdelim.empty() + && tok == rdelim && (flags & FLAG_RDELIM)) { + if (rdelim.size() > 1) + p.get_token(); // eat rdelim + return; + } // If there is anything between \end{env} and \begin{env} we // don't need to output a separator. @@ -2539,6 +2726,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, // // cat codes // + have_cycled = true; bool const starred = p.next_token().asInput() == "*"; string const starredname(starred ? (t.cs() + '*') : t.cs()); if (t.cat() == catMath) { @@ -2567,42 +2755,51 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, // output, but looks ugly in LyX. eat_whitespace(p, os, context, false); } + continue; } - else if (t.cat() == catSuper || t.cat() == catSub) + if (t.cat() == catSuper || t.cat() == catSub) { cerr << "catcode " << t << " illegal in text mode\n"; + continue; + } - // Basic support for english quotes. This should be - // extended to other quotes, but is not so easy (a - // left english quote is the same as a right german - // quote...) - else if (t.asInput() == "`" && p.next_token().asInput() == "`") { + // Basic support for quotes. We try to disambiguate + // quotes from the context (e.g., a left english quote is + // the same as a right german quote...). + // Try to make a smart guess about the side + Token const prev = p.prev_token(); + bool const opening = (prev.cat() != catSpace && prev.character() != 0 + && prev.character() != '\n' && prev.character() != '~'); + if (t.asInput() == "`" && p.next_token().asInput() == "`") { context.check_layout(os); begin_inset(os, "Quotes "); - os << "eld"; + os << guessQuoteStyle("eld", opening); end_inset(os); p.get_token(); skip_braces(p); + continue; } - else if (t.asInput() == "'" && p.next_token().asInput() == "'") { + if (t.asInput() == "'" && p.next_token().asInput() == "'") { context.check_layout(os); begin_inset(os, "Quotes "); - os << "erd"; + os << guessQuoteStyle("erd", opening); end_inset(os); p.get_token(); skip_braces(p); + continue; } - else if (t.asInput() == ">" && p.next_token().asInput() == ">") { + if (t.asInput() == ">" && p.next_token().asInput() == ">") { context.check_layout(os); begin_inset(os, "Quotes "); - os << "ald"; + os << guessQuoteStyle("ald", opening); end_inset(os); p.get_token(); skip_braces(p); + continue; } - else if (t.asInput() == "<" + if (t.asInput() == "<" && p.next_token().asInput() == "<") { bool has_chunk = false; if (noweb_mode) { @@ -2616,19 +2813,47 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, if (!has_chunk) { context.check_layout(os); begin_inset(os, "Quotes "); - //FIXME: this is a right danish quote; - // why not a left french quote? - os << "ard"; + os << guessQuoteStyle("ard", opening); end_inset(os); p.get_token(); skip_braces(p); } + continue; } - else if (t.cat() == catSpace || (t.cat() == catNewline && ! p.isParagraph())) + if (t.cat() == catSpace || (t.cat() == catNewline && ! p.isParagraph())) { check_space(p, os, context); + continue; + } - else if (t.character() == '[' && noweb_mode && + // babel shorthands (also used by polyglossia) + // Since these can have different meanings for different languages + // we import them as ERT (but they must be put in ERT to get output + // verbatim). + if (t.asInput() == "\"") { + string s = "\""; + // These are known pairs. We put them together in + // one ERT inset. In other cases (such as "a), only + // the quotation mark is ERTed. + if (p.next_token().asInput() == "\"" + || p.next_token().asInput() == "|" + || p.next_token().asInput() == "-" + || p.next_token().asInput() == "~" + || p.next_token().asInput() == "=" + || p.next_token().asInput() == "/" + || p.next_token().asInput() == "~" + || p.next_token().asInput() == "'" + || p.next_token().asInput() == "`" + || p.next_token().asInput() == "<" + || p.next_token().asInput() == ">") { + s += p.next_token().asInput(); + p.get_token(); + } + output_ert_inset(os, s, context); + continue; + } + + if (t.character() == '[' && noweb_mode && p.next_token().character() == '[') { // These can contain underscores p.putback(); @@ -2639,14 +2864,16 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, cerr << "Warning: Inserting missing ']' in '" << s << "'." << endl; output_ert_inset(os, s, context); + continue; } - else if (t.cat() == catLetter) { + if (t.cat() == catLetter) { context.check_layout(os); os << t.cs(); + continue; } - else if (t.cat() == catOther || + if (t.cat() == catOther || t.cat() == catAlign || t.cat() == catParameter) { context.check_layout(os); @@ -2665,9 +2892,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, } else // This translates "&" to "\\&" which may be wrong... os << t.cs(); + continue; } - else if (p.isParagraph()) { + if (p.isParagraph()) { // In minted floating listings we will collect // everything into the caption, where multiple // paragraphs are forbidden. @@ -2679,9 +2907,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, } else os << ' '; eat_whitespace(p, os, context, true); + continue; } - else if (t.cat() == catActive) { + if (t.cat() == catActive) { context.check_layout(os); if (t.character() == '~') { if (context.layout->free_spacing) @@ -2692,9 +2921,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, } } else os << t.cs(); + continue; } - else if (t.cat() == catBegin) { + if (t.cat() == catBegin) { Token const next = p.next_token(); Token const end = p.next_next_token(); if (next.cat() == catEnd) { @@ -2709,8 +2939,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, is_known(next.cs(), known_quotes) && end.cat() == catEnd) { // Something like {\textquoteright} (e.g. - // from writer2latex). LyX writes - // \textquoteright{}, so we may skip the + // from writer2latex). We may skip the // braces here for better readability. parse_text_snippet(p, os, FLAG_BRACE_LAST, outer, context); @@ -2818,24 +3047,28 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, output_ert_inset(os, "}", context); } } + continue; } - else if (t.cat() == catEnd) { + if (t.cat() == catEnd) { if (flags & FLAG_BRACE_LAST) { return; } cerr << "stray '}' in text\n"; output_ert_inset(os, "}", context); + continue; } - else if (t.cat() == catComment) + if (t.cat() == catComment) { parse_comment(p, os, t, context); + continue; + } // // control sequences // - else if (t.cs() == "(" || t.cs() == "[") { + if (t.cs() == "(" || t.cs() == "[") { bool const simple = t.cs() == "("; context.check_layout(os); begin_inset(os, "Formula"); @@ -2849,13 +3082,16 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, // output, but looks ugly in LyX. eat_whitespace(p, os, context, false); } + continue; } - else if (t.cs() == "begin") + if (t.cs() == "begin") { parse_environment(p, os, outer, last_env, context); + continue; + } - else if (t.cs() == "end") { + if (t.cs() == "end") { if (flags & FLAG_END) { // eat environment name string const name = p.getArg('{', '}'); @@ -2865,12 +3101,13 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, return; } p.error("found 'end' unexpectedly"); + continue; } - else if (t.cs() == "item") { + // "item" by default, but could be something else + if (t.cs() == context.layout->itemcommand()) { string s; - bool const optarg = p.hasOpt(); - if (optarg) { + if (context.layout->labeltype == LABEL_MANUAL) { // FIXME: This swallows comments, but we cannot use // eat_whitespace() since we must not output // anything before the item. @@ -2884,26 +3121,19 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, // An item in an unknown list-like environment // FIXME: Do this in check_layout()! context.has_item = false; - if (optarg) - output_ert_inset(os, "\\item", context); - else - output_ert_inset(os, "\\item ", context); + string item = "\\" + context.layout->itemcommand(); + if (!p.hasOpt()) + item += " "; + output_ert_inset(os, item, context); } - if (optarg) { - if (context.layout->labeltype != LABEL_MANUAL) { - // handle option of itemize item - begin_inset(os, "Argument item:1\n"); - os << "status open\n"; - os << "\n\\begin_layout Plain Layout\n"; - Parser p2(s + ']'); - os << parse_text_snippet(p2, - FLAG_BRACK_LAST, outer, context); - // we must not use context.check_end_layout(os) - // because that would close the outer itemize layout - os << "\n\\end_layout\n"; - end_inset(os); - eat_whitespace(p, os, context, false); - } else if (!s.empty()) { + if (context.layout->labeltype != LABEL_MANUAL) + output_arguments(os, p, outer, false, "item", context, + context.layout->itemargs()); + if (!context.list_extra_stuff.empty()) { + os << context.list_extra_stuff; + context.list_extra_stuff.clear(); + } + else if (!s.empty()) { // LyX adds braces around the argument, // so we need to remove them here. if (s.size() > 2 && s[0] == '{' && @@ -2925,30 +3155,28 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, os << ' '; eat_whitespace(p, os, context, false); } - } + continue; } - else if (t.cs() == "bibitem") { + if (t.cs() == "bibitem") { context.set_item(); context.check_layout(os); eat_whitespace(p, os, context, false); - string label = convert_command_inset_arg(p.verbatimOption()); - string key = convert_command_inset_arg(p.verbatim_item()); - if (contains(label, '\\') || contains(key, '\\')) { - // LyX can't handle LaTeX commands in labels or keys - output_ert_inset(os, t.asInput() + '[' + label + - "]{" + p.verbatim_item() + '}', - context); - } else { - begin_command_inset(os, "bibitem", "bibitem"); - os << "label \"" << label << "\"\n" - << "key \"" << key << "\"\n" - << "literal \"true\"\n"; - end_inset(os); - } + string label = p.verbatimOption(); + pair lbl = convert_latexed_command_inset_arg(label); + bool const literal = !lbl.first; + label = literal ? subst(label, "\n", " ") : lbl.second; + string lit = literal ? "\"true\"" : "\"false\""; + string key = convert_literate_command_inset_arg(p.verbatim_item()); + begin_command_inset(os, "bibitem", "bibitem"); + os << "label \"" << label << "\"\n" + << "key \"" << key << "\"\n" + << "literal " << lit << "\n"; + end_inset(os); + continue; } - else if (is_macro(p)) { + if (is_macro(p)) { // catch the case of \def\inputGnumericTable bool macro = true; if (t.cs() == "def") { @@ -3001,14 +3229,16 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, } if (macro) parse_macro(p, os, context); + continue; } - else if (t.cs() == "noindent") { + if (t.cs() == "noindent") { p.skip_spaces(); context.add_par_extra_stuff("\\noindent\n"); + continue; } - else if (t.cs() == "appendix") { + if (t.cs() == "appendix") { context.add_par_extra_stuff("\\start_of_appendix\n"); // We need to start a new paragraph. Otherwise the // appendix in 'bla\appendix\chapter{' would start @@ -3029,10 +3259,11 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, // empty paragraph, but that does not hurt, because // whitespace does not matter here. eat_whitespace(p, os, context, true); + continue; } // Must catch empty dates before findLayout is called below - else if (t.cs() == "date") { + if (t.cs() == "date") { eat_whitespace(p, os, context, false); p.pushPosition(); string const date = p.verbatim_item(); @@ -3061,11 +3292,32 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, "\\date{" + p.verbatim_item() + '}', context); } + continue; } + // Before we look for the layout name with star and alone below, we check the layouts including + // the LateXParam, which might be one or several options or a star. + // The single '=' is meant here. + if (context.new_layout_allowed && + (newlayout = findLayout(context.textclass, t.cs(), true, p.getCommandLatexParam()))) { + // store the latexparam here. This is eaten in output_command_layout + context.latexparam = newlayout->latexparam(); + // write the layout + output_command_layout(os, p, outer, context, newlayout); + context.latexparam.clear(); + p.skip_spaces(); + if (!preamble.titleLayoutFound()) + preamble.titleLayoutFound(newlayout->intitle); + set const & req = newlayout->requires(); + for (set::const_iterator it = req.begin(); it != req.end(); ++it) + preamble.registerAutomaticallyLoadedPackage(*it); + continue; + } + + // Starred section headings // Must attempt to parse "Section*" before "Section". - else if ((p.next_token().asInput() == "*") && + if ((p.next_token().asInput() == "*") && context.new_layout_allowed && (newlayout = findLayout(context.textclass, t.cs() + '*', true))) { // write the layout @@ -3077,10 +3329,11 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, set const & req = newlayout->requires(); for (set::const_iterator it = req.begin(); it != req.end(); ++it) preamble.registerAutomaticallyLoadedPackage(*it); + continue; } // Section headings and the like - else if (context.new_layout_allowed && + if (context.new_layout_allowed && (newlayout = findLayout(context.textclass, t.cs(), true))) { // write the layout output_command_layout(os, p, outer, context, newlayout); @@ -3090,12 +3343,12 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, set const & req = newlayout->requires(); for (set::const_iterator it = req.begin(); it != req.end(); ++it) preamble.registerAutomaticallyLoadedPackage(*it); + continue; } - else if (t.cs() == "subfloat") { + if (t.cs() == "subfloat") { // the syntax is \subfloat[list entry][sub caption]{content} // if it is a table of figure depends on the surrounding float - // FIXME: second optional argument is not parsed p.skip_spaces(); // do nothing if there is no outer float if (!float_type.empty()) { @@ -3114,6 +3367,12 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, caption = parse_text_snippet(p, FLAG_BRACK_LAST, outer, context); has_caption = true; } + // In case we have two optional args, the second is the caption. + if (p.next_token().cat() != catEscape && + p.next_token().character() == '[') { + p.get_token(); // eat '[' + caption = parse_text_snippet(p, FLAG_BRACK_LAST, outer, context); + } // the content parse_text_in_inset(p, os, FLAG_ITEM, outer, context); // the caption comes always as the last @@ -3127,35 +3386,43 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, newcontext.check_layout(os); os << caption << "\n"; newcontext.check_end_layout(os); - // We don't need really a new paragraph, but - // we must make sure that the next item gets a \begin_layout. - //newcontext.new_paragraph(os); end_inset(os); p.skip_spaces(); + // close the layout we opened + os << "\n\\end_layout"; } - // We don't need really a new paragraph, but - // we must make sure that the next item gets a \begin_layout. - if (has_caption) - context.new_paragraph(os); end_inset(os); p.skip_spaces(); - context.check_end_layout(os); - // close the layout we opened - if (has_caption) - os << "\n\\end_layout\n"; } else { // if the float type is not supported or there is no surrounding float // output it as ERT + string opt_arg1; + string opt_arg2; if (p.hasOpt()) { - string opt_arg = convert_command_inset_arg(p.getArg('[', ']')); - output_ert_inset(os, t.asInput() + '[' + opt_arg + - "]{" + p.verbatim_item() + '}', context); - } else - output_ert_inset(os, t.asInput() + "{" + p.verbatim_item() + '}', context); + opt_arg1 = convert_literate_command_inset_arg(p.getFullOpt()); + if (p.hasOpt()) + opt_arg2 = convert_literate_command_inset_arg(p.getFullOpt()); + } + output_ert_inset(os, t.asInput() + opt_arg1 + opt_arg2 + + "{" + p.verbatim_item() + '}', context); } + continue; } - else if (t.cs() == "includegraphics") { + if (t.cs() == "xymatrix") { + // we must open a new math because LyX's xy support is in math + context.check_layout(os); + begin_inset(os, "Formula "); + os << '$'; + os << "\\" << t.cs() << '{'; + parse_math(p, os, FLAG_ITEM, MATH_MODE); + os << '}' << '$'; + end_inset(os); + preamble.registerAutomaticallyLoadedPackage("xy"); + continue; + } + + if (t.cs() == "includegraphics") { bool const clip = p.next_token().asInput() == "*"; if (clip) p.get_token(); @@ -3315,9 +3582,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, // Check whether some option was given twice. end_inset(os); preamble.registerAutomaticallyLoadedPackage("graphicx"); + continue; } - else if (t.cs() == "footnote" || + if (t.cs() == "footnote" || (t.cs() == "thanks" && context.layout->intitle)) { p.skip_spaces(); context.check_layout(os); @@ -3325,24 +3593,27 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, os << "status collapsed\n\n"; parse_text_in_inset(p, os, FLAG_ITEM, false, context); end_inset(os); + continue; } - else if (t.cs() == "marginpar") { + if (t.cs() == "marginpar") { p.skip_spaces(); context.check_layout(os); begin_inset(os, "Marginal\n"); os << "status collapsed\n\n"; parse_text_in_inset(p, os, FLAG_ITEM, false, context); end_inset(os); + continue; } - else if (t.cs() == "lstinline" || t.cs() == "mintinline") { + if (t.cs() == "lstinline" || t.cs() == "mintinline") { bool const use_minted = t.cs() == "mintinline"; p.skip_spaces(); parse_listings(p, os, context, true, use_minted); + continue; } - else if (t.cs() == "ensuremath") { + if (t.cs() == "ensuremath") { p.skip_spaces(); context.check_layout(os); string const s = p.verbatim_item(); @@ -3352,17 +3623,19 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, else output_ert_inset(os, "\\ensuremath{" + s + "}", context); + continue; } - else if (t.cs() == "makeindex" || t.cs() == "maketitle") { + else if (t.cs() == "makeindex" || t.cs() == "maketitle" || t.cs() == "makebeamertitle") { if (preamble.titleLayoutFound()) { // swallow this skip_spaces_braces(p); } else output_ert_inset(os, t.asInput(), context); + continue; } - else if (t.cs() == "tableofcontents" + if (t.cs() == "tableofcontents" || t.cs() == "lstlistoflistings" || t.cs() == "listoflistings") { string name = t.cs(); @@ -3378,9 +3651,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, else preamble.registerAutomaticallyLoadedPackage("listings"); } + continue; } - else if (t.cs() == "listoffigures" || t.cs() == "listoftables") { + if (t.cs() == "listoffigures" || t.cs() == "listoftables") { context.check_layout(os); if (t.cs() == "listoffigures") begin_inset(os, "FloatList figure\n"); @@ -3388,9 +3662,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, begin_inset(os, "FloatList table\n"); end_inset(os); skip_spaces_braces(p); + continue; } - else if (t.cs() == "listof") { + if (t.cs() == "listof") { p.skip_spaces(true); string const name = p.get_token().cs(); if (context.textclass.floats().typeExist(name)) { @@ -3401,24 +3676,33 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, p.get_token(); // swallow second arg } else output_ert_inset(os, "\\listof{" + name + "}", context); + continue; } - else if ((where = is_known(t.cs(), known_text_font_families))) + if ((where = is_known(t.cs(), known_text_font_families))) { parse_text_attributes(p, os, FLAG_ITEM, outer, context, "\\family", context.font.family, known_coded_font_families[where - known_text_font_families]); + continue; + } - else if ((where = is_known(t.cs(), known_text_font_series))) + // beamer has a \textbf{} inset + if (!p.hasOpt("<") && (where = is_known(t.cs(), known_text_font_series))) { parse_text_attributes(p, os, FLAG_ITEM, outer, context, "\\series", context.font.series, known_coded_font_series[where - known_text_font_series]); + continue; + } - else if ((where = is_known(t.cs(), known_text_font_shapes))) + // beamer has a \textit{} inset + if (!p.hasOpt("<") && (where = is_known(t.cs(), known_text_font_shapes))) { parse_text_attributes(p, os, FLAG_ITEM, outer, context, "\\shape", context.font.shape, known_coded_font_shapes[where - known_text_font_shapes]); + continue; + } - else if (t.cs() == "textnormal" || t.cs() == "normalfont") { + if (t.cs() == "textnormal" || t.cs() == "normalfont") { context.check_layout(os); TeXFont oldFont = context.font; context.font.init(); @@ -3432,9 +3716,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, context.font = oldFont; } else eat_whitespace(p, os, context, false); + continue; } - else if (t.cs() == "textcolor") { + if (t.cs() == "textcolor") { // scheme is \textcolor{color name}{text} string const color = p.verbatim_item(); // we support the predefined colors of the color and the xcolor package @@ -3460,9 +3745,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, } else // for custom defined colors output_ert_inset(os, t.asInput() + "{" + color + "}", context); + continue; } - else if (t.cs() == "underbar" || t.cs() == "uline") { + if (t.cs() == "underbar" || t.cs() == "uline") { // \underbar is not 100% correct (LyX outputs \uline // of ulem.sty). The difference is that \ulem allows // line breaks, and \underbar does not. @@ -3475,20 +3761,23 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, context.check_layout(os); os << "\n\\bar default\n"; preamble.registerAutomaticallyLoadedPackage("ulem"); + continue; } - else if (t.cs() == "sout") { + if (t.cs() == "sout") { context.check_layout(os); os << "\n\\strikeout on\n"; parse_text_snippet(p, os, FLAG_ITEM, outer, context); context.check_layout(os); os << "\n\\strikeout default\n"; preamble.registerAutomaticallyLoadedPackage("ulem"); + continue; } - else if (t.cs() == "uuline" || t.cs() == "uwave" + // beamer has an \emph{} inset + if ((t.cs() == "uuline" || t.cs() == "uwave" || t.cs() == "emph" || t.cs() == "noun" - || t.cs() == "xout") { + || t.cs() == "xout") && !p.hasOpt("<")) { context.check_layout(os); os << "\n\\" << t.cs() << " on\n"; parse_text_snippet(p, os, FLAG_ITEM, outer, context); @@ -3496,9 +3785,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, os << "\n\\" << t.cs() << " default\n"; if (t.cs() == "uuline" || t.cs() == "uwave" || t.cs() == "xout") preamble.registerAutomaticallyLoadedPackage("ulem"); + continue; } - else if (t.cs() == "lyxadded" || t.cs() == "lyxdeleted") { + if (t.cs() == "lyxadded" || t.cs() == "lyxdeleted") { context.check_layout(os); string name = p.getArg('{', '}'); string localtime = p.getArg('{', '}'); @@ -3538,9 +3828,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, preamble.registerAutomaticallyLoadedPackage("xcolor"); } } + continue; } - else if (t.cs() == "textipa") { + if (t.cs() == "textipa") { context.check_layout(os); begin_inset(os, "IPA\n"); bool merging_hyphens_allowed = context.merging_hyphens_allowed; @@ -3550,25 +3841,32 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, end_inset(os); preamble.registerAutomaticallyLoadedPackage("tipa"); preamble.registerAutomaticallyLoadedPackage("tipx"); + continue; } - else if (t.cs() == "texttoptiebar" || t.cs() == "textbottomtiebar") { + if ((preamble.isPackageUsed("tipa") && t.cs() == "t" && p.next_token().asInput() == "*") + || t.cs() == "texttoptiebar" || t.cs() == "textbottomtiebar") { context.check_layout(os); - begin_inset(os, "IPADeco " + t.cs().substr(4) + "\n"); + if (t.cs() == "t") + // swallow star + p.get_token(); + string const type = (t.cs() == "t") ? "bottomtiebar" : t.cs().substr(4); + begin_inset(os, "IPADeco " + type + "\n"); os << "status open\n"; parse_text_in_inset(p, os, FLAG_ITEM, outer, context); end_inset(os); p.skip_spaces(); + continue; } - else if (t.cs() == "textvertline") { + if (t.cs() == "textvertline") { // FIXME: This is not correct, \textvertline is higher than | os << "|"; skip_braces(p); continue; } - else if (t.cs() == "tone" ) { + if (t.cs() == "tone" ) { context.check_layout(os); // register the tone package preamble.registerAutomaticallyLoadedPackage("tone"); @@ -3596,9 +3894,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, } else // we did not find a non-ert version output_ert_inset(os, command, context); + continue; } - else if (t.cs() == "phantom" || t.cs() == "hphantom" || + if (t.cs() == "phantom" || t.cs() == "hphantom" || t.cs() == "vphantom") { context.check_layout(os); if (t.cs() == "phantom") @@ -3611,12 +3910,17 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, parse_text_in_inset(p, os, FLAG_ITEM, outer, context, "Phantom"); end_inset(os); + continue; } - else if (t.cs() == "href") { + if (t.cs() == "href") { context.check_layout(os); - string target = convert_command_inset_arg(p.verbatim_item()); - string name = convert_command_inset_arg(p.verbatim_item()); + string target = convert_literate_command_inset_arg(p.verbatim_item()); + string name = p.verbatim_item(); + pair nm = convert_latexed_command_inset_arg(name); + bool const literal = !nm.first; + name = literal ? subst(name, "\n", " ") : nm.second; + string lit = literal ? "\"true\"" : "\"false\""; string type; size_t i = target.find(':'); if (i != string::npos) { @@ -3633,12 +3937,13 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, os << "target \"" << target << "\"\n"; if (type == "mailto:" || type == "file:") os << "type \"" << type << "\"\n"; - os << "literal \"true\"\n"; + os << "literal " << lit << "\n"; end_inset(os); skip_spaces_braces(p); + continue; } - else if (t.cs() == "lyxline") { + if (t.cs() == "lyxline") { // swallow size argument (it is not used anyway) p.getArg('{', '}'); if (!context.atParagraphStart()) { @@ -3663,9 +3968,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, "width \"100line%\"\n" "height \"1pt\"\n"; end_inset(os); + continue; } - else if (t.cs() == "rule") { + if (t.cs() == "rule") { string const offset = (p.hasOpt() ? p.getArg('[', ']') : string()); string const width = p.getArg('{', '}'); string const thickness = p.getArg('{', '}'); @@ -3676,31 +3982,33 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, os << "width \"" << translate_len(width) << "\"\n" "height \"" << translate_len(thickness) << "\"\n"; end_inset(os); + continue; } // handle refstyle first to catch \eqref which can also occur // without refstyle. Only recognize these commands if // refstyle.sty was found in the preamble (otherwise \eqref // and user defined ref commands could be misdetected). - else if ((where = is_known(t.cs(), known_refstyle_commands)) && - preamble.refstyle()) { + if ((where = is_known(t.cs(), known_refstyle_commands)) + && preamble.refstyle()) { context.check_layout(os); begin_command_inset(os, "ref", "formatted"); os << "reference \""; os << known_refstyle_prefixes[where - known_refstyle_commands] << ":"; - os << convert_command_inset_arg(p.verbatim_item()) + os << convert_literate_command_inset_arg(p.verbatim_item()) << "\"\n"; os << "plural \"false\"\n"; os << "caps \"false\"\n"; os << "noprefix \"false\"\n"; end_inset(os); preamble.registerAutomaticallyLoadedPackage("refstyle"); + continue; } // if refstyle is used, we must not convert \prettyref to a // formatted reference, since that would result in a refstyle command. - else if ((where = is_known(t.cs(), known_ref_commands)) && + if ((where = is_known(t.cs(), known_ref_commands)) && (t.cs() != "prettyref" || !preamble.refstyle())) { string const opt = p.getOpt(); if (opt.empty()) { @@ -3708,7 +4016,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, begin_command_inset(os, "ref", known_coded_ref_commands[where - known_ref_commands]); os << "reference \"" - << convert_command_inset_arg(p.verbatim_item()) + << convert_literate_command_inset_arg(p.verbatim_item()) << "\"\n"; os << "plural \"false\"\n"; os << "caps \"false\"\n"; @@ -3723,9 +4031,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, output_ert_inset(os, t.asInput() + '[' + opt + "]{" + p.verbatim_item() + '}', context); } + continue; } - else if (use_natbib && + if (use_natbib && is_known(t.cs(), known_natbib_commands) && ((t.cs() != "citefullauthor" && t.cs() != "citeyear" && @@ -3764,32 +4073,224 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, before.erase(); after.erase(); } + bool literal = false; + pair aft; + pair bef; // remove the brackets around after and before if (!after.empty()) { after.erase(0, 1); after.erase(after.length() - 1, 1); - after = convert_command_inset_arg(after); + aft = convert_latexed_command_inset_arg(after); + literal = !aft.first; + after = literal ? subst(after, "\n", " ") : aft.second; } if (!before.empty()) { before.erase(0, 1); before.erase(before.length() - 1, 1); - before = convert_command_inset_arg(before); + bef = convert_latexed_command_inset_arg(before); + literal |= !bef.first; + before = literal ? subst(before, "\n", " ") : bef.second; + if (literal && !after.empty()) + after = subst(after, "\n", " "); } + string lit = literal ? "\"true\"" : "\"false\""; begin_command_inset(os, "citation", command); os << "after " << '"' << after << '"' << "\n"; os << "before " << '"' << before << '"' << "\n"; os << "key \"" - << convert_command_inset_arg(p.verbatim_item()) + << convert_literate_command_inset_arg(p.verbatim_item()) << "\"\n" - << "literal \"true\"\n"; + << "literal " << lit << "\n"; end_inset(os); // Need to set the cite engine if natbib is loaded by // the document class directly if (preamble.citeEngine() == "basic") preamble.citeEngine("natbib"); + continue; } - else if (use_jurabib && + if ((use_biblatex + && is_known(t.cs(), known_biblatex_commands) + && ((t.cs() == "cite" + || t.cs() == "citeauthor" + || t.cs() == "Citeauthor" + || t.cs() == "parencite" + || t.cs() == "citetitle") + || p.next_token().asInput() != "*")) + || (use_biblatex_natbib + && (is_known(t.cs(), known_biblatex_commands) + || is_known(t.cs(), known_natbib_commands)) + && ((t.cs() == "cite" || t.cs() == "citet" || t.cs() == "Citet" + || t.cs() == "citep" || t.cs() == "Citep" || t.cs() == "citealt" + || t.cs() == "Citealt" || t.cs() == "citealp" || t.cs() == "Citealp" + || t.cs() == "citeauthor" || t.cs() == "Citeauthor" + || t.cs() == "parencite" || t.cs() == "citetitle") + || p.next_token().asInput() != "*"))){ + context.check_layout(os); + string command = t.cs(); + if (p.next_token().asInput() == "*") { + command += '*'; + p.get_token(); + } + + bool const qualified = suffixIs(command, "s"); + if (qualified) + command = rtrim(command, "s"); + + // text before the citation + string before; + // text after the citation + string after; + get_cite_arguments(p, true, before, after, qualified); + + // These use natbib cmd names in LyX + // for inter-citeengine compativility + if (command == "citeyear") + command = "citebyear"; + else if (command == "cite*") + command = "citeyear"; + else if (command == "textcite") + command = "citet"; + else if (command == "Textcite") + command = "Citet"; + else if (command == "parencite") + command = "citep"; + else if (command == "Parencite") + command = "Citep"; + else if (command == "parencite*") + command = "citeyearpar"; + else if (command == "smartcite") + command = "footcite"; + else if (command == "Smartcite") + command = "Footcite"; + + string const emptyarg = qualified ? "()" : "[]"; + if (before.empty() && after == emptyarg) + // avoid \cite[]{a} + after.erase(); + else if (before == emptyarg && after == emptyarg) { + // avoid \cite[][]{a} + before.erase(); + after.erase(); + } + bool literal = false; + pair aft; + pair bef; + // remove the brackets around after and before + if (!after.empty()) { + after.erase(0, 1); + after.erase(after.length() - 1, 1); + aft = convert_latexed_command_inset_arg(after); + literal = !aft.first; + after = literal ? subst(after, "\n", " ") : aft.second; + } + if (!before.empty()) { + before.erase(0, 1); + before.erase(before.length() - 1, 1); + bef = convert_latexed_command_inset_arg(before); + literal |= !bef.first; + before = literal ? subst(before, "\n", " ") : bef.second; + } + string keys, pretextlist, posttextlist; + if (qualified) { + map pres, posts, preslit, postslit; + vector lkeys; + // text before the citation + string lbefore, lbeforelit; + // text after the citation + string lafter, lafterlit; + string lkey; + pair laft, lbef; + while (true) { + get_cite_arguments(p, true, lbefore, lafter); + // remove the brackets around after and before + if (!lafter.empty()) { + lafter.erase(0, 1); + lafter.erase(lafter.length() - 1, 1); + laft = convert_latexed_command_inset_arg(lafter); + literal |= !laft.first; + lafter = laft.second; + lafterlit = subst(lbefore, "\n", " "); + } + if (!lbefore.empty()) { + lbefore.erase(0, 1); + lbefore.erase(lbefore.length() - 1, 1); + lbef = convert_latexed_command_inset_arg(lbefore); + literal |= !lbef.first; + lbefore = lbef.second; + lbeforelit = subst(lbefore, "\n", " "); + } + if (lbefore.empty() && lafter == "[]") { + // avoid \cite[]{a} + lafter.erase(); + lafterlit.erase(); + } + else if (lbefore == "[]" && lafter == "[]") { + // avoid \cite[][]{a} + lbefore.erase(); + lafter.erase(); + lbeforelit.erase(); + lafterlit.erase(); + } + lkey = p.getArg('{', '}'); + if (lkey.empty()) + break; + if (!lbefore.empty()) { + pres.insert(make_pair(lkey, lbefore)); + preslit.insert(make_pair(lkey, lbeforelit)); + } + if (!lafter.empty()) { + posts.insert(make_pair(lkey, lafter)); + postslit.insert(make_pair(lkey, lafterlit)); + } + lkeys.push_back(lkey); + } + keys = convert_literate_command_inset_arg(getStringFromVector(lkeys)); + if (literal) { + pres = preslit; + posts = postslit; + } + for (auto const & ptl : pres) { + if (!pretextlist.empty()) + pretextlist += '\t'; + pretextlist += ptl.first + " " + ptl.second; + } + for (auto const & potl : posts) { + if (!posttextlist.empty()) + posttextlist += '\t'; + posttextlist += potl.first + " " + potl.second; + } + } else + keys = convert_literate_command_inset_arg(p.verbatim_item()); + if (literal) { + if (!after.empty()) + after = subst(after, "\n", " "); + if (!before.empty()) + before = subst(after, "\n", " "); + } + string lit = literal ? "\"true\"" : "\"false\""; + begin_command_inset(os, "citation", command); + os << "after " << '"' << after << '"' << "\n"; + os << "before " << '"' << before << '"' << "\n"; + os << "key \"" + << keys + << "\"\n"; + if (!pretextlist.empty()) + os << "pretextlist " << '"' << pretextlist << '"' << "\n"; + if (!posttextlist.empty()) + os << "posttextlist " << '"' << posttextlist << '"' << "\n"; + os << "literal " << lit << "\n"; + end_inset(os); + // Need to set the cite engine if biblatex is loaded by + // the document class directly + if (preamble.citeEngine() == "basic") + use_biblatex_natbib ? + preamble.citeEngine("biblatex-natbib") + : preamble.citeEngine("biblatex"); + continue; + } + + if (use_jurabib && is_known(t.cs(), known_jurabib_commands) && (t.cs() == "cite" || p.next_token().asInput() != "*")) { context.check_layout(os); @@ -3824,45 +4325,64 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, "package options if you used an\n" "earlier jurabib version." << endl; } + bool literal = false; + pair aft; + pair bef; + // remove the brackets around after and before if (!after.empty()) { after.erase(0, 1); after.erase(after.length() - 1, 1); + aft = convert_latexed_command_inset_arg(after); + literal = !aft.first; + after = literal ? subst(after, "\n", " ") : aft.second; } if (!before.empty()) { before.erase(0, 1); before.erase(before.length() - 1, 1); + bef = convert_latexed_command_inset_arg(before); + literal |= !bef.first; + before = literal ? subst(before, "\n", " ") : bef.second; + if (literal && !after.empty()) + after = subst(after, "\n", " "); } + string lit = literal ? "\"true\"" : "\"false\""; begin_command_inset(os, "citation", command); os << "after " << '"' << after << "\"\n" << "before " << '"' << before << "\"\n" << "key " << '"' << citation << "\"\n" - << "literal \"true\"\n"; + << "literal " << lit << "\n"; end_inset(os); // Need to set the cite engine if jurabib is loaded by // the document class directly if (preamble.citeEngine() == "basic") preamble.citeEngine("jurabib"); + continue; } - else if (t.cs() == "cite" + if (t.cs() == "cite" || t.cs() == "nocite") { context.check_layout(os); - string after = convert_command_inset_arg(p.getArg('[', ']')); - string key = convert_command_inset_arg(p.verbatim_item()); + string after = p.getArg('[', ']'); + pair aft = convert_latexed_command_inset_arg(after); + bool const literal = !aft.first; + after = literal ? subst(after, "\n", " ") : aft.second; + string lit = literal ? "\"true\"" : "\"false\""; + string key = convert_literate_command_inset_arg(p.verbatim_item()); // store the case that it is "\nocite{*}" to use it later for // the BibTeX inset if (key != "*") { begin_command_inset(os, "citation", t.cs()); os << "after " << '"' << after << "\"\n" << "key " << '"' << key << "\"\n" - << "literal \"true\"\n"; + << "literal " << lit << "\n"; end_inset(os); } else if (t.cs() == "nocite") btprint = key; + continue; } - else if (t.cs() == "index" || - (t.cs() == "sindex" && preamble.use_indices() == "true")) { + if (t.cs() == "index" || + (t.cs() == "sindex" && preamble.use_indices() == "true")) { context.check_layout(os); string const arg = (t.cs() == "sindex" && p.hasOpt()) ? p.getArg('[', ']') : ""; @@ -3873,34 +4393,49 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, end_inset(os); if (kind != "idx") preamble.registerAutomaticallyLoadedPackage("splitidx"); + continue; } - else if (t.cs() == "nomenclature") { + if (t.cs() == "nomenclature") { context.check_layout(os); begin_command_inset(os, "nomenclature", "nomenclature"); - string prefix = convert_command_inset_arg(p.getArg('[', ']')); + string prefix = convert_literate_command_inset_arg(p.getArg('[', ']')); if (!prefix.empty()) os << "prefix " << '"' << prefix << '"' << "\n"; - os << "symbol " << '"' - << convert_command_inset_arg(p.verbatim_item()); + string symbol = p.verbatim_item(); + pair sym = convert_latexed_command_inset_arg(symbol); + bool literal = !sym.first; + string description = p.verbatim_item(); + pair desc = convert_latexed_command_inset_arg(description); + literal |= !desc.first; + if (literal) { + symbol = subst(symbol, "\n", " "); + description = subst(description, "\n", " "); + } else { + symbol = sym.second; + description = desc.second; + } + string lit = literal ? "\"true\"" : "\"false\""; + os << "symbol " << '"' << symbol; os << "\"\ndescription \"" - << convert_command_inset_arg(p.verbatim_item()) - << "\"\n" - << "literal \"true\"\n"; + << description << "\"\n" + << "literal " << lit << "\n"; end_inset(os); preamble.registerAutomaticallyLoadedPackage("nomencl"); + continue; } - else if (t.cs() == "label") { + if (t.cs() == "label") { context.check_layout(os); begin_command_inset(os, "label", "label"); os << "name \"" - << convert_command_inset_arg(p.verbatim_item()) + << convert_literate_command_inset_arg(p.verbatim_item()) << "\"\n"; end_inset(os); + continue; } - else if (t.cs() == "lyxmintcaption") { + if (t.cs() == "lyxmintcaption") { string const pos = p.getArg('[', ']'); if (pos == "t") { string const caption = @@ -3912,9 +4447,11 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, // so simply skip it. parse_text_snippet(p, FLAG_ITEM, false, context); } + eat_whitespace(p, os, context, true); + continue; } - else if (t.cs() == "printindex" || t.cs() == "printsubindex") { + if (t.cs() == "printindex" || t.cs() == "printsubindex") { context.check_layout(os); string commandname = t.cs(); bool star = false; @@ -3937,9 +4474,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, preamble.registerAutomaticallyLoadedPackage("makeidx"); if (preamble.use_indices() == "true") preamble.registerAutomaticallyLoadedPackage("splitidx"); + continue; } - else if (t.cs() == "printnomenclature") { + if (t.cs() == "printnomenclature") { string width = ""; string width_type = ""; context.check_layout(os); @@ -3955,7 +4493,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, // via \settowidth{\nomlabelwidth}{***} cannot be supported // because the user could have set anything, not only the width // of the longest label (which would be width_type = "auto") - string label = convert_command_inset_arg(p.getArg('{', '}')); + string label = convert_literate_command_inset_arg(p.getArg('{', '}')); if (label.empty() && width_type.empty()) width_type = "none"; os << "set_width \"" << width_type << "\"\n"; @@ -3964,9 +4502,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, end_inset(os); skip_spaces_braces(p); preamble.registerAutomaticallyLoadedPackage("nomencl"); + continue; } - else if ((t.cs() == "textsuperscript" || t.cs() == "textsubscript")) { + if ((t.cs() == "textsuperscript" || t.cs() == "textsubscript")) { context.check_layout(os); begin_inset(os, "script "); os << t.cs().substr(4) << '\n'; @@ -3975,30 +4514,39 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, end_inset(os); if (t.cs() == "textsubscript") preamble.registerAutomaticallyLoadedPackage("subscript"); + continue; } - else if ((where = is_known(t.cs(), known_quotes))) { + if ((where = is_known(t.cs(), known_quotes))) { context.check_layout(os); begin_inset(os, "Quotes "); - os << known_coded_quotes[where - known_quotes]; + string quotetype = known_coded_quotes[where - known_quotes]; + // try to make a smart guess about the side + Token const prev = p.prev_token(); + bool const opening = (prev.cat() != catSpace && prev.character() != 0 + && prev.character() != '\n' && prev.character() != '~'); + quotetype = guessQuoteStyle(quotetype, opening); + os << quotetype; end_inset(os); // LyX adds {} after the quote, so we have to eat // spaces here if there are any before a possible // {} pair. eat_whitespace(p, os, context, false); skip_braces(p); + continue; } - else if ((where = is_known(t.cs(), known_sizes)) && - context.new_layout_allowed) { + if ((where = is_known(t.cs(), known_sizes)) && + context.new_layout_allowed) { context.check_layout(os); TeXFont const oldFont = context.font; context.font.size = known_coded_sizes[where - known_sizes]; output_font_change(os, oldFont, context.font); eat_whitespace(p, os, context, false); + continue; } - else if ((where = is_known(t.cs(), known_font_families)) && + if ((where = is_known(t.cs(), known_font_families)) && context.new_layout_allowed) { context.check_layout(os); TeXFont const oldFont = context.font; @@ -4006,9 +4554,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, known_coded_font_families[where - known_font_families]; output_font_change(os, oldFont, context.font); eat_whitespace(p, os, context, false); + continue; } - else if ((where = is_known(t.cs(), known_font_series)) && + if ((where = is_known(t.cs(), known_font_series)) && context.new_layout_allowed) { context.check_layout(os); TeXFont const oldFont = context.font; @@ -4016,9 +4565,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, known_coded_font_series[where - known_font_series]; output_font_change(os, oldFont, context.font); eat_whitespace(p, os, context, false); + continue; } - else if ((where = is_known(t.cs(), known_font_shapes)) && + if ((where = is_known(t.cs(), known_font_shapes)) && context.new_layout_allowed) { context.check_layout(os); TeXFont const oldFont = context.font; @@ -4026,8 +4576,9 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, known_coded_font_shapes[where - known_font_shapes]; output_font_change(os, oldFont, context.font); eat_whitespace(p, os, context, false); + continue; } - else if ((where = is_known(t.cs(), known_old_font_families)) && + if ((where = is_known(t.cs(), known_old_font_families)) && context.new_layout_allowed) { context.check_layout(os); TeXFont const oldFont = context.font; @@ -4037,9 +4588,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, known_coded_font_families[where - known_old_font_families]; output_font_change(os, oldFont, context.font); eat_whitespace(p, os, context, false); + continue; } - else if ((where = is_known(t.cs(), known_old_font_series)) && + if ((where = is_known(t.cs(), known_old_font_series)) && context.new_layout_allowed) { context.check_layout(os); TeXFont const oldFont = context.font; @@ -4049,9 +4601,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, known_coded_font_series[where - known_old_font_series]; output_font_change(os, oldFont, context.font); eat_whitespace(p, os, context, false); + continue; } - else if ((where = is_known(t.cs(), known_old_font_shapes)) && + if ((where = is_known(t.cs(), known_old_font_shapes)) && context.new_layout_allowed) { context.check_layout(os); TeXFont const oldFont = context.font; @@ -4061,24 +4614,27 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, known_coded_font_shapes[where - known_old_font_shapes]; output_font_change(os, oldFont, context.font); eat_whitespace(p, os, context, false); + continue; } - else if (t.cs() == "selectlanguage") { + if (t.cs() == "selectlanguage") { context.check_layout(os); // save the language for the case that a // \foreignlanguage is used context.font.language = babel2lyx(p.verbatim_item()); os << "\n\\lang " << context.font.language << "\n"; + continue; } - else if (t.cs() == "foreignlanguage") { + if (t.cs() == "foreignlanguage") { string const lang = babel2lyx(p.verbatim_item()); parse_text_attributes(p, os, FLAG_ITEM, outer, context, "\\lang", context.font.language, lang); + continue; } - else if (prefixIs(t.cs(), "text") && preamble.usePolyglossia() + if (prefixIs(t.cs(), "text") && preamble.usePolyglossia() && is_known(t.cs().substr(4), preamble.polyglossia_languages)) { // scheme is \textLANGUAGE{text} where LANGUAGE is in polyglossia_languages[] string lang; @@ -4106,18 +4662,20 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, context, "\\lang", context.font.language, lang); } + continue; } - else if (t.cs() == "inputencoding") { + if (t.cs() == "inputencoding") { // nothing to write here string const enc = subst(p.verbatim_item(), "\n", " "); p.setEncoding(enc, Encoding::inputenc); + continue; } - else if (is_known(t.cs(), known_special_chars) || - (t.cs() == "protect" && - p.next_token().cat() == catEscape && - is_known(p.next_token().cs(), known_special_protect_chars))) { + if (is_known(t.cs(), known_special_chars) || + (t.cs() == "protect" && + p.next_token().cat() == catEscape && + is_known(p.next_token().cs(), known_special_protect_chars))) { // LyX sometimes puts a \protect in front, so we have to ignore it where = is_known( t.cs() == "protect" ? p.get_token().cs() : t.cs(), @@ -4125,9 +4683,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, context.check_layout(os); os << known_coded_special_chars[where - known_special_chars]; skip_spaces_braces(p); + continue; } - else if ((t.cs() == "nobreakdash" && p.next_token().asInput() == "-") || + if ((t.cs() == "nobreakdash" && p.next_token().asInput() == "-") || (t.cs() == "protect" && p.next_token().asInput() == "\\nobreakdash" && p.next_next_token().asInput() == "-") || (t.cs() == "@" && p.next_token().asInput() == ".")) { @@ -4140,15 +4699,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, else os << "\\SpecialChar endofsentence\n"; p.get_token(); + continue; } - else if (t.cs() == "textquotedbl") { - context.check_layout(os); - os << "\""; - skip_braces(p); - } - - else if (t.cs() == "_" || t.cs() == "&" || t.cs() == "#" + if (t.cs() == "_" || t.cs() == "&" || t.cs() == "#" || t.cs() == "$" || t.cs() == "{" || t.cs() == "}" || t.cs() == "%" || t.cs() == "-") { context.check_layout(os); @@ -4156,9 +4710,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, os << "\\SpecialChar softhyphen\n"; else os << t.cs(); + continue; } - else if (t.cs() == "char") { + if (t.cs() == "char") { context.check_layout(os); if (p.next_token().character() == '`') { p.get_token(); @@ -4172,9 +4727,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, } else { output_ert_inset(os, "\\char", context); } + continue; } - else if (t.cs() == "verb") { + if (t.cs() == "verb") { context.check_layout(os); // set catcodes to verbatim early, just in case. p.setCatcodes(VERBATIM_CATCODES); @@ -4185,15 +4741,18 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, + arg.second + delim, context); else cerr << "invalid \\verb command. Skipping" << endl; + continue; } // Problem: \= creates a tabstop inside the tabbing environment // and else an accent. In the latter case we really would want // \={o} instead of \= o. - else if (t.cs() == "=" && (flags & FLAG_TABBING)) + if (t.cs() == "=" && (flags & FLAG_TABBING)) { output_ert_inset(os, t.asInput(), context); + continue; + } - else if (t.cs() == "\\") { + if (t.cs() == "\\") { context.check_layout(os); if (p.hasOpt()) output_ert_inset(os, "\\\\" + p.getOpt(), context); @@ -4208,24 +4767,63 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, begin_inset(os, "Newline newline"); end_inset(os); } + continue; } - else if (t.cs() == "newline" || - (t.cs() == "linebreak" && !p.hasOpt())) { + if (t.cs() == "newline" || + (t.cs() == "linebreak" && !p.hasOpt())) { context.check_layout(os); begin_inset(os, "Newline "); os << t.cs(); end_inset(os); skip_spaces_braces(p); + continue; } - else if (t.cs() == "input" || t.cs() == "include" - || t.cs() == "verbatiminput") { + if (t.cs() == "input" || t.cs() == "include" + || t.cs() == "verbatiminput" + || t.cs() == "lstinputlisting" + || t.cs() == "inputminted") { string name = t.cs(); - if (t.cs() == "verbatiminput" + if (name == "verbatiminput" && p.next_token().asInput() == "*") name += p.get_token().asInput(); context.check_layout(os); + string lstparams; + if (name == "lstinputlisting" && p.hasOpt()) { + lstparams = p.getArg('[', ']'); + lstparams = subst(lstparams, "\n", " "); + } else if (name == "inputminted") { + name = "lstinputlisting"; + string const lang = p.getArg('{', '}'); + if (lang != "tex") { + string cmd = "\\inputminted{" + lang + "}{"; + cmd += p.getArg('{', '}') + "}"; + output_ert_inset(os, cmd, context); + continue; + } + if (prefixIs(minted_nonfloat_caption, "[t]")) { + minted_nonfloat_caption.erase(0,3); + // extract label and caption from the already produced LyX code + vector nfc = getVectorFromString(minted_nonfloat_caption, "\n"); + string const caption = nfc.front(); + string label; + vector::iterator it = + find(nfc.begin(), nfc.end(), "LatexCommand label"); + if (it != nfc.end()) { + ++it; + if (it != nfc.end()) + label = *it; + label = support::split(label, '"'); + label.pop_back(); + } + minted_nonfloat_caption.clear(); + lstparams = "caption=" + caption; + if (!label.empty()) + lstparams += ",label=" + label; + lstparams = subst(lstparams, "\n", " "); + } + } string filename(normalize_filename(p.getArg('{', '}'))); string const path = getMasterFilePath(true); // We want to preserve relative / absolute filenames, @@ -4333,13 +4931,16 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, outname = subst(outname, "\"", "\\\""); os << "preview false\n" "filename \"" << outname << "\"\n"; + if (!lstparams.empty()) + os << "lstparams \"" << lstparams << "\"\n"; if (t.cs() == "verbatiminput") preamble.registerAutomaticallyLoadedPackage("verbatim"); } end_inset(os); + continue; } - else if (t.cs() == "bibliographystyle") { + if (t.cs() == "bibliographystyle") { // store new bibliographystyle bibliographystyle = p.verbatim_item(); // If any other command than \bibliography, \addcontentsline @@ -4379,16 +4980,18 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, "\\bibliographystyle{" + bibliographystyle + '}', context); } + continue; } - else if (t.cs() == "phantomsection") { + if (t.cs() == "phantomsection") { // we only support this if it occurs between // \bibliographystyle and \bibliography if (bibliographystyle.empty()) output_ert_inset(os, "\\phantomsection", context); + continue; } - else if (t.cs() == "addcontentsline") { + if (t.cs() == "addcontentsline") { context.check_layout(os); // get the 3 arguments of \addcontentsline string const one = p.getArg('{', '}'); @@ -4400,6 +5003,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, "\\addcontentsline{" + one + "}{" + two + "}{"+ three + '}', context); } + continue; } else if (t.cs() == "bibliography") { @@ -4431,9 +5035,81 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, } os << "options " << '"' << BibOpts << '"' << "\n"; end_inset(os); + continue; } - else if (t.cs() == "parbox") { + if (t.cs() == "printbibliography") { + context.check_layout(os); + string BibOpts; + string bbloptions = p.hasOpt() ? p.getArg('[', ']') : string(); + vector opts = getVectorFromString(bbloptions); + vector::iterator it = + find(opts.begin(), opts.end(), "heading=bibintoc"); + if (it != opts.end()) { + opts.erase(it); + BibOpts = "bibtotoc"; + } + bbloptions = getStringFromVector(opts); + begin_command_inset(os, "bibtex", "bibtex"); + if (!btprint.empty()) { + os << "btprint " << '"' << "btPrintAll" << '"' << "\n"; + // clear the string because the next BibTeX inset can be without the + // \nocite{*} option + btprint.clear(); + } + string bibfiles; + for (auto const & bf : preamble.biblatex_bibliographies) { + if (!bibfiles.empty()) + bibfiles += ","; + bibfiles += normalize_filename(bf); + } + if (!bibfiles.empty()) + os << "bibfiles " << '"' << bibfiles << '"' << "\n"; + // Do we have addcontentsline? + if (contentslineContent == "\\refname") { + BibOpts = "bibtotoc"; + // clear string because next BibTeX inset can be without addcontentsline + contentslineContent.clear(); + } + os << "options " << '"' << BibOpts << '"' << "\n"; + if (!bbloptions.empty()) + os << "biblatexopts " << '"' << bbloptions << '"' << "\n"; + end_inset(os); + need_commentbib = false; + continue; + } + + if (t.cs() == "bibbysection") { + context.check_layout(os); + string BibOpts; + string bbloptions = p.hasOpt() ? p.getArg('[', ']') : string(); + vector opts = getVectorFromString(bbloptions); + vector::iterator it = + find(opts.begin(), opts.end(), "heading=bibintoc"); + if (it != opts.end()) { + opts.erase(it); + BibOpts = "bibtotoc"; + } + bbloptions = getStringFromVector(opts); + begin_command_inset(os, "bibtex", "bibtex"); + os << "btprint " << '"' << "bibbysection" << '"' << "\n"; + string bibfiles; + for (auto const & bf : preamble.biblatex_bibliographies) { + if (!bibfiles.empty()) + bibfiles += ","; + bibfiles += normalize_filename(bf); + } + if (!bibfiles.empty()) + os << "bibfiles " << '"' << bibfiles << '"' << "\n"; + os << "options " << '"' << BibOpts << '"' << "\n"; + if (!bbloptions.empty()) + os << "biblatexopts " << '"' << bbloptions << '"' << "\n"; + end_inset(os); + need_commentbib = false; + continue; + } + + if (t.cs() == "parbox") { // Test whether this is an outer box of a shaded box p.pushPosition(); // swallow arguments @@ -4461,14 +5137,17 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, } else parse_box(p, os, 0, FLAG_ITEM, outer, context, "", "", t.cs(), "", ""); + continue; } - else if (t.cs() == "fbox" || t.cs() == "mbox" || - t.cs() == "ovalbox" || t.cs() == "Ovalbox" || - t.cs() == "shadowbox" || t.cs() == "doublebox") + if (t.cs() == "fbox" || t.cs() == "mbox" || + t.cs() == "ovalbox" || t.cs() == "Ovalbox" || + t.cs() == "shadowbox" || t.cs() == "doublebox") { parse_outer_box(p, os, FLAG_ITEM, outer, context, t.cs(), ""); + continue; + } - else if (t.cs() == "fcolorbox" || t.cs() == "colorbox") { + if (t.cs() == "fcolorbox" || t.cs() == "colorbox") { string backgroundcolor; preamble.registerAutomaticallyLoadedPackage("xcolor"); if (t.cs() == "fcolorbox") { @@ -4479,15 +5158,16 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, backgroundcolor = p.getArg('{', '}'); parse_box(p, os, 0, 0, outer, context, "", "", "", "", backgroundcolor); } + continue; } // FIXME: due to the compiler limit of "if" nestings // the code for the alignment was put here // put them in their own if if this is fixed - else if (t.cs() == "fboxrule" || t.cs() == "fboxsep" - || t.cs() == "shadowsize" - || t.cs() == "raggedleft" || t.cs() == "centering" - || t.cs() == "raggedright") { + if (t.cs() == "fboxrule" || t.cs() == "fboxsep" + || t.cs() == "shadowsize" + || t.cs() == "raggedleft" || t.cs() == "centering" + || t.cs() == "raggedright") { if (t.cs() == "fboxrule") fboxrule = ""; if (t.cs() == "fboxsep") @@ -4510,11 +5190,12 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, } else { output_ert_inset(os, t.asInput(), context); } + continue; } //\framebox() is part of the picture environment and different from \framebox{} //\framebox{} will be parsed by parse_outer_box - else if (t.cs() == "framebox") { + if (t.cs() == "framebox") { if (p.next_token().character() == '(') { //the syntax is: \framebox(x,y)[position]{content} string arg = t.asInput(); @@ -4531,11 +5212,12 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, parse_outer_box(p, os, FLAG_ITEM, outer, context, t.cs(), special); } + continue; } //\makebox() is part of the picture environment and different from \makebox{} //\makebox{} will be parsed by parse_box - else if (t.cs() == "makebox") { + if (t.cs() == "makebox") { if (p.next_token().character() == '(') { //the syntax is: \makebox(x,y)[position]{content} string arg = t.asInput(); @@ -4549,20 +5231,22 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, //the syntax is: \makebox[width][position]{content} parse_box(p, os, 0, FLAG_ITEM, outer, context, "", "", t.cs(), "", ""); + continue; } - else if (t.cs() == "smallskip" || - t.cs() == "medskip" || - t.cs() == "bigskip" || - t.cs() == "vfill") { + if (t.cs() == "smallskip" || + t.cs() == "medskip" || + t.cs() == "bigskip" || + t.cs() == "vfill") { context.check_layout(os); begin_inset(os, "VSpace "); os << t.cs(); end_inset(os); skip_spaces_braces(p); + continue; } - else if ((where = is_known(t.cs(), known_spaces))) { + if ((where = is_known(t.cs(), known_spaces))) { context.check_layout(os); begin_inset(os, "space "); os << '\\' << known_coded_spaces[where - known_spaces] @@ -4579,20 +5263,22 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, // remove the braces after "\\,", too. if (t.cs() != " ") skip_braces(p); + continue; } - else if (t.cs() == "newpage" || - (t.cs() == "pagebreak" && !p.hasOpt()) || - t.cs() == "clearpage" || - t.cs() == "cleardoublepage") { + if (t.cs() == "newpage" || + (t.cs() == "pagebreak" && !p.hasOpt()) || + t.cs() == "clearpage" || + t.cs() == "cleardoublepage") { context.check_layout(os); begin_inset(os, "Newpage "); os << t.cs(); end_inset(os); skip_spaces_braces(p); + continue; } - else if (t.cs() == "DeclareRobustCommand" || + if (t.cs() == "DeclareRobustCommand" || t.cs() == "DeclareRobustCommandx" || t.cs() == "newcommand" || t.cs() == "newcommandx" || @@ -4630,9 +5316,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, os << "\n" << ert; end_inset(os); } + continue; } - else if (t.cs() == "let" && p.next_token().asInput() != "*") { + if (t.cs() == "let" && p.next_token().asInput() != "*") { // let could be handled by parse_command(), // but we need to call add_known_command() here. string ert = t.asInput(); @@ -4661,9 +5348,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, if (it != known_commands.end()) known_commands[t.asInput()] = it->second; output_ert_inset(os, ert, context); + continue; } - else if (t.cs() == "hspace" || t.cs() == "vspace") { + if (t.cs() == "hspace" || t.cs() == "vspace") { if (starred) p.get_token(); string name = t.asInput(); @@ -4797,10 +5485,63 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, } else output_ert_inset(os, name + '{' + length + '}', context); } + continue; + } + + // Before we look for the layout name alone below, we check the layouts including the LateXParam, which + // might be one or several options or a star. + // The single '=' is meant here. + if ((newinsetlayout = findInsetLayout(context.textclass, starredname, true, p.getCommandLatexParam()))) { + if (starred) + p.get_token(); + p.skip_spaces(); + context.check_layout(os); + // store the latexparam here. This is eaten in parse_text_in_inset + context.latexparam = newinsetlayout->latexparam(); + docstring name = newinsetlayout->name(); + bool const caption = name.find(from_ascii("Caption:")) == 0; + if (caption) { + // Already done for floating minted listings. + if (minted_float.empty()) { + begin_inset(os, "Caption "); + os << to_utf8(name.substr(8)) << '\n'; + } + } else { + // FIXME: what do we do if the prefix is not Flex: ? + if (prefixIs(name, from_ascii("Flex:"))) + name.erase(0, 5); + begin_inset(os, "Flex "); + os << to_utf8(name) << '\n' + << "status collapsed\n"; + } + if (!minted_float.empty()) { + parse_text_snippet(p, os, FLAG_ITEM, false, context); + } else if (newinsetlayout->isPassThru()) { + // set catcodes to verbatim early, just in case. + p.setCatcodes(VERBATIM_CATCODES); + string delim = p.get_token().asInput(); + if (delim != "{") + cerr << "Warning: bad delimiter for command " << t.asInput() << endl; + //FIXME: handle error condition + string const arg = p.verbatimStuff("}").second; + Context newcontext(true, context.textclass); + if (newinsetlayout->forcePlainLayout()) + newcontext.layout = &context.textclass.plainLayout(); + output_ert(os, arg, newcontext); + } else + parse_text_in_inset(p, os, FLAG_ITEM, false, context, newinsetlayout); + context.latexparam.clear(); + if (caption) + p.skip_spaces(); + // Minted caption insets are not closed here because + // we collect everything into the caption. + if (minted_float.empty()) + end_inset(os); + continue; } // The single '=' is meant here. - else if ((newinsetlayout = findInsetLayout(context.textclass, starredname, true))) { + if ((newinsetlayout = findInsetLayout(context.textclass, starredname, true))) { if (starred) p.get_token(); p.skip_spaces(); @@ -4840,9 +5581,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, // we collect everything into the caption. if (minted_float.empty()) end_inset(os); + continue; } - else if (t.cs() == "includepdf") { + if (t.cs() == "includepdf") { p.skip_spaces(); string const arg = p.getArg('[', ']'); map opts; @@ -4912,9 +5654,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, end_inset(os); context.check_layout(os); registerExternalTemplatePackages("PDFPages"); + continue; } - else if (t.cs() == "loadgame") { + if (t.cs() == "loadgame") { p.skip_spaces(); string name = normalize_filename(p.verbatim_item()); string const path = getMasterFilePath(true); @@ -4947,149 +5690,143 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, if (p.get_token().asInput() == "showboard") p.get_token(); registerExternalTemplatePackages("ChessDiagram"); + continue; } - else { - // try to see whether the string is in unicodesymbols - // Only use text mode commands, since we are in text mode here, - // and math commands may be invalid (bug 6797) - string name = t.asInput(); - // handle the dingbats, cyrillic and greek - if (name == "\\ding" || name == "\\textcyr" || - (name == "\\textgreek" && !preamble.usePolyglossia())) - name = name + '{' + p.getArg('{', '}') + '}'; - // handle the ifsym characters - else if (name == "\\textifsymbol") { - string const optif = p.getFullOpt(); - string const argif = p.getArg('{', '}'); - name = name + optif + '{' + argif + '}'; - } - // handle the \ascii characters - // the case of \ascii within braces, as LyX outputs it, is already - // handled for t.cat() == catBegin - else if (name == "\\ascii") { - // the code is "\asci\xxx" - name = "{" + name + p.get_token().asInput() + "}"; + // try to see whether the string is in unicodesymbols + // Only use text mode commands, since we are in text mode here, + // and math commands may be invalid (bug 6797) + string name = t.asInput(); + // handle the dingbats, cyrillic and greek + if (name == "\\ding" || name == "\\textcyr" || + (name == "\\textgreek" && !preamble.usePolyglossia())) + name = name + '{' + p.getArg('{', '}') + '}'; + // handle the ifsym characters + else if (name == "\\textifsymbol") { + string const optif = p.getFullOpt(); + string const argif = p.getArg('{', '}'); + name = name + optif + '{' + argif + '}'; + } + // handle the \ascii characters + // the case of \ascii within braces, as LyX outputs it, is already + // handled for t.cat() == catBegin + else if (name == "\\ascii") { + // the code is "\asci\xxx" + name = "{" + name + p.get_token().asInput() + "}"; + skip_braces(p); + } + // handle some TIPA special characters + else if (preamble.isPackageUsed("tipa")) { + if (name == "\\s") { + // fromLaTeXCommand() does not yet + // recognize tipa short cuts + name = "\\textsyllabic"; + } else if (name == "\\=" && + p.next_token().asInput() == "*") { + // fromLaTeXCommand() does not yet + // recognize tipa short cuts + p.get_token(); + name = "\\textsubbar"; + } else if (name == "\\textdoublevertline") { + // FIXME: This is not correct, + // \textvertline is higher than \textbardbl + name = "\\textbardbl"; skip_braces(p); - } - // handle some TIPA special characters - else if (preamble.isPackageUsed("tipa")) { - if (name == "\\s") { - // fromLaTeXCommand() does not yet - // recognize tipa short cuts - name = "\\textsyllabic"; - } else if (name == "\\=" && - p.next_token().asInput() == "*") { - // fromLaTeXCommand() does not yet - // recognize tipa short cuts - p.get_token(); - name = "\\textsubbar"; - } else if (name == "\\textdoublevertline") { - // FIXME: This is not correct, - // \textvertline is higher than \textbardbl - name = "\\textbardbl"; + } else if (name == "\\!" ) { + if (p.next_token().asInput() == "b") { + p.get_token(); // eat 'b' + name = "\\texthtb"; + skip_braces(p); + } else if (p.next_token().asInput() == "d") { + p.get_token(); + name = "\\texthtd"; + skip_braces(p); + } else if (p.next_token().asInput() == "g") { + p.get_token(); + name = "\\texthtg"; + skip_braces(p); + } else if (p.next_token().asInput() == "G") { + p.get_token(); + name = "\\texthtscg"; + skip_braces(p); + } else if (p.next_token().asInput() == "j") { + p.get_token(); + name = "\\texthtbardotlessj"; + skip_braces(p); + } else if (p.next_token().asInput() == "o") { + p.get_token(); + name = "\\textbullseye"; skip_braces(p); - } else if (name == "\\!" ) { - if (p.next_token().asInput() == "b") { - p.get_token(); // eat 'b' - name = "\\texthtb"; - skip_braces(p); - } else if (p.next_token().asInput() == "d") { - p.get_token(); - name = "\\texthtd"; - skip_braces(p); - } else if (p.next_token().asInput() == "g") { - p.get_token(); - name = "\\texthtg"; - skip_braces(p); - } else if (p.next_token().asInput() == "G") { - p.get_token(); - name = "\\texthtscg"; - skip_braces(p); - } else if (p.next_token().asInput() == "j") { - p.get_token(); - name = "\\texthtbardotlessj"; - skip_braces(p); - } else if (p.next_token().asInput() == "o") { - p.get_token(); - name = "\\textbullseye"; - skip_braces(p); - } - } else if (name == "\\*" ) { - if (p.next_token().asInput() == "k") { - p.get_token(); - name = "\\textturnk"; - skip_braces(p); - } else if (p.next_token().asInput() == "r") { - p.get_token(); // eat 'b' - name = "\\textturnr"; - skip_braces(p); - } else if (p.next_token().asInput() == "t") { - p.get_token(); - name = "\\textturnt"; - skip_braces(p); - } else if (p.next_token().asInput() == "w") { - p.get_token(); - name = "\\textturnw"; - skip_braces(p); - } } - } - if ((name.size() == 2 && - contains("\"'.=^`bcdHkrtuv~", name[1]) && - p.next_token().asInput() != "*") || - is_known(name.substr(1), known_tipa_marks)) { - // name is a command that corresponds to a - // combining character in unicodesymbols. - // Append the argument, fromLaTeXCommand() - // will either convert it to a single - // character or a combining sequence. - name += '{' + p.verbatim_item() + '}'; - } - // now get the character from unicodesymbols - bool termination; - docstring rem; - set req; - docstring s = normalize_c(encodings.fromLaTeXCommand(from_utf8(name), - Encodings::TEXT_CMD, termination, rem, &req)); - if (!s.empty()) { - context.check_layout(os); - os << to_utf8(s); - if (!rem.empty()) - output_ert_inset(os, to_utf8(rem), context); - if (termination) - skip_spaces_braces(p); - for (set::const_iterator it = req.begin(); it != req.end(); ++it) - preamble.registerAutomaticallyLoadedPackage(*it); - } - //cerr << "#: " << t << " mode: " << mode << endl; - // heuristic: read up to next non-nested space - /* - string s = t.asInput(); - string z = p.verbatim_item(); - while (p.good() && z != " " && !z.empty()) { - //cerr << "read: " << z << endl; - s += z; - z = p.verbatim_item(); - } - cerr << "found ERT: " << s << endl; - output_ert_inset(os, s + ' ', context); - */ - else { - if (t.asInput() == name && - p.next_token().asInput() == "*") { - // Starred commands like \vspace*{} - p.get_token(); // Eat '*' - name += '*'; + } else if (name == "\\*" ) { + if (p.next_token().asInput() == "k") { + p.get_token(); + name = "\\textturnk"; + skip_braces(p); + } else if (p.next_token().asInput() == "r") { + p.get_token(); // eat 'b' + name = "\\textturnr"; + skip_braces(p); + } else if (p.next_token().asInput() == "t") { + p.get_token(); + name = "\\textturnt"; + skip_braces(p); + } else if (p.next_token().asInput() == "w") { + p.get_token(); + name = "\\textturnw"; + skip_braces(p); } - if (!parse_command(name, p, os, outer, context)) - output_ert_inset(os, name, context); } } - - if (flags & FLAG_LEAVE) { - flags &= ~FLAG_LEAVE; - break; + if ((name.size() == 2 && + contains("\"'.=^`bcdHkrtuv~", name[1]) && + p.next_token().asInput() != "*") || + is_known(name.substr(1), known_tipa_marks)) { + // name is a command that corresponds to a + // combining character in unicodesymbols. + // Append the argument, fromLaTeXCommand() + // will either convert it to a single + // character or a combining sequence. + name += '{' + p.verbatim_item() + '}'; + } + // now get the character from unicodesymbols + bool termination; + docstring rem; + set req; + docstring s = normalize_c(encodings.fromLaTeXCommand(from_utf8(name), + Encodings::TEXT_CMD, termination, rem, &req)); + if (!s.empty()) { + context.check_layout(os); + os << to_utf8(s); + if (!rem.empty()) + output_ert_inset(os, to_utf8(rem), context); + if (termination) + skip_spaces_braces(p); + for (set::const_iterator it = req.begin(); it != req.end(); ++it) + preamble.registerAutomaticallyLoadedPackage(*it); + } + //cerr << "#: " << t << " mode: " << mode << endl; + // heuristic: read up to next non-nested space + /* + string s = t.asInput(); + string z = p.verbatim_item(); + while (p.good() && z != " " && !z.empty()) { + //cerr << "read: " << z << endl; + s += z; + z = p.verbatim_item(); + } + cerr << "found ERT: " << s << endl; + output_ert_inset(os, s + ' ', context); + */ + else { + if (t.asInput() == name && + p.next_token().asInput() == "*") { + // Starred commands like \vspace*{} + p.get_token(); // Eat '*' + name += '*'; + } + if (!parse_command(name, p, os, outer, context)) + output_ert_inset(os, name, context); } } } @@ -5167,6 +5904,31 @@ string guessLanguage(Parser & p, string const & lang) return use->first; } + +void check_comment_bib(ostream & os, Context & context) +{ + if (!need_commentbib) + return; + // We have a bibliography database, but no bibliography with biblatex + // which is completely valid. Insert a bibtex inset in a note. + context.check_layout(os); + begin_inset(os, "Note Note\n"); + os << "status open\n"; + os << "\\begin_layout Plain Layout\n"; + begin_command_inset(os, "bibtex", "bibtex"); + string bibfiles; + for (auto const & bf : preamble.biblatex_bibliographies) { + if (!bibfiles.empty()) + bibfiles += ","; + bibfiles += normalize_filename(bf); + } + if (!bibfiles.empty()) + os << "bibfiles " << '"' << bibfiles << '"' << "\n"; + end_inset(os);// Bibtex + os << "\\end_layout\n"; + end_inset(os);// Note +} + // }]) diff --git a/src/update_flags.h b/src/update_flags.h index 3e877c1ca1..a40e88c556 100644 --- a/src/update_flags.h +++ b/src/update_flags.h @@ -21,13 +21,16 @@ namespace Update { /// Recenter the screen around the cursor if is found outside the /// visible area. FitCursor = 1, - /// Force a full screen metrics update. + /// Force a full screen metrics update and a full draw. Force = 2, + /// Force a full redraw (but no metrics computations) + ForceDraw = 4, /// Try to rebreak only the current paragraph metrics. - SinglePar = 4, + /// (currently ignored!) + SinglePar = 8, /// Only the inset decorations need to be redrawn, no text metrics /// update is needed. - Decoration = 8 + Decoration = 16 }; inline flags operator|(flags const f, flags const g) @@ -40,6 +43,11 @@ inline flags operator&(flags const f, flags const g) return static_cast(int(f) & int(g)); } +inline flags operator~(flags const f) +{ + return static_cast(~int(f)); +} + } // namespace Update } // namespace lyx diff --git a/status.23x b/status.23x index 5820acae70..6f5cec884a 100644 --- a/status.23x +++ b/status.23x @@ -17,12 +17,70 @@ What's new - Updated Arabic, Russian and Slovakian user interface localization. +- It possible to anonymize document's content for bug submissions + via buffer-anonymize lfun (bug 7259). + * TEX2LYX IMPROVEMENTS * USER INTERFACE +- Add support for beamer overlay arguments (bug 11068). + +- Update tex2lyx quotation marks detection: + * Consider new quote styles of LyX 2.3. + * Consider changed quote styles in LYX 2.3. + * Try to be a bit smarter with ambiguous quotation marks, + depending on the main quote style and the local context. + +- Consider options passed via \PassOptionsToPackage. + +- Add support for URW Classico, MinionPro and the new Libertine fonts. + +- Add support for \lstinputlisting and \inputminted. + +- Add support for the \t*{} (bottomtiebar) macro of TIPA. + +- Implement better parsing of some command options (via "literate" + function of some insets) (bug 9563). + +- Add support for alignment pseudo-environments as used inside floats + (bug 7857). + + +* USER INTERFACE + +- Overhaul the document painting mechanism. Now the screen is updated + asyncronously (as all normal applications do), which makes LyX + snappier, especially on repeated events. As an added bonus, subpixel + aliasing is honored in the work area. + +- Use native file dialogs on all platforms by default. It is now + possible to switch to LyX custom dialogs (which have extra shortcuts + to relevant directories) by setting the preference + \use_native_filedialog true + +- Handle properly top/bottom of inset with mac-like cursor movement + (bug 10701). + +- Respect the last setting of the 'literal' checkbox when adding citations + via the LyX server (e.g., from JabRef). + +- Allow unification of graphic groups inside marked block via context + menu. + +- Cosmetic polishment of the "Math Options" pane of Document Settings + (bug 10777). + +- UI improvements in the graphics dialog (bug 10771). + +- Set tab stop in preamble editor to four characters. + +- Provide simple search functionality in preamble (bug 11099). + +- Change Settings -> Local Layout to Fixed-width Font and Nowrap (bug 10992). + * DOCUMENTATION AND LOCALIZATION @@ -31,6 +89,9 @@ What's new - Add support for v.2 of the enchant spell checker (bug 10986). +- Support new hunspell C++ ABI if LyX is built against hunspell >= 1.5 + (bug 10547). + ** Bug fixes: @@ -51,6 +112,21 @@ What's new - Fix detection of Open Document files. +- Fix language settings and line spacing in InPreamble-titles + (bug 9332, 1049). + +- Respect 'literal' setting when calculating longest bibitem (bug 10817). + +- Do not embrace numbers in \beginL ... \endL with polyglossia in Right- + to-Left languages, since bidi handles the numbers automatically. + +- Fix polyglossia language switches for Arabic (bug 11057). + +- Set correct path to (biblatex) bibliography databases that are entered + relative to child documents (bug 11105). + +- Load hyperref with a suitable driver (bug 6418). + * LYX2LYX @@ -59,7 +135,8 @@ What's new - Fix "Paste from LaTeX" on Windows (bug 9139). -- Disable BUFFER_EXPORT and BUFFER_EXPORT_AS while buffer is processed (bug 8338). +- Disable BUFFER_EXPORT and BUFFER_EXPORT_AS while buffer is processed + (bug 8338). - Disable CheckTeX while buffer is processed (bug 7434). @@ -84,10 +161,36 @@ What's new - Do not show errors from master when compiling child (bug 11106). +- Fix crash with server-get-xy and tall inset (bug 8120). + +- Improve Undo for operations that act on several buffers (bug 10823). + +- Improve rendering of square roots in math editor (bug 10814). + +- Fix display of citation labels when pasting from a document + with other citation type (bug 10829). + +- Save the list of recent files when a file is open/saved so that it + is up to date after a crash (bug 10712). + +- Only show header for bibliography in outliner (bug 11045). + +- Take actual font height into account when drawing placeholder box + for graphics (bug 11048). + +- Correctly set default bibliography style in the Document Settings + dialog (bug 11088). + +- Assure that the External Inset dialog is opened at first tab for + new insets (bug 11081). + +- Ignore deleted material when generating a proposed label (bug 11102). + * INTERNALS -- Fix bug that TeX files were not detected when reconfiguring LyX (bug 11053). +- Fix bug that TeX files were not detected when reconfiguring LyX + (bug 11053). * DOCUMENTATION AND LOCALIZATION @@ -107,6 +210,20 @@ What's new - Fix import of flalign* environment (bug 10501). +- Fix import of subfloats without caption (bug 10385). + +- Import straight quotations marks (e.g. babel shorthands) as ERT (bug 75). + +- Do not add duplicate \makebeamertitle. + +- Keep empty paragraph it keepempty is true (bug 11078). + +- Fix parsing issue in nested CJK (bug 9562). + +- Fix import of package options with comments (bug 5737). + +- Fix import of xymatrix (bug 10638). + * ADVANCED FIND AND REPLACE