]> git.lyx.org Git - lyx.git/blob - lib/lyx2lyx/lyx_2_1.py
Speed up convert_captionlayouts. Part of #11200.
[lyx.git] / lib / lyx2lyx / lyx_2_1.py
1 # -*- coding: utf-8 -*-
2 # This file is part of lyx2lyx
3 # Copyright (C) 2011 The LyX team
4 #
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18
19 """ Convert files to the file format generated by LyX 2.1"""
20
21 import re, string
22 import unicodedata
23 import sys, os
24
25 # Uncomment only what you need to import, please.
26
27 from parser_tools import count_pars_in_inset, del_token, find_token, find_token_exact, \
28     find_token_backwards, find_end_of, find_end_of_inset, find_end_of_layout, \
29     find_end_of_sequence, find_re, get_option_value, get_containing_layout, \
30     get_containing_inset, get_value, get_quoted_value, set_option_value
31
32 #from parser_tools import find_token, find_end_of, find_tokens, \
33   #find_end_of_inset, find_end_of_layout, \
34   #is_in_inset, del_token, check_token
35
36 from lyx2lyx_tools import add_to_preamble, put_cmd_in_ert, get_ert
37
38 #from lyx2lyx_tools import insert_to_preamble, \
39 #  lyx2latex, latex_length, revert_flex_inset, \
40 #  revert_font_attrs, hex2ratio, str2bool
41
42 ####################################################################
43 # Private helper functions
44
45 #def remove_option(lines, m, option):
46     #''' removes option from line m. returns whether we did anything '''
47     #l = lines[m].find(option)
48     #if l == -1:
49         #return False
50     #val = lines[m][l:].split('"')[1]
51     #lines[m] = lines[m][:l - 1] + lines[m][l+len(option + '="' + val + '"'):]
52     #return True
53
54
55 def revert_Argument_to_TeX_brace(document, line, endline, n, nmax, environment, opt):
56     '''
57     Reverts an InsetArgument to TeX-code
58     usage:
59     revert_Argument_to_TeX_brace(document, LineOfBegin, LineOfEnd, StartArgument, EndArgument, isEnvironment, isOpt)
60     LineOfBegin is the line  of the \\begin_layout or \\begin_inset statement
61     LineOfEnd is the line  of the \end_layout or \end_inset statement, if "0" is given, the end of the file is used instead
62     StartArgument is the number of the first argument that needs to be converted
63     EndArgument is the number of the last argument that needs to be converted or the last defined one
64     isEnvironment must be true, if the layout is for a LaTeX environment
65     isOpt must be true, if the argument is an optional one
66     '''
67     lineArg = 0
68     wasOpt = False
69     while lineArg != -1 and n < nmax + 1:
70       lineArg = find_token(document.body, "\\begin_inset Argument " + str(n), line)
71       if lineArg > endline and endline != 0:
72         return wasOpt
73       if lineArg != -1:
74         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
75         # we have to assure that no other inset is in the Argument
76         beginInset = find_token(document.body, "\\begin_inset", beginPlain)
77         endInset = find_token(document.body, "\\end_inset", beginPlain)
78         k = beginPlain + 1
79         l = k
80         while beginInset < endInset and beginInset != -1:
81           beginInset = find_token(document.body, "\\begin_inset", k)
82           endInset = find_token(document.body, "\\end_inset", l)
83           k = beginInset + 1
84           l = endInset + 1
85         if environment == False:
86           if opt == False:
87             document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}{")
88             del(document.body[lineArg : beginPlain + 1])
89             wasOpt = False
90           else:
91             document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
92             document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("[")
93             wasOpt = True
94         else:
95           document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
96           document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("{")
97           wasOpt = False
98         n += 1
99     return wasOpt
100
101
102 def convert_TeX_brace_to_Argument(document, line, n, nmax, inset, environment, opt):
103     '''
104     Converts TeX code for mandatory arguments to an InsetArgument
105     The conversion of TeX code for optional arguments must be done with another routine
106     !!! Be careful if the braces are different in your case as expected here:
107     - "}{" separates mandatory arguments of commands
108     - "}" + "{" separates mandatory arguments of commands
109     - "}" + " " + "{" separates mandatory arguments of commands
110     - { and } surround a mandatory argument of an environment
111     usage:
112     convert_TeX_brace_to_Argument(document, LineOfBeginLayout/Inset, StartArgument, EndArgument, isInset, isEnvironment, isOpt)
113     LineOfBeginLayout/Inset is the line  of the \\begin_layout or \\begin_inset statement
114     StartArgument is the number of the first ERT that needs to be converted
115     EndArgument is the number of the last ERT that needs to be converted
116     isInset must be true, if braces inside an InsetLayout needs to be converted
117     isEnvironment must be true, if the layout is for a LaTeX environment
118     isOpt must be true, if the argument is an optional one
119
120     Todo: this routine can currently handle only one mandatory argument of environments
121     '''
122
123     end_layout = find_end_of_layout(document.body, line)
124     lineERT = line
125     endn = line
126     loop = 1
127     while n < nmax + 1:
128       lineERT = find_token(document.body, "\\begin_inset ERT", lineERT, end_layout)
129       if lineERT == -1:
130         break
131       if environment == False:
132         end_ERT = find_end_of_inset(document.body, lineERT)
133         if end_ERT == -1:
134           document.warning("Can't find end of ERT!!")
135           break
136         # Note that this only checks for ][ or }{ at the beginning of a line
137         if opt:
138           bracePair = find_token(document.body, "][", lineERT, end_ERT)
139         else:
140           bracePair = find_token(document.body, "}{", lineERT, end_ERT)
141         if bracePair != -1:
142           end = find_token(document.body, "\\end_inset", bracePair)
143           document.body[lineERT : end_ERT + 1] = ["\\end_layout", "", "\\end_inset"]
144           if loop == 1:
145             # in the case that n > 1 we have optional arguments before
146             # therefore detect them if any
147             if n > 1:
148               # first check if there is an argument
149               lineArg = find_token(document.body, "\\begin_inset Argument", line)
150               if lineArg < lineERT and lineArg != -1:
151                 # we have an argument, so now search backwards for its end
152                 # we must now assure that we don't find other insets like e.g. a newline
153                 endInsetArg = lineERT
154                 endLayoutArg = endInsetArg
155                 while endInsetArg != endLayoutArg + 2 and endInsetArg != -1:
156                   endInsetArg = endInsetArg - 1
157                   endLayoutArg = endInsetArg
158                   endInsetArg = find_token_backwards(document.body, "\\end_inset", endInsetArg)
159                   endLayoutArg = find_token_backwards(document.body, "\\end_layout", endLayoutArg)
160                 line = endInsetArg + 1
161             if inset == False:
162               document.body[line + 1 : line + 1] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
163             else:
164               document.body[line + 4 : line + 4] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
165           else: # if loop != 1
166             document.body[endn : endn] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
167           n += 1
168           endn = end
169           loop += 1
170         else:
171           # no brace pair found
172           # now check the case that we have "}" + "{" in two ERTs
173           if opt:
174             endBrace = find_token(document.body, "]", lineERT, end_layout)
175           else:
176             endBrace = find_token(document.body, "}", lineERT, end_layout)
177           if endBrace == lineERT + 5:
178             if opt:
179               beginBrace = find_token(document.body, "[", endBrace, end_layout)
180             else:
181               beginBrace = find_token(document.body, "{", endBrace, end_layout)
182             # assure that the ERTs are consecutive (11 or 12 depending if there is a space between the ERTs or not)
183             if beginBrance != -1 and (beginBrace == endBrace + 11 or beginBrace == endBrace + 12):
184               end = find_token(document.body, "\\end_inset", beginBrace)
185               document.body[lineERT : end + 1] = ["\\end_layout", "", "\\end_inset"]
186               if loop == 1:
187                 # in the case that n > 1 we have optional arguments before
188                 # therefore detect them if any
189                 if n > 1:
190                   # first check if there is an argument
191                   lineArg = find_token(document.body, "\\begin_inset Argument", line)
192                   if lineArg < lineERT and lineArg != -1:
193                     # we have an argument, so now search backwards for its end
194                     # we must now assure that we don't find other insets like e.g. a newline
195                     endInsetArg = lineERT
196                     endLayoutArg = endInsetArg
197                     while endInsetArg != endLayoutArg + 2 and endInsetArg != -1:
198                       endInsetArg = endInsetArg - 1
199                       endLayoutArg = endInsetArg
200                       endInsetArg = find_token_backwards(document.body, "\\end_inset", endInsetArg)
201                       endLayoutArg = find_token_backwards(document.body, "\\end_layout", endLayoutArg)
202                     line = endInsetArg + 1
203                 if inset == False:
204                   document.body[line + 1 : line + 1] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
205                 else:
206                   document.body[line + 4 : line + 4] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
207               else:
208                 document.body[endn : endn] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
209               n += 1
210               loop += 1
211               # set the line where the next argument will be inserted
212               if beginBrace == endBrace + 11:
213                 endn = end - 11
214               else:
215                 endn = end - 12
216             else:
217               lineERT += 1
218           else:
219             lineERT += 1
220       if environment == True:
221         # FIXME This version of the routine does not check for and pass over
222         # arguments before n. So it attempts to process the argument in the
223         # document, no matter what has been specified.
224         #
225         # The other branch does do that, but probably that code would be better
226         # in a single location: Skip all those arguments, then process the ones
227         # we want.
228         end_ERT = find_end_of_inset(document.body, lineERT)
229         if end_ERT == -1:
230           document.warning("Can't find end of ERT!!")
231           break
232         # Note that this only checks for [ or { at the beginning of a line
233         if opt:
234           opening = find_token(document.body, "[", lineERT, end_ERT)
235         else:
236           opening = find_token(document.body, "{", lineERT, end_ERT)
237         if opening != -1:
238           lineERT2 = find_token(document.body, "\\begin_inset ERT", end_ERT, end_layout)
239           if lineERT2 == -1:
240             # argument in a single ERT
241             # strip off the opening bracket
242             document.body[opening] = document.body[opening][1:]
243             ertcontlastline = end_ERT - 3
244             if (opt and document.body[ertcontlastline].endswith("]")) or document.body[ertcontlastline].endswith("}"):
245               # strip off the closing bracket
246               document.body[ertcontlastline] = document.body[ertcontlastline][:-1]
247               end2 = find_token(document.body, "\\end_inset", ertcontlastline)
248               document.body[lineERT : lineERT + 1] = ["\\begin_inset Argument " + str(n)]
249           else:
250             end_ERT2 = find_end_of_inset(document.body, lineERT2)
251             if end_ERT2 == -1:
252               document.warning("Can't find end of second ERT!!")
253               break
254             if opt:
255               closing = find_token(document.body, "]", lineERT2, end_ERT2)
256             else:
257               closing = find_token(document.body, "}", lineERT2, end_ERT2)
258             if closing != -1: # assure that the "}" is in this ERT
259               end2 = find_token(document.body, "\\end_inset", closing)
260               document.body[lineERT2 : end2 + 1] = ["\\end_layout", "", "\\end_inset"]
261             document.body[lineERT : end_ERT + 1] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
262           n += 1
263         else:
264           document.warning("Unable to process argument!")
265           n += 1
266
267
268 ###############################################################################
269 ###
270 ### Conversion and reversion routines
271 ###
272 ###############################################################################
273
274 def revert_visible_space(document):
275     "Revert InsetSpace visible into its ERT counterpart"
276     i = 0
277     while True:
278       i = find_token(document.body, "\\begin_inset space \\textvisiblespace{}", i)
279       if i == -1:
280         return
281       end = find_end_of_inset(document.body, i)
282       subst = put_cmd_in_ert("\\textvisiblespace{}")
283       document.body[i:end + 1] = subst
284
285
286 undertilde_commands = ["utilde"]
287 def convert_undertilde(document):
288     " Load undertilde automatically "
289     i = find_token(document.header, "\\use_mathdots" , 0)
290     if i == -1:
291         i = find_token(document.header, "\\use_mhchem" , 0)
292     if i == -1:
293         i = find_token(document.header, "\\use_esint" , 0)
294     if i == -1:
295         document.warning("Malformed LyX document: Can't find \\use_mathdots.")
296         return;
297     j = find_token(document.preamble, "\\usepackage{undertilde}", 0)
298     if j != -1:
299         # package was loaded in the preamble, convert this to header setting for round trip
300         document.header.insert(i + 1, "\\use_undertilde 2") # on
301         del document.preamble[j]
302     else:
303         j = 0
304         while True:
305             j = find_token(document.body, '\\begin_inset Formula', j)
306             if j == -1:
307                 break
308             k = find_end_of_inset(document.body, j)
309             if k == -1:
310                 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(j))
311                 j += 1
312                 continue
313             code = "\n".join(document.body[j:k])
314             for c in undertilde_commands:
315                 if code.find("\\%s" % c) != -1:
316                     # at least one of the commands was found - need to switch package off
317                     document.header.insert(i + 1, "\\use_undertilde 0") # off
318                     return
319             j = k
320         # no command was found - set to auto (bug 9069)
321         document.header.insert(i + 1, "\\use_undertilde 1") # auto
322
323
324
325 def revert_undertilde(document):
326     " Load undertilde if used in the document "
327     regexp = re.compile(r'(\\use_undertilde)')
328     i = find_re(document.header, regexp, 0)
329     value = "1" # default is auto
330     if i != -1:
331         value = get_value(document.header, "\\use_undertilde" , i).split()[0]
332         del document.header[i]
333     if value == "2": # on
334         add_to_preamble(document, ["\\usepackage{undertilde}"])
335     elif value == "1": # auto
336         i = 0
337         while True:
338             i = find_token(document.body, '\\begin_inset Formula', i)
339             if i == -1:
340                 return
341             j = find_end_of_inset(document.body, i)
342             if j == -1:
343                 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
344                 i += 1
345                 continue
346             code = "\n".join(document.body[i:j])
347             for c in undertilde_commands:
348                 if code.find("\\%s" % c) != -1:
349                     add_to_preamble(document, ["\\usepackage{undertilde}"])
350                     return
351             i = j
352
353
354 def revert_negative_space(document):
355     "Revert InsetSpace negmedspace and negthickspace into its TeX-code counterpart"
356     i = 0
357     j = 0
358     reverted = False
359     while True:
360       i = find_token(document.body, "\\begin_inset space \\negmedspace{}", i)
361       if i == -1:
362         j = find_token(document.body, "\\begin_inset space \\negthickspace{}", j)
363         if j == -1:
364           # load amsmath in the preamble if not already loaded if we are at the end of checking
365           if reverted == True:
366             i = find_token(document.header, "\\use_amsmath 2", 0)
367             if i == -1:
368               add_to_preamble(document, ["\\@ifundefined{negthickspace}{\\usepackage{amsmath}}"])
369           return
370       if i == -1:
371         return
372       end = find_end_of_inset(document.body, i)
373       subst = put_cmd_in_ert("\\negmedspace{}")
374       document.body[i:end + 1] = subst
375       j = find_token(document.body, "\\begin_inset space \\negthickspace{}", j)
376       if j == -1:
377         return
378       end = find_end_of_inset(document.body, j)
379       subst = put_cmd_in_ert("\\negthickspace{}")
380       document.body[j:end + 1] = subst
381       reverted = True
382
383
384 def revert_math_spaces(document):
385     "Revert formulas with protected custom space and protected hfills to TeX-code"
386     i = 0
387     while True:
388       i = find_token(document.body, "\\begin_inset Formula", i)
389       if i == -1:
390         return
391       j = document.body[i].find("\\hspace*")
392       if j != -1:
393         end = find_end_of_inset(document.body, i)
394         subst = put_cmd_in_ert(document.body[i][21:])
395         document.body[i:end + 1] = subst
396       i += 1
397
398
399 def convert_japanese_encodings(document):
400     " Rename the japanese encodings to names understood by platex "
401     jap_enc_dict = {
402         "EUC-JP-pLaTeX": "euc",
403         "JIS-pLaTeX":    "jis",
404         "SJIS-pLaTeX":   "sjis"
405     }
406     i = find_token(document.header, "\\inputencoding" , 0)
407     if i == -1:
408         return
409     val = get_value(document.header, "\\inputencoding", i)
410     if val in list(jap_enc_dict.keys()):
411         document.header[i] = "\\inputencoding %s" % jap_enc_dict[val]
412
413
414 def revert_japanese_encodings(document):
415     " Revert the japanese encodings name changes "
416     jap_enc_dict = {
417         "euc":  "EUC-JP-pLaTeX",
418         "jis":  "JIS-pLaTeX",
419         "sjis": "SJIS-pLaTeX"
420     }
421     i = find_token(document.header, "\\inputencoding" , 0)
422     if i == -1:
423         return
424     val = get_value(document.header, "\\inputencoding", i)
425     if val in list(jap_enc_dict.keys()):
426         document.header[i] = "\\inputencoding %s" % jap_enc_dict[val]
427
428
429 def convert_justification(document):
430     " Add the \\justification buffer param"
431     i = find_token(document.header, "\\suppress_date" , 0)
432     if i == -1:
433         i = find_token(document.header, "\\paperorientation" , 0)
434     if i == -1:
435         i = find_token(document.header, "\\use_indices" , 0)
436     if i == -1:
437         i = find_token(document.header, "\\use_bibtopic" , 0)
438     if i == -1:
439         document.warning("Malformed LyX document: Missing \\suppress_date.")
440         return
441     document.header.insert(i + 1, "\\justification true")
442
443
444 def revert_justification(document):
445     " Revert the \\justification buffer param"
446     if not del_token(document.header, '\\justification', 0):
447         document.warning("Malformed LyX document: Missing \\justification.")
448
449
450 def revert_australian(document):
451     "Set English language variants Australian and Newzealand to English"
452
453     if document.language == "australian" or document.language == "newzealand":
454         document.language = "english"
455         i = find_token(document.header, "\\language", 0)
456         if i != -1:
457             document.header[i] = "\\language english"
458     j = 0
459     while True:
460         j = find_token(document.body, "\\lang australian", j)
461         if j == -1:
462             j = find_token(document.body, "\\lang newzealand", 0)
463             if j == -1:
464                 return
465             else:
466                 document.body[j] = document.body[j].replace("\\lang newzealand", "\\lang english")
467         else:
468             document.body[j] = document.body[j].replace("\\lang australian", "\\lang english")
469         j += 1
470
471
472 def convert_biblio_style(document):
473     "Add a sensible default for \\biblio_style based on the citation engine."
474     i = find_token(document.header, "\\cite_engine", 0)
475     if i != -1:
476         engine = get_value(document.header, "\\cite_engine", i).split("_")[0]
477         style = {"basic": "plain", "natbib": "plainnat", "jurabib": "jurabib"}
478         document.header.insert(i + 1, "\\biblio_style " + style[engine])
479
480
481 def revert_biblio_style(document):
482     "BibTeX insets with default option use the style defined by \\biblio_style."
483     i = find_token(document.header, "\\biblio_style" , 0)
484     if i == -1:
485         document.warning("No \\biblio_style line. Nothing to do.")
486         return
487
488     default_style = get_value(document.header, "\\biblio_style", i)
489     del document.header[i]
490
491     # We are looking for bibtex insets having the default option
492     i = 0
493     while True:
494         i = find_token(document.body, "\\begin_inset CommandInset bibtex", i)
495         if i == -1:
496             return
497         j = find_end_of_inset(document.body, i)
498         if j == -1:
499             document.warning("Malformed LyX document: Can't find end of bibtex inset at line " + str(i))
500             i += 1
501             return
502         k = find_token(document.body, "options", i, j)
503         if k != -1:
504             options = get_quoted_value(document.body, "options", k)
505             if "default" in options.split(","):
506                 document.body[k] = 'options "%s"' \
507                     % options.replace("default", default_style)
508         i = j
509
510
511 def handle_longtable_captions(document, forward):
512     begin_table = 0
513     while True:
514         begin_table = find_token(document.body, '<lyxtabular version=', begin_table)
515         if begin_table == -1:
516             break
517         end_table = find_end_of(document.body, begin_table, '<lyxtabular', '</lyxtabular>')
518         if end_table == -1:
519             document.warning("Malformed LyX document: Could not find end of table.")
520             begin_table += 1
521             continue
522         fline = find_token(document.body, "<features", begin_table, end_table)
523         if fline == -1:
524             document.warning("Can't find features for inset at line " + str(begin_table))
525             begin_table += 1
526             continue
527         p = document.body[fline].find("islongtable")
528         if p == -1:
529             # no longtable
530             begin_table += 1
531             continue
532         numrows = get_option_value(document.body[begin_table], "rows")
533         try:
534             numrows = int(numrows)
535         except:
536             document.warning(document.body[begin_table])
537             document.warning("Unable to determine rows!")
538             begin_table = end_table
539             continue
540         begin_row = begin_table
541         for row in range(numrows):
542             begin_row = find_token(document.body, '<row', begin_row, end_table)
543             if begin_row == -1:
544                 document.warning("Can't find row " + str(row + 1))
545                 break
546             end_row = find_end_of(document.body, begin_row, '<row', '</row>')
547             if end_row == -1:
548                 document.warning("Can't find end of row " + str(row + 1))
549                 break
550             if forward:
551                 if (get_option_value(document.body[begin_row], 'caption') == 'true' and
552                     get_option_value(document.body[begin_row], 'endfirsthead') != 'true' and
553                     get_option_value(document.body[begin_row], 'endhead') != 'true' and
554                     get_option_value(document.body[begin_row], 'endfoot') != 'true' and
555                     get_option_value(document.body[begin_row], 'endlastfoot') != 'true'):
556                     document.body[begin_row] = set_option_value(document.body[begin_row], 'caption', 'true", endfirsthead="true')
557             elif get_option_value(document.body[begin_row], 'caption') == 'true':
558                 if get_option_value(document.body[begin_row], 'endfirsthead') == 'true':
559                     document.body[begin_row] = set_option_value(document.body[begin_row], 'endfirsthead', 'false')
560                 if get_option_value(document.body[begin_row], 'endhead') == 'true':
561                     document.body[begin_row] = set_option_value(document.body[begin_row], 'endhead', 'false')
562                 if get_option_value(document.body[begin_row], 'endfoot') == 'true':
563                     document.body[begin_row] = set_option_value(document.body[begin_row], 'endfoot', 'false')
564                 if get_option_value(document.body[begin_row], 'endlastfoot') == 'true':
565                     document.body[begin_row] = set_option_value(document.body[begin_row], 'endlastfoot', 'false')
566             begin_row = end_row
567         # since there could be a tabular inside this one, we
568         # cannot jump to end.
569         begin_table += 1
570
571
572 def convert_longtable_captions(document):
573     "Add a firsthead flag to caption rows"
574     handle_longtable_captions(document, True)
575
576
577 def revert_longtable_captions(document):
578     "remove head/foot flag from caption rows"
579     handle_longtable_captions(document, False)
580
581
582 def convert_use_packages(document):
583     "use_xxx yyy => use_package xxx yyy"
584     packages = ["amsmath", "esint", "mathdots", "mhchem", "undertilde"]
585     for p in packages:
586         i = find_token(document.header, "\\use_%s" % p, 0)
587         if i != -1:
588             value = get_value(document.header, "\\use_%s" % p, i)
589             document.header[i] = "\\use_package %s %s" % (p, value)
590
591
592 def revert_use_packages(document):
593     "use_package xxx yyy => use_xxx yyy"
594     packages = ["amsmath", "esint", "mhchem", "mathdots", "undertilde"]
595     # the order is arbitrary for the use_package version, and not all packages need to be given.
596     # Ensure a complete list and correct order (important for older LyX versions and especially lyx2lyx)
597     # first loop: find line with first package
598     j = -1
599     for p in packages:
600         regexp = re.compile(r'(\\use_package\s+%s)' % p)
601         i = find_re(document.header, regexp, 0)
602         if i != -1 and (j < 0 or i < j):
603             j = i
604     # second loop: replace or insert packages in front of all existing ones
605     for p in packages:
606         regexp = re.compile(r'(\\use_package\s+%s)' % p)
607         i = find_re(document.header, regexp, 0)
608         if i != -1:
609             value = get_value(document.header, "\\use_package %s" % p, i).split()[1]
610             del document.header[i]
611             document.header.insert(j, "\\use_%s %s" % (p, value))
612         else:
613             document.header.insert(j, "\\use_%s 1" % p)
614         j += 1
615
616
617 def convert_use_package(document, pkg, commands, oldauto):
618     # oldauto defines how the version we are converting from behaves:
619     # if it is true, the old version uses the package automatically.
620     # if it is false, the old version never uses the package.
621     i = find_token(document.header, "\\use_package", 0)
622     if i == -1:
623         document.warning("Malformed LyX document: Can't find \\use_package.")
624         return;
625     j = find_token(document.preamble, "\\usepackage{" + pkg + "}", 0)
626     if j != -1:
627         # package was loaded in the preamble, convert this to header setting for round trip
628         document.header.insert(i + 1, "\\use_package " + pkg + " 2") # on
629         del document.preamble[j]
630     # If oldauto is true we have two options:
631     # We can either set the package to auto - this is correct for files in
632     # format 425 to 463, and may create a conflict for older files which use
633     # any command in commands with a different definition.
634     # Or we can look whether any command in commands is used, and set it to
635     # auto if not and to off if yes. This will not create a conflict, but will
636     # create uncompilable documents for files in format 425 to 463, which use
637     # any command in commands.
638     # We choose the first option since its error is less likely.
639     elif oldauto:
640         document.header.insert(i + 1, "\\use_package " + pkg + " 1") # auto
641     else:
642         j = 0
643         while True:
644             j = find_token(document.body, '\\begin_inset Formula', j)
645             if j == -1:
646                 break
647             k = find_end_of_inset(document.body, j)
648             if k == -1:
649                 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(j))
650                 j += 1
651                 continue
652             code = "\n".join(document.body[j:k])
653             for c in commands:
654                 if code.find("\\%s" % c) != -1:
655                     # at least one of the commands was found - need to switch package off
656                     document.header.insert(i + 1, "\\use_package " + pkg + " 0") # off
657                     return
658             j = k
659         # no command was found - set to auto (bug 9069)
660         document.header.insert(i + 1, "\\use_package " + pkg + " 1") # auto
661
662
663 def revert_use_package(document, pkg, commands, oldauto):
664     # oldauto defines how the version we are reverting to behaves:
665     # if it is true, the old version uses the package automatically.
666     # if it is false, the old version never uses the package.
667     regexp = re.compile(r'(\\use_package\s+%s)' % pkg)
668     i = find_re(document.header, regexp, 0)
669     value = "1" # default is auto
670     if i != -1:
671         value = get_value(document.header, "\\use_package" , i).split()[1]
672         del document.header[i]
673     if value == "2": # on
674         add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
675     elif value == "1" and not oldauto: # auto
676         i = 0
677         while True:
678             i = find_token(document.body, '\\begin_inset Formula', i)
679             if i == -1:
680                 return
681             j = find_end_of_inset(document.body, i)
682             if j == -1:
683                 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
684                 i += 1
685                 continue
686             code = "\n".join(document.body[i:j])
687             for c in commands:
688                 if code.find("\\%s" % c) != -1:
689                     add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
690                     return
691             i = j
692
693
694 mathtools_commands = ["mathclap", "mathllap", "mathrlap", \
695                 "lgathered", "rgathered", "vcentcolon", "dblcolon", \
696                 "coloneqq", "Coloneqq", "coloneq", "Coloneq", "eqqcolon", \
697                 "Eqqcolon", "eqcolon", "Eqcolon", "colonapprox", \
698                 "Colonapprox", "colonsim", "Colonsim"]
699 def convert_use_mathtools(document):
700     "insert use_package mathtools"
701     convert_use_package(document, "mathtools", mathtools_commands, False)
702
703
704 def revert_use_mathtools(document):
705     "remove use_package mathtools"
706     revert_use_package(document, "mathtools", mathtools_commands, False)
707
708
709 # commands provided by stmaryrd.sty but LyX uses other packages:
710 # boxdot lightning, bigtriangledown, bigtriangleup
711 stmaryrd_commands = ["shortleftarrow", "shortrightarrow", "shortuparrow", \
712                     "shortdownarrow", "Yup", "Ydown", "Yleft", "Yright", \
713                     "varcurlyvee", "varcurlywedge", "minuso", "baro", \
714                     "sslash", "bbslash", "moo", "varotimes", "varoast", \
715                     "varobar", "varodot", "varoslash", "varobslash", \
716                     "varocircle", "varoplus", "varominus", "boxast", \
717                     "boxbar", "boxslash", "boxbslash", "boxcircle", \
718                     "boxbox", "boxempty", "merge", "vartimes", \
719                     "fatsemi", "sswarrow", "ssearrow", "curlywedgeuparrow", \
720                     "curlywedgedownarrow", "fatslash", "fatbslash", "lbag", \
721                     "rbag", "varbigcirc", "leftrightarroweq", \
722                     "curlyveedownarrow", "curlyveeuparrow", "nnwarrow", \
723                     "nnearrow", "leftslice", "rightslice", "varolessthan", \
724                     "varogreaterthan", "varovee", "varowedge", "talloblong", \
725                     "interleave", "obar", "obslash", "olessthan", \
726                     "ogreaterthan", "ovee", "owedge", "oblong", "inplus", \
727                     "niplus", "nplus", "subsetplus", "supsetplus", \
728                     "subsetpluseq", "supsetpluseq", "Lbag", "Rbag", \
729                     "llbracket", "rrbracket", "llparenthesis", \
730                     "rrparenthesis", "binampersand", "bindnasrepma", \
731                     "trianglelefteqslant", "trianglerighteqslant", \
732                     "ntrianglelefteqslant", "ntrianglerighteqslant", \
733                     "llfloor", "rrfloor", "llceil", "rrceil", "arrownot", \
734                     "Arrownot", "Mapstochar", "mapsfromchar", "Mapsfromchar", \
735                     "leftrightarrowtriangle", "leftarrowtriangle", \
736                     "rightarrowtriangle", \
737                     "bigcurlyvee", "bigcurlywedge", "bigsqcap", "bigbox", \
738                     "bigparallel", "biginterleave", "bignplus", \
739                     "varcopyright", "longarrownot", "Longarrownot", \
740                     "Mapsto", "mapsfrom", "Mapsfrom" "Longmapsto", \
741                     "longmapsfrom", "Longmapsfrom"]
742 def convert_use_stmaryrd(document):
743     "insert use_package stmaryrd"
744     convert_use_package(document, "stmaryrd", stmaryrd_commands, False)
745
746
747 def revert_use_stmaryrd(document):
748     "remove use_package stmaryrd"
749     revert_use_package(document, "stmaryrd", stmaryrd_commands, False)
750
751
752 stackrel_commands = ["stackrel"]
753 def convert_use_stackrel(document):
754     "insert use_package stackrel"
755     convert_use_package(document, "stackrel", stackrel_commands, False)
756
757
758 def revert_use_stackrel(document):
759     "remove use_package stackrel"
760     revert_use_package(document, "stackrel", stackrel_commands, False)
761
762
763 def convert_cite_engine_type(document):
764     "Determine the \\cite_engine_type from the citation engine."
765     i = find_token(document.header, "\\cite_engine", 0)
766     if i == -1:
767         return
768     engine = get_value(document.header, "\\cite_engine", i)
769     if "_" in engine:
770         engine, type = engine.split("_")
771     else:
772         type = {"basic": "numerical", "jurabib": "authoryear"}[engine]
773     document.header[i] = "\\cite_engine " + engine
774     document.header.insert(i + 1, "\\cite_engine_type " + type)
775
776
777 def revert_cite_engine_type(document):
778     "Natbib had the type appended with an underscore."
779     engine_type = "numerical"
780     i = find_token(document.header, "\\cite_engine_type" , 0)
781     if i == -1:
782         document.warning("No \\cite_engine_type line. Assuming numerical.")
783     else:
784         engine_type = get_value(document.header, "\\cite_engine_type", i)
785         del document.header[i]
786
787     # We are looking for the natbib citation engine
788     i = find_token(document.header, "\\cite_engine natbib", 0)
789     if i == -1:
790         return
791     document.header[i] = "\\cite_engine natbib_" + engine_type
792
793
794 def convert_cite_engine_type_default(document):
795     "Convert \\cite_engine_type to default for the basic citation engine."
796     i = find_token(document.header, "\\cite_engine basic", 0)
797     if i == -1:
798         return
799     i = find_token(document.header, "\\cite_engine_type" , 0)
800     if i == -1:
801         return
802     document.header[i] = "\\cite_engine_type default"
803
804
805 def revert_cite_engine_type_default(document):
806     """Revert \\cite_engine_type default.
807
808     Revert to numerical for the basic cite engine, otherwise to authoryear."""
809     engine_type = "authoryear"
810     i = find_token(document.header, "\\cite_engine_type default" , 0)
811     if i == -1:
812         return
813     j = find_token(document.header, "\\cite_engine basic", 0)
814     if j != -1:
815         engine_type = "numerical"
816     document.header[i] = "\\cite_engine_type " + engine_type
817
818
819 cancel_commands = ["cancel", "bcancel", "xcancel", "cancelto"]
820 # this is the same, as revert_use_cancel() except for the default
821 def revert_cancel(document):
822     "add cancel to the preamble if necessary"
823     revert_use_package(document, "cancel", cancel_commands, False)
824
825
826 def revert_verbatim(document, starred = False):
827     " Revert verbatim environments completely to TeX-code. "
828     i = 0
829     consecutive = False
830
831     layout_name = "Verbatim"
832     latex_name  = "verbatim"
833     if starred:
834         layout_name = "Verbatim*"
835         latex_name  = "verbatim*"
836
837     subst_end = ['\\end_layout', '', '\\begin_layout Plain Layout',
838                  '\\end_layout', '',
839                  '\\begin_layout Plain Layout', '', '',
840                  '\\backslash', '',
841                  'end{%s}' % (latex_name),
842                  '\\end_layout', '', '\\end_inset',
843                  '', '', '\\end_layout']
844     subst_begin = ['\\begin_layout Standard', '\\noindent',
845                    '\\begin_inset ERT', 'status open', '',
846                    '\\begin_layout Plain Layout', '', '', '\\backslash',
847                    'begin{%s}' % (latex_name),
848                    '\\end_layout', '', '\\begin_layout Plain Layout', '']
849
850     while True:
851         i = find_token(document.body, "\\begin_layout %s" % (layout_name), i)
852         if i == -1:
853             return
854         j = find_end_of_layout(document.body, i)
855         if j == -1:
856             document.warning("Malformed LyX document: Can't find end of %s layout" \
857               % (layout_name))
858             i += 1
859             continue
860         # delete all line breaks insets (there are no other insets)
861         l = i
862         while True:
863             n = find_token(document.body, "\\begin_inset Newline newline", l, j)
864             if n == -1:
865                 n = find_token(document.body, "\\begin_inset Newline linebreak", l, j)
866                 if n == -1:
867                     break
868             m = find_end_of_inset(document.body, n)
869             del(document.body[m:m+1])
870             document.body[n:n+1] = ['\\end_layout', '', '\\begin_layout Plain Layout']
871             l += 1
872             # we deleted a line, so the end of the inset moved forward.
873             # FIXME But we also added some lines, didn't we? I think this
874             # should be j += 1.
875             j -= 1
876         # consecutive verbatim environments need to be connected
877         k = find_token(document.body, "\\begin_layout %s" % (layout_name), j)
878         if k == j + 2 and consecutive == False:
879             consecutive = True
880             document.body[j:j+1] = ['\\end_layout', '', '\\begin_layout Plain Layout']
881             document.body[i:i+1] = subst_begin
882             continue
883         if k == j + 2 and consecutive == True:
884             document.body[j:j+1] = ['\\end_layout', '', '\\begin_layout Plain Layout']
885             del(document.body[i:i+1])
886             continue
887         if k != j + 2 and consecutive == True:
888             document.body[j:j+1] = subst_end
889             # the next paragraph must not be indented
890             # FIXME This seems to be causing problems, because of the
891             # hardcoded use of 19. We should figure out exactly where
892             # this needs to go by searching for the right tag.
893             document.body[j+19:j+19] = ['\\noindent']
894             del(document.body[i:i+1])
895             consecutive = False
896             continue
897         else:
898             document.body[j:j+1] = subst_end
899             # the next paragraph must not be indented
900             # FIXME This seems to be causing problems, because of the
901             # hardcoded use of 19. We should figure out exactly where
902             # this needs to go by searching for the right tag.
903             document.body[j+19:j+19] = ['\\noindent']
904             document.body[i:i+1] = subst_begin
905
906
907 def revert_tipa(document):
908     " Revert native TIPA insets to mathed or ERT. "
909     i = 0
910     while True:
911         i = find_token(document.body, "\\begin_inset IPA", i)
912         if i == -1:
913             return
914         j = find_end_of_inset(document.body, i)
915         if j == -1:
916             document.warning("Malformed LyX document: Can't find end of IPA inset")
917             i += 1
918             continue
919         Multipar = False
920         n = find_token(document.body, "\\begin_layout", i, j)
921         if n == -1:
922             document.warning("Malformed LyX document: IPA inset has no embedded layout")
923             i += 1
924             continue
925         m = find_end_of_layout(document.body, n)
926         if m == -1:
927             document.warning("Malformed LyX document: Can't find end of embedded layout")
928             i += 1
929             continue
930         content = document.body[n+1:m]
931         p = find_token(document.body, "\\begin_layout", m, j)
932         if p != -1 or len(content) > 1:
933             Multipar = True
934             content = document.body[i+1:j]
935         if Multipar:
936             # IPA insets with multiple pars need to be wrapped by \begin{IPA}...\end{IPA}
937             document.body[i:j+1] = ['\\end_layout', '', '\\begin_layout Standard'] + put_cmd_in_ert("\\begin{IPA}") + ['\\end_layout'] + content + ['\\begin_layout Standard'] + put_cmd_in_ert("\\end{IPA}")
938             add_to_preamble(document, ["\\usepackage{tipa,tipx}"])
939         else:
940             # single-par IPA insets can be reverted to mathed
941             document.body[i:j+1] = ["\\begin_inset Formula $\\text{\\textipa{" + content[0] + "}}$", "\\end_inset"]
942         i = j
943
944
945 def revert_cell_rotation(document):
946   "Revert cell rotations to TeX-code"
947
948   load_rotating = False
949   i = 0
950   try:
951     while True:
952       # first, let's find out if we need to do anything
953       i = find_token(document.body, '<cell ', i)
954       if i == -1:
955         return
956       j = document.body[i].find('rotate="')
957       if j != -1:
958         k = document.body[i].find('"', j + 8)
959         value = document.body[i][j + 8 : k]
960         if value == "0":
961           rgx = re.compile(r' rotate="[^"]+?"')
962           # remove rotate option
963           document.body[i] = rgx.sub('', document.body[i])
964         elif value == "90":
965           rgx = re.compile(r' rotate="[^"]+?"')
966           document.body[i] = rgx.sub(' rotate="true"', document.body[i])
967         else:
968           rgx = re.compile(r' rotate="[^"]+?"')
969           load_rotating = True
970           # remove rotate option
971           document.body[i] = rgx.sub('', document.body[i])
972           # write ERT
973           document.body[i + 5 : i + 5] = \
974             put_cmd_in_ert("\\end{turn}")
975           document.body[i + 4 : i + 4] = \
976             put_cmd_in_ert("\\begin{turn}{" + value + "}")
977
978       i += 1
979
980   finally:
981     if load_rotating:
982       add_to_preamble(document, ["\\@ifundefined{turnbox}{\\usepackage{rotating}}{}"])
983
984
985 def convert_cell_rotation(document):
986     'Convert cell rotation statements from "true" to "90"'
987
988     i = 0
989     while True:
990       # first, let's find out if we need to do anything
991       i = find_token(document.body, '<cell ', i)
992       if i == -1:
993         return
994       j = document.body[i].find('rotate="true"')
995       if j != -1:
996         rgx = re.compile(r'rotate="[^"]+?"')
997         # convert "true" to "90"
998         document.body[i] = rgx.sub('rotate="90"', document.body[i])
999
1000       i += 1
1001
1002
1003 def revert_table_rotation(document):
1004   "Revert table rotations to TeX-code"
1005
1006   load_rotating = False
1007   i = 0
1008   try:
1009     while True:
1010       # first, let's find out if we need to do anything
1011       i = find_token(document.body, '<features ', i)
1012       if i == -1:
1013         return
1014       j = document.body[i].find('rotate="')
1015       if j != -1:
1016         end_table = find_token(document.body, '</lyxtabular>', j)
1017         k = document.body[i].find('"', j + 8)
1018         value = document.body[i][j + 8 : k]
1019         if value == "0":
1020           rgx = re.compile(r' rotate="[^"]+?"')
1021           # remove rotate option
1022           document.body[i] = rgx.sub('', document.body[i])
1023         elif value == "90":
1024           rgx = re.compile(r'rotate="[^"]+?"')
1025           document.body[i] = rgx.sub('rotate="true"', document.body[i])
1026         else:
1027           rgx = re.compile(r' rotate="[^"]+?"')
1028           load_rotating = True
1029           # remove rotate option
1030           document.body[i] = rgx.sub('', document.body[i])
1031           # write ERT
1032           document.body[end_table + 3 : end_table + 3] = \
1033             put_cmd_in_ert("\\end{turn}")
1034           document.body[i - 2 : i - 2] = \
1035             put_cmd_in_ert("\\begin{turn}{" + value + "}")
1036
1037       i += 1
1038
1039   finally:
1040     if load_rotating:
1041       add_to_preamble(document, ["\\@ifundefined{turnbox}{\\usepackage{rotating}}{}"])
1042
1043
1044 def convert_table_rotation(document):
1045     'Convert table rotation statements from "true" to "90"'
1046
1047     i = 0
1048     while True:
1049       # first, let's find out if we need to do anything
1050       i = find_token(document.body, '<features ', i)
1051       if i == -1:
1052         return
1053       j = document.body[i].find('rotate="true"')
1054       if j != -1:
1055         rgx = re.compile(r'rotate="[^"]+?"')
1056         # convert "true" to "90"
1057         document.body[i] = rgx.sub('rotate="90"', document.body[i])
1058
1059       i += 1
1060
1061
1062 def convert_listoflistings(document):
1063     'Convert ERT \lstlistoflistings to TOC lstlistoflistings inset'
1064     # We can support roundtrip because the command is so simple
1065     i = 0
1066     while True:
1067         i = find_token(document.body, "\\begin_inset ERT", i)
1068         if i == -1:
1069             return
1070         j = find_end_of_inset(document.body, i)
1071         if j == -1:
1072             document.warning("Malformed LyX document: Can't find end of ERT inset")
1073             i += 1
1074             continue
1075         ert = get_ert(document.body, i)
1076         if ert == "\\lstlistoflistings{}":
1077             document.body[i:j] = ["\\begin_inset CommandInset toc", "LatexCommand lstlistoflistings", ""]
1078             i = i + 4
1079         else:
1080             i = j + 1
1081
1082
1083 def revert_listoflistings(document):
1084     'Convert TOC lstlistoflistings inset to ERT lstlistoflistings'
1085     i = 0
1086     while True:
1087         i = find_token(document.body, "\\begin_inset CommandInset toc", i)
1088         if i == -1:
1089             return
1090         if document.body[i+1] == "LatexCommand lstlistoflistings":
1091             j = find_end_of_inset(document.body, i)
1092             if j == -1:
1093                 document.warning("Malformed LyX document: Can't find end of TOC inset")
1094                 i += 1
1095                 continue
1096             subst = put_cmd_in_ert("\\lstlistoflistings{}")
1097             document.body[i:j+1] = subst
1098             add_to_preamble(document, ["\\usepackage{listings}"])
1099         i += 1
1100
1101
1102 def convert_use_amssymb(document):
1103     "insert use_package amssymb"
1104     regexp = re.compile(r'(\\use_package\s+amsmath)')
1105     i = find_re(document.header, regexp, 0)
1106     if i == -1:
1107         document.warning("Malformed LyX document: Can't find \\use_package amsmath.")
1108         return;
1109     value = get_value(document.header, "\\use_package" , i).split()[1]
1110     useamsmath = 0
1111     try:
1112         useamsmath = int(value)
1113     except:
1114         document.warning("Invalid \\use_package amsmath: " + value + ". Assuming auto.")
1115         useamsmath = 1
1116     j = find_token(document.preamble, "\\usepackage{amssymb}", 0)
1117     if j == -1:
1118         document.header.insert(i + 1, "\\use_package amssymb %d" % useamsmath)
1119     else:
1120         document.header.insert(i + 1, "\\use_package amssymb 2")
1121         del document.preamble[j]
1122
1123
1124 def revert_use_amssymb(document):
1125     "remove use_package amssymb"
1126     regexp1 = re.compile(r'(\\use_package\s+amsmath)')
1127     regexp2 = re.compile(r'(\\use_package\s+amssymb)')
1128     i = find_re(document.header, regexp1, 0)
1129     j = find_re(document.header, regexp2, 0)
1130     value1 = "1" # default is auto
1131     value2 = "1" # default is auto
1132     if i != -1:
1133         value1 = get_value(document.header, "\\use_package" , i).split()[1]
1134     if j != -1:
1135         value2 = get_value(document.header, "\\use_package" , j).split()[1]
1136         del document.header[j]
1137     if value1 != value2 and value2 == "2": # on
1138         add_to_preamble(document, ["\\usepackage{amssymb}"])
1139
1140
1141 def convert_use_cancel(document):
1142     "insert use_package cancel"
1143     convert_use_package(document, "cancel", cancel_commands, True)
1144
1145
1146 def revert_use_cancel(document):
1147     "remove use_package cancel"
1148     revert_use_package(document, "cancel", cancel_commands, True)
1149
1150
1151 def revert_ancientgreek(document):
1152     "Set the document language for ancientgreek to greek"
1153
1154     if document.language == "ancientgreek":
1155         document.language = "greek"
1156         i = find_token(document.header, "\\language", 0)
1157         if i != -1:
1158             document.header[i] = "\\language greek"
1159     j = 0
1160     while True:
1161         j = find_token(document.body, "\\lang ancientgreek", j)
1162         if j == -1:
1163             return
1164         else:
1165             document.body[j] = document.body[j].replace("\\lang ancientgreek", "\\lang greek")
1166         j += 1
1167
1168
1169 def revert_languages(document):
1170     "Set the document language for new supported languages to English"
1171
1172     languages = [
1173                  "coptic", "divehi", "hindi", "kurmanji", "lao", "marathi", "occitan", "sanskrit",
1174                  "syriac", "tamil", "telugu", "urdu"
1175                 ]
1176     for n in range(len(languages)):
1177         if document.language == languages[n]:
1178             document.language = "english"
1179             i = find_token(document.header, "\\language", 0)
1180             if i != -1:
1181                 document.header[i] = "\\language english"
1182         j = 0
1183         while j < len(document.body):
1184             j = find_token(document.body, "\\lang " + languages[n], j)
1185             if j != -1:
1186                 document.body[j] = document.body[j].replace("\\lang " + languages[n], "\\lang english")
1187                 j += 1
1188             else:
1189                 j = len(document.body)
1190
1191
1192 def convert_armenian(document):
1193     "Use polyglossia and thus non-TeX fonts for Armenian"
1194
1195     if document.language == "armenian":
1196         i = find_token(document.header, "\\use_non_tex_fonts", 0)
1197         if i != -1:
1198             document.header[i] = "\\use_non_tex_fonts true"
1199
1200
1201 def revert_armenian(document):
1202     "Use ArmTeX and thus TeX fonts for Armenian"
1203
1204     if document.language == "armenian":
1205         i = find_token(document.header, "\\use_non_tex_fonts", 0)
1206         if i != -1:
1207             document.header[i] = "\\use_non_tex_fonts false"
1208
1209
1210 def revert_libertine(document):
1211     " Revert native libertine font definition to LaTeX "
1212
1213     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1214         i = find_token(document.header, "\\font_roman libertine", 0)
1215         if i != -1:
1216             osf = False
1217             j = find_token(document.header, "\\font_osf true", 0)
1218             if j != -1:
1219                 osf = True
1220             preamble = "\\usepackage"
1221             if osf:
1222                 document.header[j] = "\\font_osf false"
1223                 preamble += "[osf]"
1224             else:
1225                 preamble += "[lining]"
1226             preamble += "{libertine-type1}"
1227             add_to_preamble(document, [preamble])
1228             document.header[i] = "\\font_roman default"
1229
1230
1231 def revert_txtt(document):
1232     " Revert native txtt font definition to LaTeX "
1233
1234     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1235         i = find_token(document.header, "\\font_typewriter txtt", 0)
1236         if i != -1:
1237             preamble = "\\renewcommand{\\ttdefault}{txtt}"
1238             add_to_preamble(document, [preamble])
1239             document.header[i] = "\\font_typewriter default"
1240
1241
1242 def revert_mathdesign(document):
1243     " Revert native mathdesign font definition to LaTeX "
1244
1245     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1246         mathdesign_dict = {
1247         "mdbch":  "charter",
1248         "mdput":  "utopia",
1249         "mdugm":  "garamond"
1250         }
1251         i = find_token(document.header, "\\font_roman", 0)
1252         if i == -1:
1253             return
1254         val = get_value(document.header, "\\font_roman", i)
1255         if val in list(mathdesign_dict.keys()):
1256             preamble = "\\usepackage[%s" % mathdesign_dict[val]
1257             expert = False
1258             j = find_token(document.header, "\\font_osf true", 0)
1259             if j != -1:
1260                 expert = True
1261                 document.header[j] = "\\font_osf false"
1262             l = find_token(document.header, "\\font_sc true", 0)
1263             if l != -1:
1264                 expert = True
1265                 document.header[l] = "\\font_sc false"
1266             if expert:
1267                 preamble += ",expert"
1268             preamble += "]{mathdesign}"
1269             add_to_preamble(document, [preamble])
1270             document.header[i] = "\\font_roman default"
1271
1272
1273 def revert_texgyre(document):
1274     " Revert native TeXGyre font definition to LaTeX "
1275
1276     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1277         texgyre_fonts = ["tgadventor", "tgbonum", "tgchorus", "tgcursor", \
1278                          "tgheros", "tgpagella", "tgschola", "tgtermes"]
1279         i = find_token(document.header, "\\font_roman", 0)
1280         if i != -1:
1281             val = get_value(document.header, "\\font_roman", i)
1282             if val in texgyre_fonts:
1283                 preamble = "\\usepackage{%s}" % val
1284                 add_to_preamble(document, [preamble])
1285                 document.header[i] = "\\font_roman default"
1286         i = find_token(document.header, "\\font_sans", 0)
1287         if i != -1:
1288             val = get_value(document.header, "\\font_sans", i)
1289             if val in texgyre_fonts:
1290                 preamble = "\\usepackage{%s}" % val
1291                 add_to_preamble(document, [preamble])
1292                 document.header[i] = "\\font_sans default"
1293         i = find_token(document.header, "\\font_typewriter", 0)
1294         if i != -1:
1295             val = get_value(document.header, "\\font_typewriter", i)
1296             if val in texgyre_fonts:
1297                 preamble = "\\usepackage{%s}" % val
1298                 add_to_preamble(document, [preamble])
1299                 document.header[i] = "\\font_typewriter default"
1300
1301
1302 def revert_ipadeco(document):
1303     " Revert IPA decorations to ERT "
1304     i = 0
1305     while True:
1306       i = find_token(document.body, "\\begin_inset IPADeco", i)
1307       if i == -1:
1308           return
1309       end = find_end_of_inset(document.body, i)
1310       if end == -1:
1311           document.warning("Can't find end of inset at line " + str(i))
1312           i += 1
1313           continue
1314       line = document.body[i]
1315       rx = re.compile(r'\\begin_inset IPADeco (.*)$')
1316       m = rx.match(line)
1317       decotype = m.group(1)
1318       if decotype != "toptiebar" and decotype != "bottomtiebar":
1319           document.warning("Invalid IPADeco type: " + decotype)
1320           i = end
1321           continue
1322       blay = find_token(document.body, "\\begin_layout Plain Layout", i, end)
1323       if blay == -1:
1324           document.warning("Can't find layout for inset at line " + str(i))
1325           i = end
1326           continue
1327       bend = find_end_of_layout(document.body, blay)
1328       if bend == -1:
1329           document.warning("Malformed LyX document: Could not find end of IPADeco inset's layout.")
1330           i = end
1331           continue
1332       substi = ["\\begin_inset ERT", "status collapsed", "",
1333                 "\\begin_layout Plain Layout", "", "", "\\backslash",
1334                 decotype + "{", "\\end_layout", "", "\\end_inset"]
1335       substj = ["\\size default", "", "\\begin_inset ERT", "status collapsed", "",
1336                 "\\begin_layout Plain Layout", "", "}", "\\end_layout", "", "\\end_inset"]
1337       # do the later one first so as not to mess up the numbering
1338       document.body[bend:end + 1] = substj
1339       document.body[i:blay + 1] = substi
1340       i = end + len(substi) + len(substj) - (end - bend) - (blay - i) - 2
1341       add_to_preamble(document, "\\usepackage{tipa}")
1342
1343
1344 def revert_ipachar(document):
1345     ' Revert \\IPAChar to ERT '
1346     i = 0
1347     found = False
1348     while i < len(document.body):
1349         m = re.match(r'(.*)\\IPAChar \\(\w+\{\w+\})(.*)', document.body[i])
1350         if m:
1351             found = True
1352             before = m.group(1)
1353             ipachar = m.group(2)
1354             after = m.group(3)
1355             subst = [before,
1356                      '\\begin_inset ERT',
1357                      'status collapsed', '',
1358                      '\\begin_layout Standard',
1359                      '', '', '\\backslash',
1360                      ipachar,
1361                      '\\end_layout', '',
1362                      '\\end_inset', '',
1363                      after]
1364             document.body[i: i+1] = subst
1365             i = i + len(subst)
1366         else:
1367             i += 1
1368     if found:
1369         add_to_preamble(document, "\\usepackage{tone}")
1370
1371
1372 def revert_minionpro(document):
1373     " Revert native MinionPro font definition to LaTeX "
1374
1375     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1376         i = find_token(document.header, "\\font_roman minionpro", 0)
1377         if i != -1:
1378             osf = False
1379             j = find_token(document.header, "\\font_osf true", 0)
1380             if j != -1:
1381                 osf = True
1382             preamble = "\\usepackage"
1383             if osf:
1384                 document.header[j] = "\\font_osf false"
1385             else:
1386                 preamble += "[lf]"
1387             preamble += "{MinionPro}"
1388             add_to_preamble(document, [preamble])
1389             document.header[i] = "\\font_roman default"
1390
1391
1392 def revert_mathfonts(document):
1393     " Revert native math font definitions to LaTeX "
1394
1395     i = find_token(document.header, "\\font_math", 0)
1396     if i == -1:
1397        return
1398     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1399         val = get_value(document.header, "\\font_math", i)
1400         if val == "eulervm":
1401             add_to_preamble(document, "\\usepackage{eulervm}")
1402         elif val == "default":
1403             mathfont_dict = {
1404             "lmodern":  "\\renewcommand{\\rmdefault}{lmr}",
1405             "minionpro":  "\\usepackage[onlytext,lf]{MinionPro}",
1406             "minionpro-osf":  "\\usepackage[onlytext]{MinionPro}",
1407             "palatino":  "\\renewcommand{\\rmdefault}{ppl}",
1408             "palatino-osf":  "\\renewcommand{\\rmdefault}{pplj}",
1409             "times":  "\\renewcommand{\\rmdefault}{ptm}",
1410             "utopia":  "\\renewcommand{\\rmdefault}{futs}",
1411             "utopia-osf":  "\\renewcommand{\\rmdefault}{futj}",
1412             }
1413             j = find_token(document.header, "\\font_roman", 0)
1414             if j != -1:
1415                 rm = get_value(document.header, "\\font_roman", j)
1416                 k = find_token(document.header, "\\font_osf true", 0)
1417                 if k != -1:
1418                     rm += "-osf"
1419                 if rm in list(mathfont_dict.keys()):
1420                     add_to_preamble(document, mathfont_dict[rm])
1421                     document.header[j] = "\\font_roman default"
1422                     if k != -1:
1423                         document.header[k] = "\\font_osf false"
1424     del document.header[i]
1425
1426
1427 def revert_mdnomath(document):
1428     " Revert mathdesign and fourier without math "
1429
1430     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1431         mathdesign_dict = {
1432         "md-charter": "mdbch",
1433         "md-utopia": "mdput",
1434         "md-garamond": "mdugm"
1435         }
1436         i = find_token(document.header, "\\font_roman", 0)
1437         if i == -1:
1438             return
1439         val = get_value(document.header, "\\font_roman", i)
1440         if val in list(mathdesign_dict.keys()):
1441             j = find_token(document.header, "\\font_math", 0)
1442             if j == -1:
1443                 document.header[i] = "\\font_roman %s" % mathdesign_dict[val]
1444             mval = get_value(document.header, "\\font_math", j)
1445             if mval == "default":
1446                 document.header[i] = "\\font_roman default"
1447                 add_to_preamble(document, "\\renewcommand{\\rmdefault}{%s}" % mathdesign_dict[val])
1448             else:
1449                 document.header[i] = "\\font_roman %s" % mathdesign_dict[val]
1450
1451
1452 def convert_mathfonts(document):
1453     document.header.insert(-1, "\\font_math auto")
1454
1455
1456 def convert_mdnomath(document):
1457     " Change mathdesign font name "
1458
1459     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1460         mathdesign_dict = {
1461         "mdbch":  "md-charter",
1462         "mdput":  "md-utopia",
1463         "mdugm":  "md-garamond"
1464         }
1465         i = find_token(document.header, "\\font_roman", 0)
1466         if i == -1:
1467             return
1468         val = get_value(document.header, "\\font_roman", i)
1469         if val in list(mathdesign_dict.keys()):
1470              document.header[i] = "\\font_roman %s" % mathdesign_dict[val]
1471
1472
1473 def revert_newtxmath(document):
1474     " Revert native newtxmath definitions to LaTeX "
1475
1476     i = find_token(document.header, "\\font_math", 0)
1477     if i == -1:
1478        return
1479     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1480         val = get_value(document.header, "\\font_math", i)
1481         mathfont_dict = {
1482         "libertine-ntxm":  "\\usepackage[libertine]{newtxmath}",
1483         "minion-ntxm":  "\\usepackage[minion]{newtxmath}",
1484         "newtxmath":  "\\usepackage{newtxmath}",
1485         }
1486         if val in list(mathfont_dict.keys()):
1487             add_to_preamble(document, mathfont_dict[val])
1488             document.header[i] = "\\font_math auto"
1489
1490
1491 def revert_biolinum(document):
1492     " Revert native biolinum font definition to LaTeX "
1493
1494     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1495         i = find_token(document.header, "\\font_sans biolinum", 0)
1496         if i != -1:
1497             osf = False
1498             j = find_token(document.header, "\\font_osf true", 0)
1499             if j != -1:
1500                 osf = True
1501             preamble = "\\usepackage"
1502             if not osf:
1503                 preamble += "[lf]"
1504             preamble += "{biolinum-type1}"
1505             add_to_preamble(document, [preamble])
1506             document.header[i] = "\\font_sans default"
1507
1508
1509 def revert_uop(document):
1510     " Revert native URW Classico (Optima) font definition to LaTeX "
1511
1512     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1513         i = find_token(document.header, "\\font_sans uop", 0)
1514         if i != -1:
1515                 preamble = "\\renewcommand{\\sfdefault}{uop}"
1516                 add_to_preamble(document, [preamble])
1517                 document.header[i] = "\\font_sans default"
1518
1519
1520 def convert_latexargs(document):
1521     " Convert InsetArgument to new syntax "
1522
1523     if find_token(document.body, "\\begin_inset Argument", 0) == -1:
1524         # nothing to do.
1525         return
1526
1527     # A list of layouts (document classes) with only optional or no arguments.
1528     # These can be safely converted to the new syntax
1529     # (I took the liberty to add some of my personal layouts/modules here; JSP)
1530     safe_layouts = ["aa", "aapaper", "aastex", "achemso", "acmsiggraph", "AEA",
1531                     "agu-dtd", "agums", "agutex", "amsart", "amsbook", "apa",
1532                     "arab-article", "armenian-article", "article-beamer", "article",
1533                     "beamer", "book", "broadway", "chess", "cl2emult", "ctex-article",
1534                     "ctex-book", "ctex-report", "dinbrief", "docbook-book", "docbook-chapter",
1535                     "docbook", "docbook-section", "doublecol-new", "dtk", "ectaart", "egs",
1536                     "elsarticle", "elsart", "entcs", "europecv", "extarticle", "extbook",
1537                     "extletter", "extreport", "foils", "frletter", "g-brief2", "g-brief",
1538                     "heb-article", "heb-letter", "hollywood", "IEEEtran", "ijmpc", "ijmpd",
1539                     "iopart", "isprs", "jarticle", "jasatex", "jbook", "jgrga", "jreport",
1540                     "jsarticle", "jsbeamer", "jsbook", "jss", "kluwer", "latex8", "letter", "lettre",
1541                     "literate-article", "literate-book", "literate-report", "llncs", "ltugboat",
1542                     "memoir", "moderncv", "mwart", "mwbk", "mwrep", "paper", "powerdot",
1543                     "recipebook", "report", "revtex4", "revtex", "scrartcl", "scrarticle-beamer",
1544                     "scrbook", "scrlettr", "scrlttr2", "scrreprt", "seminar", "siamltex",
1545                     "sigplanconf", "simplecv", "singlecol", "singlecol-new", "slides", "spie",
1546                     "svglobal3", "svglobal", "svjog", "svmono", "svmult", "svprobth", "tarticle",
1547                     "tbook", "treport", "tufte-book", "tufte-handout"]
1548     # A list of "safe" modules, same as above
1549     safe_modules = ["biblatex", "beameraddons", "beamer-resenumerate", "beamersession", "braille",
1550                     "customHeadersFooters", "endnotes", "enumitem", "eqs-within-sections", "figs-within-sections",
1551                     "fix-cm", "fixltx2e", "foottoend", "hanging", "jscharstyles", "knitr", "lilypond",
1552                     "linguistics", "linguisticx", "logicalmkup", "minimalistic", "nomindex", "noweb",
1553                     "pdfcomment", "sweave", "tabs-within-sections", "theorems-ams-bytype",
1554                     "theorems-ams-extended-bytype", "theorems-ams-extended", "theorems-ams", "theorems-bytype",
1555                     "theorems-chap-bytype", "theorems-chap", "theorems-named", "theorems-sec-bytype",
1556                     "theorems-sec", "theorems-starred", "theorems-std", "todonotes"]
1557     # Modules we need to take care of
1558     caveat_modules = ["initials"]
1559     # information about the relevant styles in caveat_modules (number of opt and req args)
1560     # use this if we get more caveat_modules. For now, use hard coding (see below).
1561     # initials = [{'Layout' : 'Initial', 'opt' : 1, 'req' : 1}]
1562
1563     # Is this a known safe layout?
1564     safe_layout = document.textclass in safe_layouts
1565     if not safe_layout:
1566         document.warning("Lyx2lyx knows nothing about textclass '%s'. "
1567                          "Please check if short title insets have been converted correctly."
1568                          % document.textclass)
1569     # Do we use unsafe or unknown modules
1570     mods = document.get_module_list()
1571     unknown_modules = False
1572     used_caveat_modules = list()
1573     for mod in mods:
1574         if mod in safe_modules:
1575             continue
1576         if mod in caveat_modules:
1577             used_caveat_modules.append(mod)
1578             continue
1579         unknown_modules = True
1580         document.warning("Lyx2lyx knows nothing about module '%s'. "
1581                          "Please check if short title insets have been converted correctly."
1582                          % mod)
1583
1584     i = 0
1585     while True:
1586         i = find_token(document.body, "\\begin_inset Argument", i)
1587         if i == -1:
1588             return
1589
1590         if not safe_layout or unknown_modules:
1591             # We cannot do more here since we have no access to this layout.
1592             # InsetArgument itself will do the real work
1593             # (see InsetArgument::updateBuffer())
1594             document.body[i] = "\\begin_inset Argument 999"
1595             i += 1
1596             continue
1597
1598         # Find containing paragraph layout
1599         parent = get_containing_layout(document.body, i)
1600         if parent == False:
1601             document.warning("Malformed LyX document: Can't find parent paragraph layout")
1602             i += 1
1603             continue
1604         parbeg = parent[1]
1605         parend = parent[2]
1606         allowed_opts = -1
1607         first_req = -1
1608         if len(used_caveat_modules) > 0:
1609             # We know for now that this must be the initials module with the Initial layout
1610             # If we get more such modules, we need some automating.
1611             if parent[0] == "Initial":
1612                 # Layout has 1 opt and 1 req arg.
1613                 # Count the actual arguments
1614                 actualargs = 0
1615                 for p in range(parbeg, parend):
1616                     if document.body[p] == "\\begin_inset Argument":
1617                         actualargs += 1
1618                 if actualargs == 1:
1619                     allowed_opts = 0
1620                     first_req = 2
1621         # Collect all arguments in this paragraph
1622         argnr = 0
1623         for p in range(parbeg, parend):
1624             if document.body[p] == "\\begin_inset Argument":
1625                 argnr += 1
1626                 if allowed_opts != -1:
1627                     # We have less arguments than opt + required.
1628                     # required must take precedence.
1629                     if argnr > allowed_opts and argnr < first_req:
1630                         argnr = first_req
1631                 document.body[p] = "\\begin_inset Argument %d" % argnr
1632         i = parend + 1
1633
1634
1635 def revert_latexargs(document):
1636     " Revert InsetArgument to old syntax "
1637
1638     i = 0
1639     rx = re.compile(r'^\\begin_inset Argument (\d+)$')
1640     args = dict()
1641     while True:
1642         # Search for Argument insets
1643         i = find_token(document.body, "\\begin_inset Argument", i)
1644         if i == -1:
1645             return
1646         m = rx.match(document.body[i])
1647         if not m:
1648             # No ID: inset already reverted
1649             i += 1
1650             continue
1651         # Find containing paragraph layout
1652         parent = get_containing_layout(document.body, i)
1653         if parent == False:
1654             document.warning("Malformed LyX document: Can't find parent paragraph layout")
1655             i += 1
1656             continue
1657         parbeg = parent[1]
1658         parend = parent[2]
1659         # Do not set realparbeg to parent[3], since this does not work if we
1660         # have another inset (e.g. label or index) before the first argument
1661         # inset (this is the case in the user guide of LyX 2.0.8)
1662         realparbeg = -1
1663         # Collect all arguments in this paragraph
1664         realparend = parend
1665         for p in range(parbeg, parend):
1666             m = rx.match(document.body[p])
1667             if m:
1668                 if realparbeg < 0:
1669                     # This is the first argument inset
1670                     realparbeg = p
1671                 val = int(m.group(1))
1672                 j = find_end_of_inset(document.body, p)
1673                 # Revert to old syntax
1674                 document.body[p] = "\\begin_inset Argument"
1675                 if j == -1:
1676                     document.warning("Malformed LyX document: Can't find end of Argument inset")
1677                     continue
1678                 if val > 0:
1679                     args[val] = document.body[p : j + 1]
1680                 # Adjust range end
1681                 realparend = realparend - len(document.body[p : j + 1])
1682                 # Remove arg inset at this position
1683                 del document.body[p : j + 1]
1684             if p >= realparend:
1685                 break
1686         if realparbeg < 0:
1687             # No argument inset found
1688             realparbeg = parent[3]
1689         # Now sort the arg insets
1690         subst = []
1691         for f in sorted(args):
1692             subst += args[f]
1693             del args[f]
1694         # Insert the sorted arg insets at paragraph begin
1695         document.body[realparbeg : realparbeg] = subst
1696
1697         i = realparbeg + 1 + len(subst)
1698
1699
1700 def revert_IEEEtran(document):
1701     '''
1702     Reverts InsetArgument of
1703     Page headings
1704     Biography
1705     Biography without photo
1706     to TeX-code
1707     '''
1708     if document.textclass != "IEEEtran":
1709         return
1710
1711     layouts = {"Page headings": False,
1712                "Biography without photo": True}
1713
1714     for layout in list(layouts.keys()):
1715         i = 0
1716         while True:
1717             i = find_token(document.body, '\\begin_layout ' + layout, i)
1718             if i == -1:
1719                 break
1720             revert_Argument_to_TeX_brace(document, i, 0, 1, 1, layouts[layout], False)
1721             i += 1
1722
1723     i = 0
1724     while True:
1725         i = find_token(document.body, '\\begin_inset Flex Paragraph Start', i)
1726         if i == -1:
1727             break
1728         revert_Argument_to_TeX_brace(document, i, 0, 1, 1, False, False)
1729         i += 1
1730
1731     i = 0
1732     while True:
1733         i = find_token_exact(document.body, "\\begin_layout Biography", i)
1734         if i == -1:
1735                 break
1736
1737         if document.body[i] == "\\begin_layout Biography without photo":
1738             i += 1
1739             continue
1740
1741         # start with the second argument, therefore 2
1742         revert_Argument_to_TeX_brace(document, i, 0, 2, 2, True, False)
1743         i += 1
1744
1745
1746 def revert_IEEEtran_2(document):
1747   '''
1748   Reverts Flex Paragraph Start to TeX-code
1749   '''
1750   if document.textclass == "IEEEtran":
1751     begin = 0
1752     while True:
1753       begin = find_token(document.body, "\\begin_inset Flex Paragraph Start", begin)
1754       if begin == -1:
1755         return
1756       end1 = find_end_of_inset(document.body, begin)
1757       document.body[end1 - 2 : end1 + 1] = put_cmd_in_ert("}")
1758       document.body[begin : begin + 4] = put_cmd_in_ert("\\IEEEPARstart{")
1759       begin = begin + 5
1760
1761
1762 def convert_IEEEtran(document):
1763     '''
1764     Converts ERT of
1765     Page headings
1766     Biography
1767     Biography without photo
1768     to InsetArgument
1769     '''
1770     if document.textclass != "IEEEtran":
1771         return
1772
1773     layouts = {"Page headings": False,
1774                "Biography without photo": True}
1775
1776     for layout in list(layouts.keys()):
1777         i = 0
1778         while True:
1779             i = find_token(document.body, '\\begin_layout ' + layout, i)
1780             if i == -1:
1781                 break
1782             convert_TeX_brace_to_Argument(document, i, 1, 1, False, layouts[layout], False)
1783             i += 1
1784
1785     i = 0
1786     while True:
1787         i = find_token_exact(document.body, "\\begin_layout Biography", i)
1788         if i == -1:
1789                 break
1790
1791         if document.body[i] == "\\begin_layout Biography without photo":
1792             i += 1
1793             continue
1794
1795         # the argument we want to convert is the second one
1796         convert_TeX_brace_to_Argument(document, i, 2, 2, False, True, False)
1797         i += 1
1798
1799
1800 def revert_AASTeX(document):
1801   " Reverts InsetArgument of Altaffilation to TeX-code "
1802   if document.textclass == "aastex":
1803     i = 0
1804     while True:
1805       i = find_token(document.body, "\\begin_layout Altaffilation", i)
1806       if i == -1:
1807         return
1808       revert_Argument_to_TeX_brace(document, i, 0, 1, 1, False, False)
1809       i += 1
1810
1811
1812 def convert_AASTeX(document):
1813   " Converts ERT of Altaffilation to InsetArgument "
1814   if document.textclass == "aastex":
1815     i = 0
1816     while True:
1817       i = find_token(document.body, "\\begin_layout Altaffilation", i)
1818       if i == -1:
1819         return
1820       convert_TeX_brace_to_Argument(document, i, 1, 1, False, False, False)
1821       i += 1
1822
1823
1824 def revert_AGUTeX(document):
1825   " Reverts InsetArgument of Author affiliation to TeX-code "
1826   if document.textclass == "agutex":
1827     i = 0
1828     while True:
1829       i = find_token(document.body, "\\begin_layout Author affiliation", i)
1830       if i == -1:
1831         return
1832       revert_Argument_to_TeX_brace(document, i, 0, 1, 1, False, False)
1833       i += 1
1834
1835
1836 def convert_AGUTeX(document):
1837   " Converts ERT of Author affiliation to InsetArgument "
1838   if document.textclass == "agutex":
1839     i = 0
1840     while True:
1841       i = find_token(document.body, "\\begin_layout Author affiliation", i)
1842       if i == -1:
1843         return
1844       convert_TeX_brace_to_Argument(document, i, 1, 1, False, False, False)
1845       i += 1
1846
1847
1848 def revert_IJMP(document):
1849   " Reverts InsetArgument of MarkBoth to TeX-code "
1850   if document.textclass == "ijmpc" or document.textclass == "ijmpd":
1851     i = 0
1852     while True:
1853       i = find_token(document.body, "\\begin_layout MarkBoth", i)
1854       if i == -1:
1855         return
1856       revert_Argument_to_TeX_brace(document, i, 0, 1, 1, False, False)
1857       i += 1
1858
1859
1860 def convert_IJMP(document):
1861   " Converts ERT of MarkBoth to InsetArgument "
1862   if document.textclass == "ijmpc" or document.textclass == "ijmpd":
1863     i = 0
1864     while True:
1865       i = find_token(document.body, "\\begin_layout MarkBoth", i)
1866       if i == -1:
1867         return
1868       convert_TeX_brace_to_Argument(document, i, 1, 1, False, False, False)
1869       i += 1
1870
1871
1872 def revert_SIGPLAN(document):
1873   " Reverts InsetArguments of SIGPLAN to TeX-code "
1874   if document.textclass == "sigplanconf":
1875     i = 0
1876     j = 0
1877     while True:
1878       if i != -1:
1879         i = find_token(document.body, "\\begin_layout Conference", i)
1880       if i != -1:
1881         revert_Argument_to_TeX_brace(document, i, 0, 1, 1, False, False)
1882         i += 1
1883       if j != -1:
1884         j = find_token(document.body, "\\begin_layout Author", j)
1885       if j != -1:
1886         revert_Argument_to_TeX_brace(document, j, 0, 1, 2, False, False)
1887         j += 1
1888       if i == -1 and j == -1:
1889         return
1890
1891
1892 def convert_SIGPLAN(document):
1893   " Converts ERT of SIGPLAN to InsetArgument "
1894   if document.textclass == "sigplanconf":
1895     i = 0
1896     j = 0
1897     while True:
1898       if i != -1:
1899         i = find_token(document.body, "\\begin_layout Conference", i)
1900       if i != -1:
1901         convert_TeX_brace_to_Argument(document, i, 1, 1, False, False, False)
1902         i += 1
1903       if j != -1:
1904         j = find_token(document.body, "\\begin_layout Author", j)
1905       if j != -1:
1906         convert_TeX_brace_to_Argument(document, j, 1, 2, False, False, False)
1907         j += 1
1908       if i == -1 and j == -1:
1909         return
1910
1911
1912 def revert_SIGGRAPH(document):
1913   " Reverts InsetArgument of Flex CRcat to TeX-code "
1914   if document.textclass == "acmsiggraph":
1915     i = 0
1916     while True:
1917       i = find_token(document.body, "\\begin_inset Flex CRcat", i)
1918       if i == -1:
1919         return
1920       revert_Argument_to_TeX_brace(document, i, 0, 1, 3, False, False)
1921       i += 1
1922
1923
1924 def convert_SIGGRAPH(document):
1925   " Converts ERT of Flex CRcat to InsetArgument "
1926   if document.textclass == "acmsiggraph":
1927     i = 0
1928     while True:
1929       i = find_token(document.body, "\\begin_inset Flex CRcat", i)
1930       if i == -1:
1931         return
1932       convert_TeX_brace_to_Argument(document, i, 1, 3, True, False, False)
1933       i += 1
1934
1935
1936 def revert_EuropeCV(document):
1937   " Reverts InsetArguments of europeCV to TeX-code "
1938   if document.textclass == "europecv":
1939     i = 0
1940     j = 0
1941     k = 0
1942     m = 0
1943     while True:
1944       if i != -1:
1945         i = find_token(document.body, "\\begin_layout Item", i)
1946       if i != -1:
1947         revert_Argument_to_TeX_brace(document, i, 0, 2, 2, False, False)
1948         i += 1
1949       if j != -1:
1950         j = find_token(document.body, "\\begin_layout BulletedItem", j)
1951       if j != -1:
1952         revert_Argument_to_TeX_brace(document, j, 0, 2, 2, False, False)
1953         j += 1
1954       if k != -1:
1955         k = find_token(document.body, "\\begin_layout Language", k)
1956       if k != -1:
1957         revert_Argument_to_TeX_brace(document, k, 0, 2, 6, False, False)
1958         k += 1
1959       if m != -1:
1960         m = find_token(document.body, "\\begin_layout LastLanguage", m)
1961       if m != -1:
1962         revert_Argument_to_TeX_brace(document, m, 0, 2, 6, False, False)
1963         m += 1
1964       if i == -1 and j == -1 and k == -1 and m == -1:
1965         return
1966
1967
1968 def convert_EuropeCV(document):
1969   " Converts ERT of europeCV to InsetArgument "
1970   if document.textclass == "europecv":
1971     i = 0
1972     j = 0
1973     k = 0
1974     m = 0
1975     while True:
1976       if i != -1:
1977         i = find_token(document.body, "\\begin_layout Item", i)
1978       if i != -1:
1979         convert_TeX_brace_to_Argument(document, i, 2, 2, False, False, False)
1980         i += 1
1981       if j != -1:
1982         j = find_token(document.body, "\\begin_layout BulletedItem", j)
1983       if j != -1:
1984         convert_TeX_brace_to_Argument(document, j, 2, 2, False, False, False)
1985         j += 1
1986       if k != -1:
1987         k = find_token(document.body, "\\begin_layout Language", k)
1988       if k != -1:
1989         convert_TeX_brace_to_Argument(document, k, 2, 6, False, False, False)
1990         k += 1
1991       if m != -1:
1992         m = find_token(document.body, "\\begin_layout LastLanguage", m)
1993       if m != -1:
1994         convert_TeX_brace_to_Argument(document, m, 2, 6, False, False, False)
1995         m += 1
1996       if i == -1 and j == -1 and k == -1 and m == -1:
1997         return
1998
1999
2000 def revert_ModernCV(document):
2001   " Reverts InsetArguments of modernCV to TeX-code "
2002   if document.textclass == "moderncv":
2003     j = 0
2004     k = 0
2005     m = 0
2006     o = 0
2007     p = 0
2008     while True:
2009       if j != -1:
2010         j = find_token(document.body, "\\begin_layout Entry", j)
2011       if j != -1:
2012         revert_Argument_to_TeX_brace(document, j, 0, 1, 5, False, False)
2013         j += 1
2014       if k != -1:
2015         k = find_token(document.body, "\\begin_layout Item", k)
2016       if k != -1:
2017         revert_Argument_to_TeX_brace(document, k, 0, 1, 1, False, False)
2018         k += 1
2019       if m != -1:
2020         m = find_token(document.body, "\\begin_layout ItemWithComment", m)
2021       if m != -1:
2022         revert_Argument_to_TeX_brace(document, m, 0, 1, 2, False, False)
2023         document.body[m] = document.body[m].replace("\\begin_layout ItemWithComment", "\\begin_layout Language")
2024         m += 1
2025       if o != -1:
2026         o = find_token(document.body, "\\begin_layout DoubleItem", o)
2027       if o != -1:
2028         revert_Argument_to_TeX_brace(document, o, 0, 1, 3, False, False)
2029         document.body[o] = document.body[o].replace("\\begin_layout DoubleItem", "\\begin_layout Computer")
2030         o = o + 1
2031       if p != -1:
2032         p = find_token(document.body, "\\begin_layout Social", p)
2033       if p != -1:
2034         revert_Argument_to_TeX_brace(document, p, 0, 1, 1, False, True)
2035         p = p + 1
2036       if j == -1 and k == -1 and m == -1 and o == -1 and p == -1:
2037         return
2038
2039
2040 def revert_ModernCV_2(document):
2041   " Reverts the Flex:Column inset of modernCV to TeX-code "
2042   if document.textclass == "moderncv":
2043     flex = 0
2044     flexEnd = -1
2045     while True:
2046       flex = find_token(document.body, "\\begin_inset Flex Column", flex)
2047       if flex == -1:
2048         return flexEnd
2049       flexEnd = find_end_of_inset(document.body, flex)
2050       wasOpt = revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, False, True)
2051       revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, False, False)
2052       flexEnd = find_end_of_inset(document.body, flex)
2053       if wasOpt == True:
2054         document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\cvcolumn")
2055       else:
2056         document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\cvcolumn{")
2057       document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("}")
2058       flex += 1
2059
2060
2061 def revert_ModernCV_3(document):
2062   " Reverts the Column style of modernCV to TeX-code "
2063   if document.textclass == "moderncv":
2064     # revert the layouts
2065     revert_ModernCV(document)
2066     p = 0
2067     # get the position of the end of the last column inset
2068     LastFlexEnd = revert_ModernCV_2(document)
2069     while True:
2070       p = find_token(document.body, "\\begin_layout Columns", p)
2071       if p == -1:
2072         return
2073       pEnd = find_end_of_layout(document.body, p)
2074       document.body[p] = document.body[p].replace("\\begin_layout Columns", "\\begin_layout Standard")
2075       if LastFlexEnd != -1:
2076         document.body[p + 1 : p + 1] = put_cmd_in_ert("\\begin{cvcolumns}")
2077         document.body[LastFlexEnd + 24 : LastFlexEnd + 24] = put_cmd_in_ert("\\end{cvcolumns}")
2078       p += 1
2079
2080
2081 def revert_ModernCV_4(document):
2082   " Reverts the style Social to TeX-code "
2083   if document.textclass == "moderncv":
2084     # revert the layouts
2085     revert_ModernCV(document)
2086     p = 0
2087     while True:
2088       p = find_token(document.body, "\\begin_layout Social", p)
2089       if p == -1:
2090         return
2091       pEnd = find_end_of_layout(document.body, p)
2092       document.body[p] = document.body[p].replace("\\begin_layout Social", "\\begin_layout Standard")
2093       document.body[p + 1 : p + 1] = put_cmd_in_ert("\\social")
2094       hasOpt = find_token(document.body, "[", p + 9)
2095       if hasOpt < p + 18:
2096         document.body[p + 30 : p + 30] = put_cmd_in_ert("{")
2097         document.body[p + 41 : p + 41] = put_cmd_in_ert("}")
2098       else:
2099         document.body[p + 11 : p + 11] = put_cmd_in_ert("{")
2100         document.body[p + 21 : p + 21] = put_cmd_in_ert("}")
2101       p += 1
2102
2103
2104 def convert_ModernCV(document):
2105   " Converts ERT of modernCV to InsetArgument "
2106   if document.textclass == "moderncv":
2107     i = 0
2108     j = 0
2109     k = 0
2110     m = 0
2111     o = 0
2112     while True:
2113       if i != -1:
2114         i = find_token(document.body, "\\begin_layout DoubleItem", i)
2115       if i != -1:
2116         convert_TeX_brace_to_Argument(document, i, 1, 1, False, False, False)
2117         document.body[o] = document.body[o].replace("\\begin_layout DoubleItem", "\\begin_layout DoubleListItem")
2118         i += 1
2119       if j != -1:
2120         j = find_token(document.body, "\\begin_layout Entry", j)
2121       if j != -1:
2122         convert_TeX_brace_to_Argument(document, j, 1, 5, False, False, False)
2123         j += 1
2124       if k != -1:
2125         k = find_token(document.body, "\\begin_layout Item", k)
2126       if k != -1:
2127         convert_TeX_brace_to_Argument(document, k, 1, 1, False, False, False)
2128         k += 1
2129       if m != -1:
2130         m = find_token(document.body, "\\begin_layout Language", m)
2131       if m != -1:
2132         convert_TeX_brace_to_Argument(document, m, 1, 2, False, False, False)
2133         m += 1
2134       if i == -1 and j == -1 and k == -1 and m == -1:
2135         return
2136
2137
2138 def revert_Initials(document):
2139   " Reverts InsetArgument of Initial to TeX-code "
2140   i = 0
2141   while True:
2142     i = find_token(document.body, "\\begin_layout Initial", i)
2143     if i == -1:
2144       return
2145     # first arg (optional) and second arg (first mandatory) are supported in LyX 2.0.x
2146     revert_Argument_to_TeX_brace(document, i, 0, 3, 3, False, False)
2147     i += 1
2148
2149
2150 def convert_Initials(document):
2151   " Converts ERT of Initial to InsetArgument "
2152   i = 0
2153   while True:
2154     i = find_token(document.body, "\\begin_layout Initial", i)
2155     if i == -1:
2156       return
2157     convert_TeX_brace_to_Argument(document, i, 3, 3, False, False, False)
2158     i += 1
2159
2160
2161 def revert_literate(document):
2162     " Revert Literate document to old format "
2163     if del_token(document.header, "noweb", 0):
2164       document.textclass = "literate-" + document.textclass
2165       i = 0
2166       while True:
2167         i = find_token(document.body, "\\begin_layout Chunk", i)
2168         if i == -1:
2169           break
2170         document.body[i] = "\\begin_layout Scrap"
2171         i += 1
2172
2173
2174 def convert_literate(document):
2175     " Convert Literate document to new format"
2176     i = find_token(document.header, "\\textclass", 0)
2177     if (i != -1) and "literate-" in document.header[i]:
2178       document.textclass = document.header[i].replace("\\textclass literate-", "")
2179       j = find_token(document.header, "\\begin_modules", 0)
2180       if (j != -1):
2181         document.header.insert(j + 1, "noweb")
2182       else:
2183         document.header.insert(i + 1, "\\end_modules")
2184         document.header.insert(i + 1, "noweb")
2185         document.header.insert(i + 1, "\\begin_modules")
2186       i = 0
2187       while True:
2188         i = find_token(document.body, "\\begin_layout Scrap", i)
2189         if i == -1:
2190           break
2191         document.body[i] = "\\begin_layout Chunk"
2192         i += 1
2193
2194
2195 def revert_itemargs(document):
2196     " Reverts \\item arguments to TeX-code "
2197     i = 0
2198     while True:
2199         i = find_token(document.body, "\\begin_inset Argument item:", i)
2200         if i == -1:
2201             return
2202         j = find_end_of_inset(document.body, i)
2203         # Find containing paragraph layout
2204         parent = get_containing_layout(document.body, i)
2205         if parent == False:
2206             document.warning("Malformed LyX document: Can't find parent paragraph layout")
2207             i += 1
2208             continue
2209         parbeg = parent[3]
2210         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2211         endPlain = find_end_of_layout(document.body, beginPlain)
2212         content = document.body[beginPlain + 1 : endPlain]
2213         del document.body[i:j+1]
2214         subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
2215         document.body[parbeg : parbeg] = subst
2216         i += 1
2217
2218
2219 def revert_garamondx_newtxmath(document):
2220     " Revert native garamond newtxmath definition to LaTeX "
2221
2222     i = find_token(document.header, "\\font_math", 0)
2223     if i == -1:
2224        return
2225     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
2226         val = get_value(document.header, "\\font_math", i)
2227         if val == "garamondx-ntxm":
2228             add_to_preamble(document, "\\usepackage[garamondx]{newtxmath}")
2229             document.header[i] = "\\font_math auto"
2230
2231
2232 def revert_garamondx(document):
2233     " Revert native garamond font definition to LaTeX "
2234
2235     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
2236         i = find_token(document.header, "\\font_roman garamondx", 0)
2237         if i != -1:
2238             osf = False
2239             j = find_token(document.header, "\\font_osf true", 0)
2240             if j != -1:
2241                 osf = True
2242             preamble = "\\usepackage"
2243             if osf:
2244                 preamble += "[osfI]"
2245             preamble += "{garamondx}"
2246             add_to_preamble(document, [preamble])
2247             document.header[i] = "\\font_roman default"
2248
2249
2250 def convert_beamerargs(document):
2251     " Converts beamer arguments to new layout "
2252
2253     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
2254     if document.textclass not in beamer_classes:
2255         return
2256
2257     shifted_layouts = ["Part", "Section", "Subsection", "Subsubsection"]
2258     list_layouts = ["Itemize", "Enumerate", "Description"]
2259     rx = re.compile(r'^\\begin_inset Argument (\d+)$')
2260
2261     i = 0
2262     while True:
2263         i = find_token(document.body, "\\begin_inset Argument", i)
2264         if i == -1:
2265             return
2266         # Find containing paragraph layout
2267         parent = get_containing_layout(document.body, i)
2268         if parent == False:
2269             document.warning("Malformed LyX document: Can't find parent paragraph layout")
2270             i += 1
2271             continue
2272         parbeg = parent[1]
2273         parend = parent[2]
2274         layoutname = parent[0]
2275         for p in range(parbeg, parend):
2276             if layoutname in shifted_layouts:
2277                 m = rx.match(document.body[p])
2278                 if m:
2279                     argnr = int(m.group(1))
2280                     argnr += 1
2281                     document.body[p] = "\\begin_inset Argument %d" % argnr
2282             if layoutname == "AgainFrame":
2283                 m = rx.match(document.body[p])
2284                 if m:
2285                     document.body[p] = "\\begin_inset Argument 3"
2286                     if document.body[p + 4] == "\\begin_inset ERT":
2287                         if document.body[p + 9].startswith("<"):
2288                             # This is an overlay specification
2289                             # strip off the <
2290                             document.body[p + 9] = document.body[p + 9][1:]
2291                             if document.body[p + 9].endswith(">"):
2292                                 # strip off the >
2293                                 document.body[p + 9] = document.body[p + 9][:-1]
2294                                 # Shift this one
2295                                 document.body[p] = "\\begin_inset Argument 2"
2296             if layoutname in list_layouts:
2297                 m = rx.match(document.body[p])
2298                 if m:
2299                     if m.group(1) == "1":
2300                         if document.body[p + 4] == "\\begin_inset ERT":
2301                             if document.body[p + 9].startswith("<"):
2302                                 # This is an overlay specification
2303                                 # strip off the <
2304                                 document.body[p + 9] = document.body[p + 9][1:]
2305                                 if document.body[p + 9].endswith(">"):
2306                                     # strip off the >
2307                                     document.body[p + 9] = document.body[p + 9][:-1]
2308                         elif document.body[p + 4].startswith("<"):
2309                             # This is an overlay specification (without ERT)
2310                             # strip off the <
2311                             document.body[p + 4] = document.body[p + 4][1:]
2312                             if document.body[p + 4].endswith(">"):
2313                                 # strip off the >
2314                                 document.body[p + 4] = document.body[p + 4][:-1]
2315                         elif layoutname != "Itemize":
2316                             # Shift this one
2317                             document.body[p] = "\\begin_inset Argument 2"
2318         i += 1
2319
2320
2321 #
2322 # Helper function for the frame conversion routines
2323 #
2324 # FIXME: This method currently requires the arguments to be either
2325 #        * In one (whole) ERT each: <ERT>[<arg1>]</ERT><ERT><arg2></ERT><ERT>[arg3]</ERT>
2326 #        * Altogether in one whole ERT: <ERT>[<arg1>]<arg2>[arg3]</ERT>
2327 #        If individual arguments mix ERT and non-ERT or are splitted
2328 #        over several ERTs, the parsing fails.
2329 def convert_beamerframeargs(document, i, parbeg):
2330     ertend = i
2331     while True:
2332         if document.body[parbeg] != "\\begin_inset ERT":
2333             return ertend
2334         ertend = find_end_of_inset(document.body, parbeg)
2335         if ertend == -1:
2336             document.warning("Malformed LyX document: missing ERT \\end_inset")
2337             return ertend
2338         ertcont = parbeg + 5
2339         if document.body[ertcont].startswith("[<"):
2340             # This is a default overlay specification
2341             # strip off the [<
2342             document.body[ertcont] = document.body[ertcont][2:]
2343             if document.body[ertcont].endswith(">]"):
2344                 # strip off the >]
2345                 document.body[ertcont] = document.body[ertcont][:-2]
2346             elif document.body[ertcont].endswith("]"):
2347                 # divide the args
2348                 tok = document.body[ertcont].find('>][')
2349                 if tok != -1:
2350                     subst = [document.body[ertcont][:tok],
2351                               '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 3',
2352                               'status collapsed', '', '\\begin_layout Plain Layout',
2353                               document.body[ertcont][tok + 3:-1]]
2354                     document.body[ertcont : ertcont + 1] = subst
2355                     ertend += 11
2356             # Convert to ArgInset
2357             document.body[parbeg] = "\\begin_inset Argument 2"
2358         elif document.body[ertcont].startswith("<"):
2359             # This is an overlay specification
2360             # strip off the <
2361             document.body[ertcont] = document.body[ertcont][1:]
2362             if document.body[ertcont].endswith(">"):
2363                 # strip off the >
2364                 document.body[ertcont] = document.body[ertcont][:-1]
2365                 # Convert to ArgInset
2366                 document.body[parbeg] = "\\begin_inset Argument 1"
2367             elif document.body[ertcont].endswith(">]"):
2368                 # divide the args
2369                 tok = document.body[ertcont].find('>[<')
2370                 if tok != -1:
2371                     document.body[ertcont : ertcont + 1] = [document.body[ertcont][:tok],
2372                                                     '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 2',
2373                                                     'status collapsed', '', '\\begin_layout Plain Layout',
2374                                                     document.body[ertcont][tok + 3:-2]]
2375                 # Convert to ArgInset
2376                 document.body[parbeg] = "\\begin_inset Argument 1"
2377                 ertend += 11
2378             elif document.body[ertcont].endswith("]"):
2379                 # divide the args
2380                 tok = document.body[ertcont].find('>[<')
2381                 if tok != -1:
2382                     # divide the args
2383                     tokk = document.body[ertcont].find('>][')
2384                     if tokk != -1:
2385                         document.body[ertcont : ertcont + 1] = [document.body[ertcont][:tok],
2386                                                         '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 2',
2387                                                         'status collapsed', '', '\\begin_layout Plain Layout',
2388                                                         document.body[ertcont][tok + 3:tokk],
2389                                                         '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 3',
2390                                                         'status collapsed', '', '\\begin_layout Plain Layout',
2391                                                         document.body[ertcont][tokk + 3:-1]]
2392                         ertend += 22
2393                 else:
2394                     tokk = document.body[ertcont].find('>[')
2395                     if tokk != -1:
2396                         document.body[ertcont : ertcont + 1] = [document.body[ertcont][:tokk],
2397                                                         '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 3',
2398                                                         'status collapsed', '', '\\begin_layout Plain Layout',
2399                                                         document.body[ertcont][tokk + 2:-1]]
2400                         ertend += 11
2401                 # Convert to ArgInset
2402                 document.body[parbeg] = "\\begin_inset Argument 1"
2403         elif document.body[ertcont].startswith("["):
2404             # This is an ERT option
2405             # strip off the [
2406             document.body[ertcont] = document.body[ertcont][1:]
2407             if document.body[ertcont].endswith("]"):
2408                 # strip off the ]
2409                 document.body[ertcont] = document.body[ertcont][:-1]
2410                 # Convert to ArgInset
2411                 document.body[parbeg] = "\\begin_inset Argument 3"
2412         parbeg = ertend + 3
2413         continue
2414     return ertend
2415
2416
2417 def convert_againframe_args(document):
2418     " Converts beamer AgainFrame to new layout "
2419
2420     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
2421     if document.textclass not in beamer_classes:
2422         return
2423
2424     i = 0
2425     while True:
2426         i = find_token(document.body, "\\begin_layout AgainFrame", i)
2427         if i == -1:
2428             break
2429         parent = get_containing_layout(document.body, i)
2430         if parent[1] != i:
2431             document.warning("Wrong parent layout!")
2432         j = parent[2]
2433         parbeg = parent[3]
2434         if i != -1:
2435             # Convert ERT arguments
2436             # FIXME: See restrictions in convert_beamerframeargs method
2437             ertend = convert_beamerframeargs(document, i, parbeg)
2438             if ertend == -1:
2439                 break
2440         i = j
2441
2442
2443 def convert_corollary_args(document):
2444     " Converts beamer corrolary-style ERT arguments native InsetArgs "
2445
2446     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
2447     if document.textclass not in beamer_classes:
2448         return
2449
2450     corollary_layouts = ["Corollary", "Definition", "Definitions", "Example", "Examples", "Fact", "Proof", "Theorem"]
2451     for lay in corollary_layouts:
2452         i = 0
2453         while True:
2454             i = find_token_exact(document.body, "\\begin_layout " + lay, i)
2455             if i == -1:
2456                 break
2457             parent = get_containing_layout(document.body, i)
2458             if parent[1] != i:
2459                 document.warning("Wrong parent layout!")
2460             j = parent[2]
2461             parbeg = parent[3]
2462             if i != -1:
2463                 if document.body[parbeg] == "\\begin_inset ERT":
2464                     ertcontfirstline = parbeg + 5
2465                     # Find the last ERT in this paragraph (which might also be the first)
2466                     lastertbeg = find_token_backwards(document.body, "\\begin_inset ERT", j)
2467                     if lastertbeg == -1:
2468                         document.warning("Last ERT not found!")
2469                         break
2470                     lastertend = find_end_of_inset(document.body, lastertbeg)
2471                     if lastertend == -1:
2472                         document.warning("End of last ERT not found!")
2473                         break
2474                     ertcontlastline = lastertend - 3
2475                     if document.body[ertcontfirstline].startswith("<"):
2476                         # This is an overlay specification
2477                         # strip off the <
2478                         document.body[ertcontfirstline] = document.body[ertcontfirstline][1:]
2479                         if document.body[ertcontlastline].endswith(">"):
2480                             # strip off the >
2481                             document.body[ertcontlastline] = document.body[ertcontlastline][:-1]
2482                             if ertcontfirstline < ertcontlastline:
2483                                 # Multiline ERT. Might contain TeX code.  Embrace in ERT.
2484                                 document.body[ertcontlastline : ertcontlastline + 1] = [
2485                                                                     document.body[ertcontlastline], '\\end_layout', '', '\\end_inset']
2486                                 document.body[ertcontfirstline : ertcontfirstline + 1] = [
2487                                                                     '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 1',
2488                                                                     'status collapsed', '', '\\begin_layout Plain Layout',
2489                                                                     '\\begin_inset ERT', '', 'status open' '', '\\begin_layout Plain Layout',
2490                                                                     document.body[ertcontfirstline]]
2491                             else:
2492                                 # Convert to ArgInset
2493                                 document.body[parbeg] = "\\begin_inset Argument 1"
2494                         elif document.body[ertcontlastline].endswith("]"):
2495                             # divide the args
2496                             tok = document.body[ertcontfirstline].find('>[')
2497                             if tok != -1:
2498                                 if ertcontfirstline < ertcontlastline:
2499                                     # Multiline ERT. Might contain TeX code.  Embrace in ERT.
2500                                     document.body[ertcontlastline : ertcontlastline + 1] = [
2501                                                                         document.body[ertcontlastline], '\\end_layout', '', '\\end_inset']
2502                                     document.body[ertcontfirstline : ertcontfirstline + 1] = [document.body[ertcontfirstline][:tok],
2503                                                                         '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 2',
2504                                                                         'status collapsed', '', '\\begin_layout Plain Layout',
2505                                                                         '\\begin_inset ERT', '', 'status open' '', '\\begin_layout Plain Layout',
2506                                                                         document.body[ertcontfirstline][tok + 2:-1]]
2507                                 else:
2508                                     document.body[ertcontfirstline : ertcontfirstline + 1] = [document.body[ertcontfirstline][:tok],
2509                                                                         '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 2',
2510                                                                         'status collapsed', '', '\\begin_layout Plain Layout',
2511                                                                         document.body[ertcontfirstline][tok + 2:-1]]
2512                             # Convert to ArgInset
2513                             document.body[parbeg] = "\\begin_inset Argument 1"
2514                         i = j
2515                         continue
2516                     elif document.body[ertcontlastline].startswith("["):
2517                         if document.body[ertcontlastline].endswith("]"):
2518                             # This is an ERT option
2519                             # strip off the [
2520                             document.body[ertcontlastline] = document.body[ertcontlastline][1:]
2521                             # strip off the ]
2522                             document.body[ertcontlastline] = document.body[ertcontlastline][:-1]
2523                             # Convert to ArgInset
2524                             document.body[parbeg] = "\\begin_inset Argument 2"
2525                         else:
2526                             convert_TeX_brace_to_Argument(document, i, 2, 2, False, True, True)
2527                     i += 1
2528                     continue
2529             i = j
2530
2531
2532
2533 def convert_quote_args(document):
2534     " Converts beamer quote style ERT args to native InsetArgs "
2535
2536     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
2537     if document.textclass not in beamer_classes:
2538         return
2539
2540     quote_layouts = ["Uncover", "Only", "Quotation", "Quote", "Verse"]
2541     for lay in quote_layouts:
2542         i = 0
2543         while True:
2544             i = find_token(document.body, "\\begin_layout " + lay, i)
2545             if i == -1:
2546                 break
2547             parent = get_containing_layout(document.body, i)
2548             if parent[1] != i:
2549                 document.warning("Wrong parent layout!")
2550             j = parent[2]
2551             parbeg = parent[3]
2552             if i != -1:
2553                 if document.body[parbeg] == "\\begin_inset ERT":
2554                     if document.body[i + 6].startswith("<"):
2555                         # This is an overlay specification
2556                         # strip off the <
2557                         document.body[i + 6] = document.body[i + 6][1:]
2558                         if document.body[i + 6].endswith(">"):
2559                             # strip off the >
2560                             document.body[i + 6] = document.body[i + 6][:-1]
2561                             # Convert to ArgInset
2562                             document.body[i + 1] = "\\begin_inset Argument 1"
2563             i = j
2564
2565
2566 def cleanup_beamerargs(document):
2567     " Clean up empty ERTs (conversion artefacts) "
2568
2569     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
2570     if document.textclass not in beamer_classes:
2571         return
2572
2573     i = 0
2574     while True:
2575         i = find_token(document.body, "\\begin_inset Argument", i)
2576         if i == -1:
2577             return
2578         j = find_end_of_inset(document.body, i)
2579         if j == -1:
2580             document.warning("Malformed LyX document: Can't find end of Argument inset")
2581             i += 1
2582             continue
2583         while True:
2584             ertbeg = find_token(document.body, "\\begin_inset ERT", i, j)
2585             if ertbeg == -1:
2586                 break
2587             ertend = find_end_of_inset(document.body, ertbeg)
2588             if ertend == -1:
2589                 document.warning("Malformed LyX document: Can't find end of ERT inset")
2590                 break
2591             stripped = [line for line in document.body[ertbeg : ertend + 1] if line.strip()]
2592             if len(stripped) == 5:
2593                 # This is an empty ERT
2594                 offset = len(document.body[ertbeg : ertend + 1])
2595                 del document.body[ertbeg : ertend + 1]
2596                 j = j - offset
2597             else:
2598                 i = ertend
2599         i += 1
2600
2601
2602 def revert_beamerargs(document):
2603     " Reverts beamer arguments to old layout "
2604
2605     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
2606     if document.textclass not in beamer_classes:
2607         return
2608
2609     i = 0
2610     list_layouts = ["Itemize", "Enumerate", "Description"]
2611     headings = ["Part", "Section", "Section*", "Subsection", "Subsection*",
2612                 "Subsubsection", "Subsubsection*", "FrameSubtitle", "NoteItem"]
2613     quote_layouts = ["Uncover", "Only", "Quotation", "Quote", "Verse"]
2614     corollary_layouts = ["Corollary", "Definition", "Definitions", "Example", "Examples", "Fact", "Proof", "Theorem"]
2615     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
2616
2617     while True:
2618         i = find_token(document.body, "\\begin_inset Argument", i)
2619         if i == -1:
2620             return
2621         # Find containing paragraph layout
2622         parent = get_containing_layout(document.body, i)
2623         if parent == False:
2624             document.warning("Malformed LyX document: Can't find parent paragraph layout")
2625             i += 1
2626             continue
2627         parbeg = parent[1]
2628         parend = parent[2]
2629         realparbeg = parent[3]
2630         layoutname = parent[0]
2631         realparend = parend
2632         for p in range(parbeg, parend):
2633             if p >= realparend:
2634                 i = realparend
2635                 break
2636             if layoutname in headings:
2637                 m = rx.match(document.body[p])
2638                 if m:
2639                     argnr = m.group(1)
2640                     if argnr == "1":
2641                         # Find containing paragraph layout
2642                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2643                         endPlain = find_end_of_layout(document.body, beginPlain)
2644                         endInset = find_end_of_inset(document.body, p)
2645                         argcontent = document.body[beginPlain + 1 : endPlain]
2646                         # Adjust range end
2647                         realparend = realparend - len(document.body[p : endInset + 1])
2648                         # Remove arg inset
2649                         del document.body[p : endInset + 1]
2650                         if layoutname == "FrameSubtitle":
2651                             pre = put_cmd_in_ert("\\" + layoutname.lower() + "<") + argcontent + put_cmd_in_ert(">")
2652                         elif layoutname == "NoteItem":
2653                             pre = put_cmd_in_ert("\\note<") + argcontent + put_cmd_in_ert(">[item]")
2654                         elif layoutname.endswith('*'):
2655                             pre = put_cmd_in_ert("\\lyxframeend\\" + layoutname.lower()[:-1] + "<") + argcontent + put_cmd_in_ert(">*")
2656                         else:
2657                             pre = put_cmd_in_ert("\\lyxframeend\\" + layoutname.lower() + "<") + argcontent + put_cmd_in_ert(">")
2658                         secarg = find_token(document.body, "\\begin_inset Argument 2", parbeg, parend)
2659                         if secarg != -1:
2660                             # Find containing paragraph layout
2661                             beginPlain = find_token(document.body, "\\begin_layout Plain Layout", secarg)
2662                             endPlain = find_end_of_layout(document.body, beginPlain)
2663                             endInset = find_end_of_inset(document.body, secarg)
2664                             argcontent = document.body[beginPlain + 1 : endPlain]
2665                             # Adjust range end
2666                             realparend = realparend - len(document.body[secarg : endInset + 1])
2667                             del document.body[secarg : endInset + 1]
2668                             pre += put_cmd_in_ert("[") + argcontent + put_cmd_in_ert("]")
2669                         pre += put_cmd_in_ert("{")
2670                         document.body[parbeg] = "\\begin_layout Standard"
2671                         document.body[realparbeg : realparbeg] = pre
2672                         pe = find_end_of_layout(document.body, parbeg)
2673                         post = put_cmd_in_ert("}")
2674                         document.body[pe : pe] = post
2675                         realparend += len(pre) + len(post)
2676             if layoutname == "AgainFrame":
2677                 m = rx.match(document.body[p])
2678                 if m:
2679                     argnr = m.group(1)
2680                     if argnr == "3":
2681                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2682                         endPlain = find_end_of_layout(document.body, beginPlain)
2683                         endInset = find_end_of_inset(document.body, p)
2684                         content = document.body[beginPlain + 1 : endPlain]
2685                         # Adjust range end
2686                         realparend = realparend - len(document.body[p : endInset + 1])
2687                         # Remove arg inset
2688                         del document.body[p : endInset + 1]
2689                         subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
2690                         document.body[realparbeg : realparbeg] = subst
2691             if layoutname == "Overprint":
2692                 m = rx.match(document.body[p])
2693                 if m:
2694                     argnr = m.group(1)
2695                     if argnr == "1":
2696                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2697                         endPlain = find_end_of_layout(document.body, beginPlain)
2698                         endInset = find_end_of_inset(document.body, p)
2699                         content = document.body[beginPlain + 1 : endPlain]
2700                         # Adjust range end
2701                         realparend = realparend - len(document.body[p : endInset + 1])
2702                         # Remove arg inset
2703                         del document.body[p : endInset + 1]
2704                         subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
2705                         document.body[realparbeg : realparbeg] = subst
2706             if layoutname == "OverlayArea":
2707                 m = rx.match(document.body[p])
2708                 if m:
2709                     argnr = m.group(1)
2710                     if argnr == "2":
2711                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2712                         endPlain = find_end_of_layout(document.body, beginPlain)
2713                         endInset = find_end_of_inset(document.body, p)
2714                         content = document.body[beginPlain + 1 : endPlain]
2715                         # Adjust range end
2716                         realparend = realparend - len(document.body[p : endInset + 1])
2717                         # Remove arg inset
2718                         del document.body[p : endInset + 1]
2719                         subst = put_cmd_in_ert("{") + content + put_cmd_in_ert("}")
2720                         document.body[realparbeg : realparbeg] = subst
2721             if layoutname in list_layouts:
2722                 m = rx.match(document.body[p])
2723                 if m:
2724                     argnr = m.group(1)
2725                     if argnr == "1":
2726                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2727                         endPlain = find_end_of_layout(document.body, beginPlain)
2728                         endInset = find_end_of_inset(document.body, p)
2729                         content = document.body[beginPlain + 1 : endPlain]
2730                         subst = put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
2731                         realparend = realparend + len(subst) - len(content)
2732                         document.body[beginPlain + 1 : endPlain] = subst
2733                     elif argnr == "item:1":
2734                         j = find_end_of_inset(document.body, i)
2735                         # Find containing paragraph layout
2736                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2737                         endPlain = find_end_of_layout(document.body, beginPlain)
2738                         content = document.body[beginPlain + 1 : endPlain]
2739                         del document.body[i:j+1]
2740                         if layoutname == "Description":
2741                             # Description only has one (overlay) item arg
2742                             subst = put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
2743                             # This must be put after the first space (begin of decription body
2744                             # in LyX's arkward description list syntax)
2745                             # Try to find that place ...
2746                             rxx = re.compile(r'^([^\\ ]+ )(.*)$')
2747                             for q in range(parbeg, parend):
2748                                 m = rxx.match(document.body[q])
2749                                 if m:
2750                                     # We found it. Now insert the ERT argument just there:
2751                                     document.body[q : q] = [m.group(1), ''] + subst + ['', m.group(2)]
2752                                     break
2753                         else:
2754                             subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
2755                             document.body[realparbeg : realparbeg] = subst
2756                     elif argnr == "item:2":
2757                         j = find_end_of_inset(document.body, i)
2758                         # Find containing paragraph layout
2759                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2760                         endPlain = find_end_of_layout(document.body, beginPlain)
2761                         content = document.body[beginPlain + 1 : endPlain]
2762                         del document.body[i:j+1]
2763                         subst = put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
2764                         document.body[realparbeg : realparbeg] = subst
2765             if layoutname in quote_layouts:
2766                 m = rx.match(document.body[p])
2767                 if m:
2768                     argnr = m.group(1)
2769                     if argnr == "1":
2770                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2771                         endPlain = find_end_of_layout(document.body, beginPlain)
2772                         endInset = find_end_of_inset(document.body, p)
2773                         content = document.body[beginPlain + 1 : endPlain]
2774                         # Adjust range end
2775                         realparend = realparend - len(document.body[p : endInset + 1])
2776                         # Remove arg inset
2777                         del document.body[p : endInset + 1]
2778                         subst = put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
2779                         document.body[realparbeg : realparbeg] = subst
2780             if layoutname in corollary_layouts:
2781                 m = rx.match(document.body[p])
2782                 if m:
2783                     argnr = m.group(1)
2784                     if argnr == "2":
2785                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2786                         endPlain = find_end_of_layout(document.body, beginPlain)
2787                         endInset = find_end_of_inset(document.body, p)
2788                         content = document.body[beginPlain + 1 : endPlain]
2789                         # Adjust range end
2790                         realparend = realparend - len(document.body[p : endInset + 1])
2791                         # Remove arg inset
2792                         del document.body[p : endInset + 1]
2793                         subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
2794                         document.body[realparbeg : realparbeg] = subst
2795
2796         i = realparend
2797
2798
2799 def revert_beamerargs2(document):
2800     " Reverts beamer arguments to old layout, step 2 "
2801
2802     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
2803     if document.textclass not in beamer_classes:
2804         return
2805
2806     i = 0
2807     shifted_layouts = ["Part", "Section", "Subsection", "Subsubsection"]
2808     corollary_layouts = ["Corollary", "Definition", "Definitions", "Example", "Examples", "Fact", "Proof", "Theorem"]
2809     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
2810
2811     while True:
2812         i = find_token(document.body, "\\begin_inset Argument", i)
2813         if i == -1:
2814             return
2815         # Find containing paragraph layout
2816         parent = get_containing_layout(document.body, i)
2817         if parent == False:
2818             document.warning("Malformed LyX document: Can't find parent paragraph layout")
2819             i += 1
2820             continue
2821         parbeg = parent[1]
2822         parend = parent[2]
2823         realparbeg = parent[3]
2824         layoutname = parent[0]
2825         realparend = parend
2826         for p in range(parbeg, parend):
2827             if p >= realparend:
2828                 i = realparend
2829                 break
2830             if layoutname in shifted_layouts:
2831                 m = rx.match(document.body[p])
2832                 if m:
2833                     argnr = m.group(1)
2834                     if argnr == "2":
2835                         document.body[p] = "\\begin_inset Argument 1"
2836             if layoutname in corollary_layouts:
2837                 m = rx.match(document.body[p])
2838                 if m:
2839                     argnr = m.group(1)
2840                     if argnr == "1":
2841                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2842                         endPlain = find_end_of_layout(document.body, beginPlain)
2843                         endInset = find_end_of_inset(document.body, p)
2844                         content = document.body[beginPlain + 1 : endPlain]
2845                         # Adjust range end
2846                         realparend = realparend - len(document.body[p : endInset + 1])
2847                         # Remove arg inset
2848                         del document.body[p : endInset + 1]
2849                         subst = put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
2850                         document.body[realparbeg : realparbeg] = subst
2851             if layoutname == "OverlayArea":
2852                 m = rx.match(document.body[p])
2853                 if m:
2854                     argnr = m.group(1)
2855                     if argnr == "1":
2856                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2857                         endPlain = find_end_of_layout(document.body, beginPlain)
2858                         endInset = find_end_of_inset(document.body, p)
2859                         content = document.body[beginPlain + 1 : endPlain]
2860                         # Adjust range end
2861                         realparend = realparend - len(document.body[p : endInset + 1])
2862                         # Remove arg inset
2863                         del document.body[p : endInset + 1]
2864                         subst = put_cmd_in_ert("{") + content + put_cmd_in_ert("}")
2865                         document.body[realparbeg : realparbeg] = subst
2866             if layoutname == "AgainFrame":
2867                 m = rx.match(document.body[p])
2868                 if m:
2869                     argnr = m.group(1)
2870                     if argnr == "2":
2871                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2872                         endPlain = find_end_of_layout(document.body, beginPlain)
2873                         endInset = find_end_of_inset(document.body, p)
2874                         content = document.body[beginPlain + 1 : endPlain]
2875                         # Adjust range end
2876                         realparend = realparend - len(document.body[p : endInset + 1])
2877                         # Remove arg inset
2878                         del document.body[p : endInset + 1]
2879                         subst = put_cmd_in_ert("[<") + content + put_cmd_in_ert(">]")
2880                         document.body[realparbeg : realparbeg] = subst
2881         i = realparend
2882
2883
2884 def revert_beamerargs3(document):
2885     " Reverts beamer arguments to old layout, step 3 "
2886
2887     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
2888     if document.textclass not in beamer_classes:
2889         return
2890
2891     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
2892     i = 0
2893     while True:
2894         i = find_token(document.body, "\\begin_inset Argument", i)
2895         if i == -1:
2896             return
2897         # Find containing paragraph layout
2898         parent = get_containing_layout(document.body, i)
2899         if parent == False:
2900             document.warning("Malformed LyX document: Can't find parent paragraph layout")
2901             i += 1
2902             continue
2903         parbeg = parent[1]
2904         parend = parent[2]
2905         realparbeg = parent[3]
2906         layoutname = parent[0]
2907         realparend = parend
2908         for p in range(parbeg, parend):
2909             if p >= realparend:
2910                 i = realparend
2911                 break
2912             if layoutname == "AgainFrame":
2913                 m = rx.match(document.body[p])
2914                 if m:
2915                     argnr = m.group(1)
2916                     if argnr == "1":
2917                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2918                         endPlain = find_end_of_layout(document.body, beginPlain)
2919                         endInset = find_end_of_inset(document.body, p)
2920                         content = document.body[beginPlain + 1 : endPlain]
2921                         # Adjust range end
2922                         realparend = realparend - len(document.body[p : endInset + 1])
2923                         # Remove arg inset
2924                         del document.body[p : endInset + 1]
2925                         subst = put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
2926                         document.body[realparbeg : realparbeg] = subst
2927         i = realparend
2928
2929
2930 def revert_beamerflex(document):
2931     " Reverts beamer Flex insets "
2932
2933     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
2934     if document.textclass not in beamer_classes:
2935         return
2936
2937     new_flexes = {"Bold" : "\\textbf", "Emphasize" : "\\emph", "Only" : "\\only",
2938                   "Uncover" : "\\uncover", "Visible" : "\\visible",
2939                   "Invisible" : "\\invisible", "Alternative" : "\\alt",
2940                   "Beamer_Note" : "\\note"}
2941     old_flexes = {"Alert" : "\\alert", "Structure" : "\\structure"}
2942     rx = re.compile(r'^\\begin_inset Flex (.+)$')
2943
2944     i = 0
2945     while True:
2946         i = find_token(document.body, "\\begin_inset Flex", i)
2947         if i == -1:
2948             return
2949         m = rx.match(document.body[i])
2950         if m:
2951             flextype = m.group(1)
2952             z = find_end_of_inset(document.body, i)
2953             if z == -1:
2954                 document.warning("Can't find end of Flex " + flextype + " inset.")
2955                 i += 1
2956                 continue
2957             if flextype in new_flexes:
2958                 pre = put_cmd_in_ert(new_flexes[flextype])
2959                 arg = find_token(document.body, "\\begin_inset Argument 1", i, z)
2960                 if arg != -1:
2961                     argend = find_end_of_inset(document.body, arg)
2962                     if argend == -1:
2963                         document.warning("Can't find end of Argument!")
2964                         i += 1
2965                         continue
2966                     # Find containing paragraph layout
2967                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
2968                     endPlain = find_end_of_layout(document.body, beginPlain)
2969                     argcontent = document.body[beginPlain + 1 : endPlain]
2970                     # Adjust range end
2971                     z = z - len(document.body[arg : argend + 1])
2972                     # Remove arg inset
2973                     del document.body[arg : argend + 1]
2974                     pre += put_cmd_in_ert("<") + argcontent + put_cmd_in_ert(">")
2975                 arg = find_token(document.body, "\\begin_inset Argument 2", i, z)
2976                 if arg != -1:
2977                     argend = find_end_of_inset(document.body, arg)
2978                     if argend == -1:
2979                         document.warning("Can't find end of Argument!")
2980                         i += 1
2981                         continue
2982                     # Find containing paragraph layout
2983                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
2984                     endPlain = find_end_of_layout(document.body, beginPlain)
2985                     argcontent = document.body[beginPlain + 1 : endPlain]
2986                     # Adjust range end
2987                     z = z - len(document.body[arg : argend + 1])
2988                     # Remove arg inset
2989                     del document.body[arg : argend + 1]
2990                     if flextype == "Alternative":
2991                         pre += put_cmd_in_ert("{") + argcontent + put_cmd_in_ert("}")
2992                     else:
2993                         pre += put_cmd_in_ert("[") + argcontent + put_cmd_in_ert("]")
2994                 pre += put_cmd_in_ert("{")
2995                 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2996                 endPlain = find_end_of_layout(document.body, beginPlain)
2997                 # Adjust range end
2998                 z = z - len(document.body[i : beginPlain + 1])
2999                 z += len(pre)
3000                 document.body[i : beginPlain + 1] = pre
3001                 post = put_cmd_in_ert("}")
3002                 document.body[z - 2 : z + 1] = post
3003             elif flextype in old_flexes:
3004                 pre = put_cmd_in_ert(old_flexes[flextype])
3005                 arg = find_token(document.body, "\\begin_inset Argument 1", i, z)
3006                 if arg == -1:
3007                     i += 1
3008                     continue
3009                 argend = find_end_of_inset(document.body, arg)
3010                 if argend == -1:
3011                     document.warning("Can't find end of Argument!")
3012                     i += 1
3013                     continue
3014                 # Find containing paragraph layout
3015                 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3016                 endPlain = find_end_of_layout(document.body, beginPlain)
3017                 argcontent = document.body[beginPlain + 1 : endPlain]
3018                 # Adjust range end
3019                 z = z - len(document.body[arg : argend + 1])
3020                 # Remove arg inset
3021                 del document.body[arg : argend + 1]
3022                 pre += put_cmd_in_ert("<") + argcontent + put_cmd_in_ert(">")
3023                 pre += put_cmd_in_ert("{")
3024                 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
3025                 endPlain = find_end_of_layout(document.body, beginPlain)
3026                 # Adjust range end
3027                 z = z - len(document.body[i : beginPlain + 1])
3028                 z += len(pre)
3029                 document.body[i : beginPlain + 1] = pre
3030                 post = put_cmd_in_ert("}")
3031                 document.body[z - 2 : z + 1] = post
3032
3033         i += 1
3034
3035
3036 def revert_beamerblocks(document):
3037     " Reverts beamer block arguments to ERT "
3038
3039     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3040     if document.textclass not in beamer_classes:
3041         return
3042
3043     blocks = ["Block", "ExampleBlock", "AlertBlock"]
3044
3045     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
3046     i = 0
3047     while True:
3048         i = find_token(document.body, "\\begin_inset Argument", i)
3049         if i == -1:
3050             return
3051         # Find containing paragraph layout
3052         parent = get_containing_layout(document.body, i)
3053         if parent == False:
3054             document.warning("Malformed LyX document: Can't find parent paragraph layout")
3055             i += 1
3056             continue
3057         parbeg = parent[1]
3058         parend = parent[2]
3059         realparbeg = parent[3]
3060         layoutname = parent[0]
3061         realparend = parend
3062         for p in range(parbeg, parend):
3063             if p >= realparend:
3064                 i = realparend
3065                 break
3066             if layoutname in blocks:
3067                 m = rx.match(document.body[p])
3068                 if m:
3069                     argnr = m.group(1)
3070                     if argnr == "1":
3071                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
3072                         endPlain = find_end_of_layout(document.body, beginPlain)
3073                         endInset = find_end_of_inset(document.body, p)
3074                         content = document.body[beginPlain + 1 : endPlain]
3075                         # Adjust range end
3076                         realparend = realparend - len(document.body[p : endInset + 1])
3077                         # Remove arg inset
3078                         del document.body[p : endInset + 1]
3079                         subst = put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
3080                         document.body[realparbeg : realparbeg] = subst
3081                     elif argnr == "2":
3082                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
3083                         endPlain = find_end_of_layout(document.body, beginPlain)
3084                         endInset = find_end_of_inset(document.body, p)
3085                         content = document.body[beginPlain + 1 : endPlain]
3086                         # Adjust range end
3087                         realparend = realparend - len(document.body[p : endInset + 1])
3088                         # Remove arg inset
3089                         del document.body[p : endInset + 1]
3090                         subst = put_cmd_in_ert("{") + content + put_cmd_in_ert("}")
3091                         document.body[realparbeg : realparbeg] = subst
3092         i = realparend
3093
3094
3095
3096 def convert_beamerblocks(document):
3097     " Converts beamer block ERT args to native InsetArgs "
3098
3099     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3100     if document.textclass not in beamer_classes:
3101         return
3102
3103     blocks = ["Block", "ExampleBlock", "AlertBlock"]
3104     for lay in blocks:
3105         i = 0
3106         while True:
3107             i = find_token_exact(document.body, "\\begin_layout " + lay, i)
3108             if i == -1:
3109                 break
3110             parent = get_containing_layout(document.body, i)
3111             if parent == False or parent[1] != i:
3112                 document.warning("Wrong parent layout!")
3113                 i += 1
3114                 continue
3115             parbeg = parent[3]
3116             parend = parent[2]
3117             j = parend
3118             if i != -1:
3119                 # If the paragraph starts with a language switch, adjust parbeg
3120                 if len(document.body[parbeg]) == 0 and parbeg < parend \
3121                 and document.body[parbeg + 1].startswith("\\lang"):
3122                     parbeg += 2
3123                 if document.body[parbeg] == "\\begin_inset ERT":
3124                     ertcontfirstline = parbeg + 5
3125                     lastertbeg = -1
3126                     lastertend = -1
3127                     while True:
3128                         # Find the last ERT in this paragraph used for arguments
3129                         # (which might also be the first)
3130                         lastertbeg = find_token_backwards(document.body, "\\begin_inset ERT", j)
3131                         if lastertbeg == -1:
3132                             document.warning("Last ERT not found!")
3133                             break
3134                         lastertend = find_end_of_inset(document.body, lastertbeg)
3135                         if lastertend == -1:
3136                             document.warning("End of last ERT not found!")
3137                             break
3138                         # Is this ERT really used for an argument?
3139                         # Note: This will fail when non-argument ERTs actually use brackets
3140                         #       (e.g. \pause{})
3141                         regexp = re.compile(r'.*[>\]\}]', re.IGNORECASE)
3142                         cbracket = find_re(document.body, regexp, lastertbeg, lastertend)
3143                         if cbracket != -1:
3144                             break
3145                         if lastertbeg == parbeg:
3146                             break
3147                         j = lastertbeg - 1
3148                     if lastertbeg == -1 or lastertend == -1:
3149                         break
3150                     ertcontlastline = lastertend - 3
3151                     while True:
3152                         if document.body[ertcontfirstline].lstrip().startswith("<"):
3153                             # This is an overlay specification
3154                             # strip off the <
3155                             document.body[ertcontfirstline] = document.body[ertcontfirstline].lstrip()[1:]
3156                             if document.body[ertcontlastline].rstrip().endswith(">"):
3157                                 # strip off the >
3158                                 document.body[ertcontlastline] = document.body[ertcontlastline].rstrip()[:-1]
3159                                 # Convert to ArgInset
3160                                 document.body[parbeg] = "\\begin_inset Argument 1"
3161                             elif document.body[ertcontlastline].rstrip().endswith("}"):
3162                                 # strip off the }
3163                                 document.body[ertcontlastline] = document.body[ertcontlastline].rstrip()[:-1]
3164                                 # divide the args
3165                                 ertcontdivline = ertcontfirstline
3166                                 tok = document.body[ertcontdivline].find('>{')
3167                                 if tok == -1:
3168                                     regexp = re.compile(r'.*>\{', re.IGNORECASE)
3169                                     ertcontdivline = find_re(document.body, regexp, ertcontfirstline, lastertend)
3170                                     tok = document.body[ertcontdivline].find('>{')
3171                                 if tok != -1:
3172                                     if ertcontfirstline < ertcontlastline:
3173                                         # Multiline ERT. Might contain TeX code.  Embrace in ERT.
3174                                         document.body[ertcontlastline : ertcontlastline + 1] = [
3175                                                                             document.body[ertcontlastline], '\\end_layout', '', '\\end_inset']
3176                                         if ertcontdivline == ertcontfirstline:
3177                                             document.body[ertcontdivline : ertcontdivline + 1] = [document.body[ertcontdivline][:tok],
3178                                                                                 '\\end_layout', '', '\\end_inset', '',
3179                                                                                 '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 2',
3180                                                                                 'status collapsed', '', '\\begin_layout Plain Layout',
3181                                                                                 '\\begin_inset ERT', '', 'status open' '', '\\begin_layout Plain Layout',
3182                                                                                 document.body[ertcontdivline][tok + 2:]]
3183                                         else:
3184                                             document.body[ertcontdivline : ertcontdivline + 1] = [document.body[ertcontdivline][:tok],
3185                                                                                 '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 2',
3186                                                                                 'status collapsed', '', '\\begin_layout Plain Layout',
3187                                                                                 '\\begin_inset ERT', '', 'status open' '', '\\begin_layout Plain Layout',
3188                                                                                 document.body[ertcontdivline][tok + 2:]]
3189                                     else:
3190                                         document.body[ertcontdivline : ertcontdivline + 1] = [document.body[ertcontdivline][:tok],
3191                                                                             '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 2',
3192                                                                             'status collapsed', '', '\\begin_layout Plain Layout',
3193                                                                             document.body[ertcontdivline][tok + 2:]]
3194                                 else:
3195                                     # check if have delimiters in two different ERTs
3196                                     tok = document.body[ertcontdivline].find('>')
3197                                     if tok == -1:
3198                                         regexp = re.compile(r'.*>', re.IGNORECASE)
3199                                         ertcontdivline = find_re(document.body, regexp, ertcontfirstline, lastertend)
3200                                         tok = document.body[ertcontdivline].find('>')
3201                                     if tok != -1:
3202                                         tokk = document.body[ertcontdivline].find('{')
3203                                         if tokk == -1:
3204                                             regexp = re.compile(r'.*\{', re.IGNORECASE)
3205                                             ertcontdivlinetwo = find_re(document.body, regexp, ertcontfirstline, lastertend)
3206                                             tokk = document.body[ertcontdivlinetwo].find('{')
3207                                         if tokk != -1:
3208                                             if ertcontfirstline < ertcontlastline:
3209                                                 # Multiline ERT. Might contain TeX code.  Embrace in ERT.
3210                                                 document.body[ertcontlastline : ertcontlastline + 1] = [
3211                                                                                     document.body[ertcontlastline], '\\end_layout', '', '\\end_inset']
3212                                                 document.body[ertcontdivline : ertcontdivlinetwo + 1] = [document.body[ertcontdivline][:tok],
3213                                                                                     '\\end_layout', '', '\\end_inset', '', '\\end_layout', '',
3214                                                                                     '\\end_inset', '', '', '\\begin_inset Argument 2',
3215                                                                                     'status collapsed', '', '\\begin_layout Plain Layout',
3216                                                                                     '\\begin_inset ERT', '', 'status open' '', '\\begin_layout Plain Layout',
3217                                                                                     document.body[ertcontdivlinetwo][tokk + 1:]]
3218                                             else:
3219                                                 document.body[ertcontdivline : ertcontdivlinetwo + 1] = [document.body[ertcontdivline][:tok],
3220                                                                                     '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 2',
3221                                                                                     'status collapsed', '', '\\begin_layout Plain Layout',
3222                                                                                     document.body[ertcontdivlinetwo][tokk + 1:]]
3223                                 # Convert to ArgInset
3224                                 if ertcontfirstline < ertcontlastline:
3225                                     # Multiline ERT. Might contain TeX code.  Embrace in ERT.
3226                                     document.body[parbeg : parbeg + 1] = ['\\begin_inset Argument 1',
3227                                                                         'status collapsed', '', '\\begin_layout Plain Layout',
3228                                                                         '\\begin_inset ERT', '']
3229                                 else:
3230                                     document.body[parbeg] = "\\begin_inset Argument 1"
3231                         elif document.body[ertcontfirstline].lstrip().startswith("{"):
3232                             # This is the block title
3233                             if document.body[ertcontlastline].rstrip().endswith("}"):
3234                                 # strip off the braces
3235                                 document.body[ertcontfirstline] = document.body[ertcontfirstline].lstrip()[1:]
3236                                 document.body[ertcontlastline] = document.body[ertcontlastline].rstrip()[:-1]
3237                                 if ertcontfirstline < ertcontlastline:
3238                                     # Multiline ERT. Might contain TeX code.  Embrace in ERT.
3239                                     document.body[parend : parend + 1] = [
3240                                                                         document.body[parend], '\\end_inset', '', '\\end_layout']
3241                                     document.body[parbeg : parbeg + 1] = ['\\begin_inset Argument 2',
3242                                                                         'status collapsed', '', '\\begin_layout Plain Layout',
3243                                                                         '\\begin_inset ERT', '']
3244                                 else:
3245                                     # Convert to ArgInset
3246                                     document.body[parbeg] = "\\begin_inset Argument 2"
3247                             # the overlay argument can also follow the title, so ...
3248                             elif document.body[ertcontlastline].rstrip().endswith(">"):
3249                                 # strip off the {
3250                                 document.body[ertcontfirstline] = document.body[ertcontfirstline].lstrip()[1:]
3251                                 # strip off the >
3252                                 document.body[ertcontlastline] = document.body[ertcontlastline].rstrip()[:-1]
3253                                 # divide the args
3254                                 ertcontdivline = ertcontfirstline
3255                                 tok = document.body[ertcontdivline].find('}<')
3256                                 if tok == -1:
3257                                     regexp = re.compile(r'.*\}<', re.IGNORECASE)
3258                                     ertcontdivline = find_re(document.body, regexp, ertcontfirstline, lastertend)
3259                                     tok = document.body[ertcontdivline].find('}<')
3260                                 if tok != -1:
3261                                     if ertcontfirstline < ertcontlastline:
3262                                         # Multiline ERT. Might contain TeX code.  Embrace in ERT.
3263                                         document.body[ertcontlastline : ertcontlastline + 1] = [
3264                                                                             document.body[ertcontlastline], '\\end_layout', '', '\\end_inset']
3265                                         if ertcontdivline == ertcontfirstline:
3266                                             document.body[ertcontdivline : ertcontdivline + 1] = [document.body[ertcontdivline][:tok],
3267                                                                                 '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 1',
3268                                                                                 'status collapsed', '', '\\begin_layout Plain Layout',
3269                                                                                 '\\begin_inset ERT', '', 'status open' '', '\\begin_layout Plain Layout',
3270                                                                                 document.body[ertcontdivline][tok + 2:]]
3271                                         else:
3272                                             document.body[ertcontdivline : ertcontdivline + 1] = [document.body[ertcontdivline][:tok],
3273                                                                                 '\\end_layout', '', '\\end_inset', '',
3274                                                                                 '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 1',
3275                                                                                 'status collapsed', '', '\\begin_layout Plain Layout',
3276                                                                                 '\\begin_inset ERT', '', 'status open' '', '\\begin_layout Plain Layout',
3277                                                                                 document.body[ertcontdivline][tok + 2:]]
3278                                     else:
3279                                         document.body[ertcontdivline : ertcontdivline + 1] = [document.body[ertcontdivline][:tok],
3280                                                                             '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 1',
3281                                                                             'status collapsed', '', '\\begin_layout Plain Layout',
3282                                                                             document.body[ertcontdivline][tok + 2:]]
3283                                 else:
3284                                     # check if have delimiters in two different ERTs
3285                                     tok = document.body[ertcontdivline].find('}')
3286                                     if tok == -1:
3287                                         regexp = re.compile(r'.*\}', re.IGNORECASE)
3288                                         ertcontdivline = find_re(document.body, regexp, ertcontfirstline, lastertend)
3289                                         tok = document.body[ertcontdivline].find('}')
3290                                         if tok != -1:
3291                                             tokk = document.body[ertcontdivline].find('<')
3292                                             if tokk == -1:
3293                                                 regexp = re.compile(r'.*<', re.IGNORECASE)
3294                                                 ertcontdivlinetwo = find_re(document.body, regexp, ertcontfirstline, lastertend)
3295                                                 tokk = document.body[ertcontdivlinetwo].find('<')
3296                                                 if tokk != -1:
3297                                                     if ertcontfirstline < ertcontlastline:
3298                                                         # Multiline ERT. Might contain TeX code.  Embrace in ERT.
3299                                                         document.body[ertcontlastline : ertcontlastline + 1] = [
3300                                                                                             document.body[ertcontlastline], '\\end_layout', '', '\\end_inset']
3301                                                         document.body[ertcontdivline : ertcontdivlinetwo + 1] = [document.body[ertcontdivline][:tok],
3302                                                                                             '\\end_layout', '', '\\end_inset', '', '\\end_layout', '',
3303                                                                                             '\\end_inset', '', '', '\\begin_inset Argument 1',
3304                                                                                             'status collapsed', '', '\\begin_layout Plain Layout',
3305                                                                                             '\\begin_inset ERT', '', 'status open' '', '\\begin_layout Plain Layout',
3306                                                                                             document.body[ertcontdivlinetwo][tokk + 1:]]
3307                                                     else:
3308                                                         document.body[ertcontdivline : ertcontdivlinetwo + 1] = [document.body[ertcontdivline][:tok],
3309                                                                                             '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 1',
3310                                                                                             'status collapsed', '', '\\begin_layout Plain Layout',
3311                                                                                             document.body[ertcontdivlinetwo][tokk + 1:]]
3312                                 # Convert to ArgInset
3313                                 if ertcontfirstline < ertcontlastline:
3314                                     # Multiline ERT. Might contain TeX code.  Embrace in ERT.
3315                                     document.body[parbeg : parbeg + 1] = ['\\begin_inset Argument 2',
3316                                                                         'status collapsed', '', '\\begin_layout Plain Layout',
3317                                                                         '\\begin_inset ERT', '']
3318                                 else:
3319                                     document.body[parbeg] = "\\begin_inset Argument 2"
3320                             elif count_pars_in_inset(document.body, ertcontfirstline) > 1:
3321                                 # Multipar ERT. Skip this.
3322                                 break
3323                             else:
3324                                 # ERT has contents after the closing bracket. We cannot convert this.
3325                                 # convert_TeX_brace_to_Argument cannot either.
3326                                 #convert_TeX_brace_to_Argument(document, i, 2, 2, False, True, False)
3327                                 break
3328                         else:
3329                             break
3330                         j = find_end_of_layout(document.body, i)
3331                         if j == -1:
3332                             document.warning("end of layout not found!")
3333                         k = find_token(document.body, "\\begin_inset Argument", i, j)
3334                         if k == -1:
3335                             document.warning("InsetArgument not found!")
3336                             break
3337                         l = find_end_of_inset(document.body, k)
3338                         m = find_token(document.body, "\\begin_inset ERT", l, j)
3339                         if m == -1:
3340                             break
3341                         ertcontfirstline = m + 5
3342                         parbeg = m
3343             i = j
3344
3345
3346 def convert_overprint(document):
3347     " Convert old beamer overprint layouts to ERT "
3348
3349     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3350     if document.textclass not in beamer_classes:
3351         return
3352
3353     i = 0
3354     while True:
3355         i = find_token(document.body, "\\begin_layout Overprint", i)
3356         if i == -1:
3357             return
3358         # Find end of sequence
3359         j = find_end_of_sequence(document.body, i)
3360         if j == -1:
3361             document.warning("Malformed LyX document. Cannot find end of Overprint sequence!")
3362             i += 1
3363             continue
3364         endseq = j
3365         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\begin{overprint}")
3366         esubst = list()
3367         if document.body[j] == "\\end_deeper":
3368             esubst = ["", "\\begin_layout Standard"] + put_cmd_in_ert("\\end{overprint}") + ["\\end_layout"]
3369         else:
3370             esubst = ["\\end_layout", "", "\\begin_layout Standard"] + put_cmd_in_ert("\\end{overprint}") + ["\\end_layout"]
3371         endseq = endseq + len(esubst) - len(document.body[j : j])
3372         document.body[j : j] = esubst
3373         argbeg = find_token(document.body, "\\begin_inset Argument 1", i, j)
3374         if argbeg != -1:
3375             argend = find_end_of_layout(document.body, argbeg)
3376             if argend == -1:
3377                 document.warning("Malformed LyX document. Cannot find end of Overprint argument!")
3378                 i += 1
3379                 continue
3380             beginPlain = find_token(document.body, "\\begin_layout Plain Layout", argbeg)
3381             endPlain = find_end_of_layout(document.body, beginPlain)
3382             content = document.body[beginPlain + 1 : endPlain]
3383             # Adjust range end
3384             endseq = endseq - len(document.body[argbeg : argend + 1])
3385             # Remove arg inset
3386             del document.body[argbeg : argend + 1]
3387             subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
3388
3389         endseq = endseq - len(document.body[i : i])
3390         document.body[i : i] = subst + ["\\end_layout"]
3391         endseq += len(subst)
3392
3393         for p in range(i, endseq):
3394             if document.body[p] == "\\begin_layout Overprint":
3395                 document.body[p] = "\\begin_layout Standard"
3396
3397         i = endseq
3398
3399
3400 def revert_overprint(document):
3401     " Revert old beamer overprint layouts to ERT "
3402
3403     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3404     if document.textclass not in beamer_classes:
3405         return
3406
3407     i = 0
3408     while True:
3409         i = find_token(document.body, "\\begin_layout Overprint", i)
3410         if i == -1:
3411             return
3412         # Find end of sequence
3413         j = find_end_of_sequence(document.body, i)
3414         if j == -1:
3415             document.warning("Malformed LyX document. Cannot find end of Overprint sequence!")
3416             i += 1
3417             continue
3418         endseq = j
3419         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\begin{overprint}")
3420         esubst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\end{overprint}")
3421         endseq = endseq + len(esubst) - len(document.body[j : j])
3422         if document.body[j] == "\\end_deeper":
3423             document.body[j : j] = [""] + esubst + ["", "\\end_layout"]
3424         else:
3425             document.body[j : j] = ["\\end_layout", ""] + esubst
3426         r = i
3427         while r < j:
3428             if document.body[r] == "\\begin_deeper":
3429                 s = find_end_of(document.body, r, "\\begin_deeper", "\\end_deeper")
3430                 if s != -1:
3431                     document.body[r] = ""
3432                     document.body[s] = ""
3433                     r = s
3434                     continue
3435             r = r + 1
3436         argbeg = find_token(document.body, "\\begin_inset Argument 1", i, j)
3437         if argbeg != -1:
3438             # Is this really our argument?
3439             nested = find_token(document.body, "\\begin_deeper", i, argbeg)
3440             if nested != -1:
3441                 argend = find_end_of_inset(document.body, argbeg)
3442                 if argend == -1:
3443                     document.warning("Malformed LyX document. Cannot find end of Overprint argument!")
3444                     i += 1
3445                     continue
3446                 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", argbeg)
3447                 endPlain = find_end_of_layout(document.body, beginPlain)
3448                 content = document.body[beginPlain + 1 : endPlain]
3449                 # Adjust range end
3450                 endseq = endseq - len(document.body[argbeg : argend])
3451                 # Remove arg inset
3452                 del document.body[argbeg : argend + 1]
3453                 subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
3454
3455         endseq = endseq - len(document.body[i : i])
3456         document.body[i : i] = subst + ["\\end_layout"]
3457         endseq += len(subst)
3458
3459         p = i
3460         while True:
3461             if p >= endseq:
3462                 break
3463             if document.body[p] == "\\begin_layout Overprint":
3464                 q = find_end_of_layout(document.body, p)
3465                 if q == -1:
3466                     document.warning("Malformed LyX document. Cannot find end of Overprint layout!")
3467                     p += 1
3468                     continue
3469                 subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\onslide")
3470                 argbeg = find_token(document.body, "\\begin_inset Argument item:1", p, q)
3471                 if argbeg != -1:
3472                     argend = find_end_of_inset(document.body, argbeg)
3473                     if argend == -1:
3474                         document.warning("Malformed LyX document. Cannot find end of Overprint item argument!")
3475                         p += 1
3476                         continue
3477                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", argbeg)
3478                     endPlain = find_end_of_layout(document.body, beginPlain)
3479                     content = document.body[beginPlain + 1 : endPlain]
3480                     # Adjust range end
3481                     endseq = endseq - len(document.body[argbeg : argend + 1])
3482                     # Remove arg inset
3483                     del document.body[argbeg : argend + 1]
3484                     subst += put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
3485                 endseq = endseq - len(document.body[p : p + 1]) + len(subst)
3486                 document.body[p : p + 1] = subst
3487             p = p + 1
3488
3489         i = endseq
3490
3491
3492 def revert_frametitle(document):
3493     " Reverts beamer frametitle layout to ERT "
3494
3495     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3496     if document.textclass not in beamer_classes:
3497         return
3498
3499     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
3500     i = 0
3501     while True:
3502         i = find_token(document.body, "\\begin_layout FrameTitle", i)
3503         if i == -1:
3504             return
3505         j = find_end_of_layout(document.body, i)
3506         if j == -1:
3507             document.warning("Malformed LyX document: Can't find end of FrameTitle layout")
3508             i += 1
3509             continue
3510         endlay = j
3511         document.body[j : j] = put_cmd_in_ert("}") + document.body[j : j]
3512         endlay += len(put_cmd_in_ert("}"))
3513         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\frametitle")
3514         for p in range(i, j):
3515             if p >= endlay:
3516                 break
3517             m = rx.match(document.body[p])
3518             if m:
3519                 argnr = m.group(1)
3520                 if argnr == "1":
3521                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
3522                     endPlain = find_end_of_layout(document.body, beginPlain)
3523                     endInset = find_end_of_inset(document.body, p)
3524                     content = document.body[beginPlain + 1 : endPlain]
3525                     # Adjust range end
3526                     endlay = endlay - len(document.body[p : endInset + 1])
3527                     # Remove arg inset
3528                     del document.body[p : endInset + 1]
3529                     subst += put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
3530                 elif argnr == "2":
3531                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
3532                     endPlain = find_end_of_layout(document.body, beginPlain)
3533                     endInset = find_end_of_inset(document.body, p)
3534                     content = document.body[beginPlain + 1 : endPlain]
3535                     # Adjust range end
3536                     endlay = endlay - len(document.body[p : endInset + 1])
3537                     # Remove arg inset
3538                     del document.body[p : endInset + 1]
3539                     subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
3540
3541         subst += put_cmd_in_ert("{")
3542         document.body[i : i + 1] = subst
3543         i = endlay
3544
3545
3546 def convert_epigraph(document):
3547     " Converts memoir epigraph to new syntax "
3548
3549     if document.textclass != "memoir":
3550         return
3551
3552     i = 0
3553     while True:
3554         i = find_token(document.body, "\\begin_layout Epigraph", i)
3555         if i == -1:
3556             return
3557         j = find_end_of_layout(document.body, i)
3558         if j == -1:
3559             document.warning("Malformed LyX document: Can't find end of Epigraph layout")
3560             i += 1
3561             continue
3562         endlay = j
3563         subst = list()
3564         ert = find_token(document.body, "\\begin_inset ERT", i, j)
3565         if ert != -1:
3566             endInset = find_end_of_inset(document.body, ert)
3567             beginPlain = find_token(document.body, "\\begin_layout Plain Layout", ert)
3568             endPlain = find_end_of_layout(document.body, beginPlain)
3569             ertcont = beginPlain + 2
3570             if document.body[ertcont] == "}{":
3571                 # strip off the <
3572                 # Convert to ArgInset
3573                 endlay = endlay - 2 * len(document.body[j])
3574                 begsubst = ['\\begin_inset Argument post:1', 'status collapsed', '',
3575                             '\\begin_layout Plain Layout']
3576                 endsubst = ['\\end_layout', '', '\\end_inset', '', document.body[j]]
3577                 document.body[j : j + 1] = endsubst
3578                 document.body[endInset + 1 : endInset + 1] = begsubst
3579                 # Adjust range end
3580                 endlay += len(begsubst) + len(endsubst)
3581                 endlay = endlay - len(document.body[ert : endInset + 1])
3582                 del document.body[ert : endInset + 1]
3583
3584         i = endlay
3585
3586
3587 def revert_epigraph(document):
3588     " Reverts memoir epigraph argument to ERT "
3589
3590     if document.textclass != "memoir":
3591         return
3592
3593     i = 0
3594     while True:
3595         i = find_token(document.body, "\\begin_layout Epigraph", i)
3596         if i == -1:
3597             return
3598         j = find_end_of_layout(document.body, i)
3599         if j == -1:
3600             document.warning("Malformed LyX document: Can't find end of Epigraph layout")
3601             i += 1
3602             continue
3603         endlay = j
3604         subst = list()
3605         p = find_token(document.body, "\\begin_layout Argument post:1", i, j)
3606         if p != -1:
3607             beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
3608             endPlain = find_end_of_layout(document.body, beginPlain)
3609             endInset = find_end_of_inset(document.body, p)
3610             content = document.body[beginPlain + 1 : endPlain]
3611             # Adjust range end
3612             endlay = endlay - len(document.body[p : endInset + 1])
3613             # Remove arg inset
3614             del document.body[p : endInset + 1]
3615             subst += put_cmd_in_ert("}{") + content
3616         else:
3617             subst += put_cmd_in_ert("}{")
3618
3619         document.body[j : j] = subst + document.body[j : j]
3620         i = endlay
3621
3622
3623 def convert_captioninsets(document):
3624     " Converts caption insets to new syntax "
3625
3626     i = 0
3627     while True:
3628       i = find_token(document.body, "\\begin_inset Caption", i)
3629       if i == -1:
3630           return
3631       document.body[i] = "\\begin_inset Caption Standard"
3632       i += 1
3633
3634
3635 def revert_captioninsets(document):
3636     " Reverts caption insets to old syntax "
3637
3638     i = 0
3639     while True:
3640       i = find_token(document.body, "\\begin_inset Caption Standard", i)
3641       if i == -1:
3642           return
3643       document.body[i] = "\\begin_inset Caption"
3644       i += 1
3645
3646
3647 def convert_captionlayouts(document):
3648     " Convert caption layouts to caption insets. "
3649
3650     caption_dict = {
3651         "Captionabove":  "Above",
3652         "Captionbelow":  "Below",
3653         "FigCaption"  :  "FigCaption",
3654         "Table_Caption" :  "Table",
3655         "CenteredCaption" : "Centered",
3656         "Bicaption" : "Bicaption",
3657         }
3658
3659     for captype in caption_dict.keys():
3660         i = 0
3661         while True:
3662             i = find_token(document.body, "\\begin_layout " + captype, i)
3663             if i == -1:
3664                 break
3665             j = find_end_of_layout(document.body, i)
3666             if j == -1:
3667                 document.warning("Malformed LyX document: Missing `\\end_layout'.")
3668                 break
3669
3670             document.body[j:j] = ["\\end_layout", "", "\\end_inset", "", ""]
3671             document.body[i:i+1] = ["\\begin_layout %s" % document.default_layout,
3672                                     "\\begin_inset Caption %s" % caption_dict[captype], "",
3673                                     "\\begin_layout %s" % document.default_layout]
3674             i = j + 1
3675
3676
3677 def revert_captionlayouts(document):
3678     " Revert caption insets to caption layouts. "
3679
3680     caption_dict = {
3681         "Above" : "Captionabove",
3682         "Below" : "Captionbelow",
3683         "FigCaption"  :  "FigCaption",
3684         "Table" : "Table_Caption",
3685         "Centered" : "CenteredCaption",
3686         "Bicaption" : "Bicaption",
3687         }
3688
3689     i = 0
3690     rx = re.compile(r'^\\begin_inset Caption (\S+)$')
3691     while True:
3692         i = find_token(document.body, "\\begin_inset Caption", i)
3693         if i == -1:
3694             return
3695
3696         m = rx.match(document.body[i])
3697         val = ""
3698         if m:
3699             val = m.group(1)
3700         if val not in list(caption_dict.keys()):
3701             i += 1
3702             continue
3703
3704         # We either need to delete the previous \begin_layout line, or we
3705         # need to end the previous layout if this inset is not in the first
3706         # position of the paragraph.
3707         layout_before = find_token_backwards(document.body, "\\begin_layout", i)
3708         if layout_before == -1:
3709             document.warning("Malformed LyX document: Missing `\\begin_layout'.")
3710             return
3711         layout_line = document.body[layout_before]
3712         del_layout_before = True
3713         l = layout_before + 1
3714         while l < i:
3715             if document.body[l] != "":
3716                 del_layout_before = False
3717                 break
3718             l = l + 1
3719         if del_layout_before:
3720             del document.body[layout_before:i]
3721             i = layout_before
3722         else:
3723             document.body[i:i] = ["\\end_layout", ""]
3724             i = i + 2
3725
3726         # Find start of layout in the inset and end of inset
3727         j = find_token(document.body, "\\begin_layout", i)
3728         if j == -1:
3729             document.warning("Malformed LyX document: Missing `\\begin_layout'.")
3730             return
3731         k = find_end_of_inset(document.body, i)
3732         if k == -1:
3733             document.warning("Malformed LyX document: Missing `\\end_inset'.")
3734             return
3735
3736         # We either need to delete the following \end_layout line, or we need
3737         # to restart the old layout if this inset is not at the paragraph end.
3738         layout_after = find_token(document.body, "\\end_layout", k)
3739         if layout_after == -1:
3740             document.warning("Malformed LyX document: Missing `\\end_layout'.")
3741             return
3742         del_layout_after = True
3743         l = k + 1
3744         while l < layout_after:
3745             if document.body[l] != "":
3746                 del_layout_after = False
3747                 break
3748             l = l + 1
3749         if del_layout_after:
3750             del document.body[k+1:layout_after+1]
3751         else:
3752             document.body[k+1:k+1] = [layout_line, ""]
3753
3754         # delete \begin_layout and \end_inset and replace \begin_inset with
3755         # "\begin_layout XXX". This works because we can only have one
3756         # paragraph in the caption inset: The old \end_layout will be recycled.
3757         del document.body[k]
3758         if document.body[k] == "":
3759             del document.body[k]
3760         del document.body[j]
3761         if document.body[j] == "":
3762             del document.body[j]
3763         document.body[i] = "\\begin_layout %s" % caption_dict[val]
3764         if document.body[i+1] == "":
3765             del document.body[i+1]
3766         i += 1
3767
3768
3769 def revert_fragileframe(document):
3770     " Reverts beamer FragileFrame layout to ERT "
3771
3772     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3773     if document.textclass not in beamer_classes:
3774         return
3775
3776     i = 0
3777     while True:
3778         i = find_token(document.body, "\\begin_layout FragileFrame", i)
3779         if i == -1:
3780             return
3781         # Find end of sequence
3782         j = find_end_of_sequence(document.body, i)
3783         if j == -1:
3784             document.warning("Malformed LyX document. Cannot find end of FragileFrame sequence!")
3785             i += 1
3786             continue
3787         endseq = j
3788         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\begin{frame}")
3789         esubst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\end{frame}")
3790         endseq = endseq + len(esubst) - len(document.body[j : j])
3791         if document.body[j] == "\\end_deeper":
3792             document.body[j : j] = [""] + esubst + ["", "\\end_layout"]
3793         else:
3794             document.body[j : j] = esubst
3795         for q in range(i, j):
3796             if document.body[q] == "\\begin_layout FragileFrame":
3797                 document.body[q] = "\\begin_layout %s" % document.default_layout
3798         r = i
3799         while r < j:
3800             if document.body[r] == "\\begin_deeper":
3801                 s = find_end_of(document.body, r, "\\begin_deeper", "\\end_deeper")
3802                 if s != -1:
3803                     document.body[r] = ""
3804                     document.body[s] = ""
3805                     r = s
3806                     continue
3807             r = r + 1
3808         for p in range(1, 5):
3809             arg = find_token(document.body, "\\begin_inset Argument %d" % p, i, j)
3810             if arg != -1:
3811                 if p == 1:
3812                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3813                     endPlain = find_end_of_layout(document.body, beginPlain)
3814                     endInset = find_end_of_inset(document.body, arg)
3815                     content = document.body[beginPlain + 1 : endPlain]
3816                     # Adjust range end
3817                     j = j - len(document.body[arg : endInset + 1])
3818                     # Remove arg inset
3819                     del document.body[arg : endInset + 1]
3820                     subst += put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
3821                 elif p == 2:
3822                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3823                     endPlain = find_end_of_layout(document.body, beginPlain)
3824                     endInset = find_end_of_inset(document.body, arg)
3825                     content = document.body[beginPlain + 1 : endPlain]
3826                     # Adjust range end
3827                     j = j - len(document.body[arg : endInset + 1])
3828                     # Remove arg inset
3829                     del document.body[arg : endInset + 1]
3830                     subst += put_cmd_in_ert("[<") + content + put_cmd_in_ert(">]")
3831                 elif p == 3:
3832                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3833                     endPlain = find_end_of_layout(document.body, beginPlain)
3834                     endInset = find_end_of_inset(document.body, arg)
3835                     content = document.body[beginPlain + 1 : endPlain]
3836                     # Adjust range end
3837                     j = j - len(document.body[arg : endInset + 1])
3838                     # Remove arg inset
3839                     del document.body[arg : endInset + 1]
3840                     subst += put_cmd_in_ert("[fragile,") + content + put_cmd_in_ert("]")
3841                 elif p == 4:
3842                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3843                     endPlain = find_end_of_layout(document.body, beginPlain)
3844                     endInset = find_end_of_inset(document.body, arg)
3845                     content = document.body[beginPlain + 1 : endPlain]
3846                     # Adjust range end
3847                     j = j - len(document.body[arg : endInset + 1])
3848                     # Remove arg inset
3849                     del document.body[arg : endInset + 1]
3850                     subst += put_cmd_in_ert("{") + content + put_cmd_in_ert("}")
3851             elif p == 3:
3852                 subst += put_cmd_in_ert("[fragile]")
3853
3854         document.body[i : i + 1] = subst
3855         i = j
3856
3857
3858 def revert_newframes(document):
3859     " Reverts beamer Frame and PlainFrame layouts to old forms "
3860
3861     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3862     if document.textclass not in beamer_classes:
3863         return
3864
3865     frame_dict = {
3866         "Frame" : "BeginFrame",
3867         "PlainFrame" : "BeginPlainFrame",
3868         }
3869
3870     rx = re.compile(r'^\\begin_layout (\S+)$')
3871     i = 0
3872     while True:
3873         i = find_token(document.body, "\\begin_layout", i)
3874         if i == -1:
3875             return
3876
3877         m = rx.match(document.body[i])
3878         val = ""
3879         if m:
3880             val = m.group(1)
3881         if val not in list(frame_dict.keys()):
3882             i += 1
3883             continue
3884         # Find end of sequence
3885         j = find_end_of_sequence(document.body, i)
3886         if j == -1:
3887             document.warning("Malformed LyX document. Cannot find end of Frame sequence!")
3888             i += 1
3889             continue
3890         endseq = j
3891         subst = ["\\begin_layout %s" % frame_dict[val]]
3892         esubst = ["", "\\begin_layout EndFrame", "", "\\end_layout"]
3893         endseq = endseq + len(esubst) - len(document.body[j : j])
3894         if document.body[j] == "\\end_deeper":
3895             document.body[j : j] = esubst
3896         else:
3897             document.body[j+1 : j+1] = esubst
3898         for q in range(i, j):
3899             if document.body[q] == "\\begin_layout %s" % val:
3900                 document.body[q] = "\\begin_layout %s" % document.default_layout
3901         r = i
3902         while r < j:
3903             if document.body[r] == "\\begin_deeper":
3904                 s = find_end_of(document.body, r, "\\begin_deeper", "\\end_deeper")
3905                 if s != -1:
3906                     document.body[r] = ""
3907                     document.body[s] = ""
3908                     r = s
3909                     continue
3910             r = r + 1
3911         l = find_end_of_layout(document.body, i)
3912         for p in range(1, 5):
3913             arg = find_token(document.body, "\\begin_inset Argument %d" % p, i, l)
3914             if arg != -1:
3915                 if p == 1:
3916                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3917                     endPlain = find_end_of_layout(document.body, beginPlain)
3918                     endInset = find_end_of_inset(document.body, arg)
3919                     content = document.body[beginPlain + 1 : endPlain]
3920                     # Adjust range end
3921                     l = l - len(document.body[arg : endInset + 1])
3922                     # Remove arg inset
3923                     del document.body[arg : endInset + 1]
3924                     subst += put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
3925                 elif p == 2:
3926                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3927                     endPlain = find_end_of_layout(document.body, beginPlain)
3928                     endInset = find_end_of_inset(document.body, arg)
3929                     content = document.body[beginPlain + 1 : endPlain]
3930                     # Adjust range end
3931                     l = l - len(document.body[arg : endInset + 1])
3932                     # Remove arg inset
3933                     del document.body[arg : endInset + 1]
3934                     subst += put_cmd_in_ert("[<") + content + put_cmd_in_ert(">]")
3935                 elif p == 3:
3936                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3937                     endPlain = find_end_of_layout(document.body, beginPlain)
3938                     endInset = find_end_of_inset(document.body, arg)
3939                     content = document.body[beginPlain + 1 : endPlain]
3940                     # Adjust range end
3941                     l = l - len(document.body[arg : endInset + 1])
3942                     # Remove arg inset
3943                     del document.body[arg : endInset + 1]
3944                     subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
3945                 elif p == 4:
3946                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3947                     endPlain = find_end_of_layout(document.body, beginPlain)
3948                     endInset = find_end_of_inset(document.body, arg)
3949                     content = document.body[beginPlain + 1 : endPlain]
3950                     # Adjust range end
3951                     l = l - len(document.body[arg : endInset + 1])
3952                     # Remove arg inset
3953                     del document.body[arg : endInset + 1]
3954                     subst += content
3955
3956         document.body[i : i + 1] = subst
3957         i = j
3958
3959 # known encodings that do not change their names (same LyX and LaTeX names)
3960 known_enc_tuple = ("auto", "default", "ansinew", "applemac", "armscii8", "ascii",
3961     "cp437", "cp437de", "cp850", "cp852", "cp855", "cp858", "cp862", "cp865", "cp866",
3962     "cp1250", "cp1251", "cp1252", "cp1255", "cp1256", "cp1257", "koi8-r", "koi8-u",
3963     "pt154", "pt254", "tis620-0", "utf8", "utf8x", "utf8-plain")
3964
3965 def convert_encodings(document):
3966     "Use the LyX names of the encodings instead of the LaTeX names."
3967     LaTeX2LyX_enc_dict = {
3968         "8859-6":     "iso8859-6",
3969         "8859-8":     "iso8859-8",
3970         "Bg5":        "big5",
3971         "euc":        "euc-jp-platex",
3972         "EUC-JP":     "euc-jp",
3973         "EUC-TW":     "euc-tw",
3974         "GB":         "euc-cn",
3975         "GBK":        "gbk",
3976         "iso88595":   "iso8859-5",
3977         "iso-8859-7": "iso8859-7",
3978         "JIS":        "jis",
3979         "jis":        "jis-platex",
3980         "KS":         "euc-kr",
3981         "l7xenc":     "iso8859-13",
3982         "latin1":     "iso8859-1",
3983         "latin2":     "iso8859-2",
3984         "latin3":     "iso8859-3",
3985         "latin4":     "iso8859-4",
3986         "latin5":     "iso8859-9",
3987         "latin9":     "iso8859-15",
3988         "latin10":    "iso8859-16",
3989         "SJIS":       "shift-jis",
3990         "sjis":       "shift-jis-platex",
3991         "UTF8":       "utf8-cjk"
3992     }
3993     i = find_token(document.header, "\\inputencoding" , 0)
3994     if i == -1:
3995         return
3996     val = get_value(document.header, "\\inputencoding", i)
3997     if val in list(LaTeX2LyX_enc_dict.keys()):
3998         document.header[i] = "\\inputencoding %s" % LaTeX2LyX_enc_dict[val]
3999     elif val not in known_enc_tuple:
4000         document.warning("Ignoring unknown input encoding: `%s'" % val)
4001
4002
4003 def revert_encodings(document):
4004     """Revert to using the LaTeX names of the encodings instead of the LyX names.
4005     Also revert utf8-platex to sjis, the language default when using Japanese.
4006     """
4007     LyX2LaTeX_enc_dict = {
4008         "big5":             "Bg5",
4009         "euc-cn":           "GB",
4010         "euc-kr":           "KS",
4011         "euc-jp":           "EUC-JP",
4012         "euc-jp-platex":    "euc",
4013         "euc-tw":           "EUC-TW",
4014         "gbk":              "GBK",
4015         "iso8859-1":        "latin1",
4016         "iso8859-2":        "latin2",
4017         "iso8859-3":        "latin3",
4018         "iso8859-4":        "latin4",
4019         "iso8859-5":        "iso88595",
4020         "iso8859-6":        "8859-6",
4021         "iso8859-7":        "iso-8859-7",
4022         "iso8859-8":        "8859-8",
4023         "iso8859-9":        "latin5",
4024         "iso8859-13":       "l7xenc",
4025         "iso8859-15":       "latin9",
4026         "iso8859-16":       "latin10",
4027         "jis":              "JIS",
4028         "jis-platex":       "jis",
4029         "shift-jis":        "SJIS",
4030         "shift-jis-platex": "sjis",
4031         "utf8-cjk":         "UTF8",
4032         "utf8-platex":      "sjis"
4033     }
4034     i = find_token(document.header, "\\inputencoding" , 0)
4035     if i == -1:
4036         return
4037     val = get_value(document.header, "\\inputencoding", i)
4038     if val in list(LyX2LaTeX_enc_dict.keys()):
4039         document.header[i] = "\\inputencoding %s" % LyX2LaTeX_enc_dict[val]
4040     elif val not in known_enc_tuple:
4041         document.warning("Ignoring unknown input encoding: `%s'" % val)
4042
4043
4044 def revert_IEEEtran_3(document):
4045   '''
4046   Reverts Flex Insets to TeX-code
4047   '''
4048   if document.textclass == "IEEEtran":
4049     h = 0
4050     i = 0
4051     j = 0
4052     while True:
4053       if h != -1:
4054         h = find_token(document.body, "\\begin_inset Flex Author Mark", h)
4055       if h != -1:
4056         endh = find_end_of_inset(document.body, h)
4057         document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
4058         document.body[h : h + 4] = put_cmd_in_ert("\\IEEEauthorrefmark{")
4059         h = h + 5
4060       if i != -1:
4061         i = find_token(document.body, "\\begin_inset Flex Author Name", i)
4062       if i != -1:
4063         endi = find_end_of_inset(document.body, i)
4064         document.body[endi - 2 : endi + 1] = put_cmd_in_ert("}")
4065         document.body[i : i + 4] = put_cmd_in_ert("\\IEEEauthorblockN{")
4066         i = i + 5
4067       if j != -1:
4068         j = find_token(document.body, "\\begin_inset Flex Author Affiliation", j)
4069       if j != -1:
4070         endj = find_end_of_inset(document.body, j)
4071         document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
4072         document.body[j : j + 4] = put_cmd_in_ert("\\IEEEauthorblockA{")
4073         j = j + 5
4074       if i == -1 and j == -1 and h == -1:
4075         return
4076
4077
4078 def revert_kurier_fonts(document):
4079   " Revert kurier font definition to LaTeX "
4080
4081   i = find_token(document.header, "\\font_math", 0)
4082   if i != -1:
4083     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
4084       val = get_value(document.header, "\\font_math", i)
4085       if val == "kurier-math":
4086         add_to_preamble(document, "\\let\\Myrmdefault\\rmdefault\n" \
4087           "\\usepackage[math]{kurier}\n" \
4088           "\\renewcommand{\\rmdefault}{\\Myrmdefault}")
4089         document.header[i] = "\\font_math auto"
4090
4091   if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
4092     kurier_fonts = ["kurier", "kurierc", "kurierl", "kurierlc"]
4093     k = find_token(document.header, "\\font_sans kurier", 0)
4094     if k != -1:
4095       sf = get_value(document.header, "\\font_sans", k)
4096       if sf in kurier_fonts:
4097         add_to_preamble(document, "\\renewcommand{\\sfdefault}{%s}" % sf)
4098         document.header[k] = "\\font_sans default"
4099
4100 def revert_iwona_fonts(document):
4101   " Revert iwona font definition to LaTeX "
4102
4103   i = find_token(document.header, "\\font_math", 0)
4104   if i != -1:
4105     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
4106       val = get_value(document.header, "\\font_math", i)
4107       if val == "iwona-math":
4108         add_to_preamble(document, "\\let\\Myrmdefault\\rmdefault\n" \
4109           "\\usepackage[math]{iwona}\n" \
4110           "\\renewcommand{\\rmdefault}{\\Myrmdefault}")
4111         document.header[i] = "\\font_math auto"
4112
4113   if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
4114     iwona_fonts = ["iwona", "iwonac", "iwonal", "iwonalc"]
4115     k = find_token(document.header, "\\font_sans iwona", 0)
4116     if k != -1:
4117       sf = get_value(document.header, "\\font_sans", k)
4118       if sf in iwona_fonts:
4119         add_to_preamble(document, "\\renewcommand{\\sfdefault}{%s}" % sf)
4120         document.header[k] = "\\font_sans default"
4121
4122
4123 def revert_new_libertines(document):
4124     " Revert new libertine font definition to LaTeX "
4125
4126     if find_token(document.header, "\\use_non_tex_fonts true", 0) != -1:
4127         return
4128
4129     i = find_token(document.header, "\\font_typewriter libertine-mono", 0)
4130     if i != -1:
4131         preamble = "\\usepackage"
4132         sc = find_token(document.header, "\\font_tt_scale", 0)
4133         if sc != -1:
4134             scval = get_value(document.header, "\\font_tt_scale", sc)
4135             if scval != "100":
4136                 preamble += "[scale=%f]" % (float(scval) / 100)
4137                 document.header[sc] = "\\font_tt_scale 100"
4138         preamble += "{libertineMono-type1}"
4139         add_to_preamble(document, [preamble])
4140         document.header[i] = "\\font_typewriter default"
4141
4142     k = find_token(document.header, "\\font_sans biolinum", 0)
4143     if k != -1:
4144         preamble = "\\usepackage"
4145         options = ""
4146         j = find_token(document.header, "\\font_osf true", 0)
4147         if j != -1:
4148             options += "osf"
4149         else:
4150             options += "lining"
4151         sc = find_token(document.header, "\\font_sf_scale", 0)
4152         if sc != -1:
4153             scval = get_value(document.header, "\\font_sf_scale", sc)
4154             if scval != "100":
4155                 options += ",scale=%f" % (float(scval) / 100)
4156                 document.header[sc] = "\\font_sf_scale 100"
4157         if options != "":
4158             preamble += "[" + options +"]"
4159         preamble += "{biolinum-type1}"
4160         add_to_preamble(document, [preamble])
4161         document.header[k] = "\\font_sans default"
4162
4163
4164 def convert_lyxframes(document):
4165     " Converts old beamer frames to new style "
4166
4167     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
4168     if document.textclass not in beamer_classes:
4169         return
4170
4171     framebeg = ["BeginFrame", "BeginPlainFrame"]
4172     frameend = ["Frame", "PlainFrame", "EndFrame", "BeginFrame", "BeginPlainFrame", "AgainFrame",
4173                 "Section", "Section*", "Subsection", "Subsection*", "Subsubsection", "Subsubsection*"]
4174     for lay in framebeg:
4175         i = 0
4176         while True:
4177             i = find_token_exact(document.body, "\\begin_layout " + lay, i)
4178             if i == -1:
4179                 break
4180             parent = get_containing_layout(document.body, i)
4181             if parent == False or parent[1] != i:
4182                 document.warning("Wrong parent layout!")
4183                 i += 1
4184                 continue
4185             frametype = parent[0]
4186             j = parent[2]
4187             parbeg = parent[3]
4188             if i != -1:
4189                 # Step I: Convert ERT arguments
4190                 # FIXME: See restrictions in convert_beamerframeargs method
4191                 ertend = convert_beamerframeargs(document, i, parbeg)
4192                 if ertend == -1:
4193                     break
4194                 # Step II: Now rename the layout and convert the title to an argument
4195                 j = find_end_of_layout(document.body, i)
4196                 document.body[j : j + 1] = ['\\end_layout', '', '\\end_inset', '', '\\end_layout']
4197                 if lay == "BeginFrame":
4198                     document.body[i] = "\\begin_layout Frame"
4199                 else:
4200                     document.body[i] = "\\begin_layout PlainFrame"
4201                 document.body[ertend + 1 : ertend + 1] = ['\\begin_inset Argument 4',
4202                                                 'status open', '', '\\begin_layout Plain Layout']
4203                 # Step III: find real frame end
4204                 j = j + 8
4205                 jj = j
4206                 inInset = get_containing_inset(document.body, i)
4207                 while True:
4208                     fend = find_token(document.body, "\\begin_layout", jj)
4209                     if fend == -1:
4210                         document.warning("Malformed LyX document: No real frame end!")
4211                         return
4212                     val = get_value(document.body, "\\begin_layout", fend)
4213                     if val not in frameend:
4214                         jj = fend + 1
4215                         continue
4216                     # is this frame nested in an inset (e.g., Note)?
4217                     if inInset != False:
4218                         # if so, end the frame inside the inset
4219                         if inInset[2] < fend:
4220                             fend = inInset[2]
4221                     if val == frametype:
4222                         document.body[fend : fend] = ['\\end_deeper', '', '\\begin_layout Separator', '', '\\end_layout']
4223                     # consider explicit EndFrames between two identical frame types
4224                     elif val == "EndFrame":
4225                         nextlayout = find_token(document.body, "\\begin_layout", fend + 1)
4226                         if nextlayout != -1 and get_value(document.body, "\\begin_layout", nextlayout) == frametype:
4227                             document.body[fend : fend] = ['\\end_deeper', '', '\\begin_layout Separator', '', '\\end_layout']
4228                         else:
4229                             document.body[fend : fend] = ['\\end_deeper']
4230                     else:
4231                         document.body[fend : fend] = ['\\end_deeper']
4232                     document.body[j + 1 : j + 1] = ['', '\\begin_deeper']
4233                     break
4234             i = j
4235
4236
4237 def remove_endframes(document):
4238     " Remove deprecated beamer endframes "
4239
4240     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
4241     if document.textclass not in beamer_classes:
4242         return
4243
4244     i = 0
4245     while True:
4246         i = find_token_exact(document.body, "\\begin_layout EndFrame", i)
4247         if i == -1:
4248             break
4249         j = find_end_of_layout(document.body, i)
4250         if j == -1:
4251             document.warning("Malformed LyX document: Missing \\end_layout to EndFrame")
4252             i += 1
4253             continue
4254         del document.body[i : j + 1]
4255
4256
4257 def revert_powerdot_flexes(document):
4258     " Reverts powerdot flex insets "
4259
4260     if document.textclass != "powerdot":
4261         return
4262
4263     flexes = {"Onslide" : "\\onslide",
4264               "Onslide*" : "\\onslide*",
4265               "Onslide+" : "\\onslide+"}
4266     rx = re.compile(r'^\\begin_inset Flex (.+)$')
4267
4268     i = 0
4269     while True:
4270         i = find_token(document.body, "\\begin_inset Flex", i)
4271         if i == -1:
4272             return
4273         m = rx.match(document.body[i])
4274         if m:
4275             flextype = m.group(1)
4276             z = find_end_of_inset(document.body, i)
4277             if z == -1:
4278                 document.warning("Can't find end of Flex " + flextype + " inset.")
4279                 i += 1
4280                 continue
4281             if flextype in flexes:
4282                 pre = put_cmd_in_ert(flexes[flextype])
4283                 arg = find_token(document.body, "\\begin_inset Argument 1", i, z)
4284                 if arg != -1:
4285                     argend = find_end_of_inset(document.body, arg)
4286                     if argend == -1:
4287                         document.warning("Can't find end of Argument!")
4288                         i += 1
4289                         continue
4290                     # Find containing paragraph layout
4291                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
4292                     endPlain = find_end_of_layout(document.body, beginPlain)
4293                     argcontent = document.body[beginPlain + 1 : endPlain]
4294                     # Adjust range end
4295                     z = z - len(document.body[arg : argend + 1])
4296                     # Remove arg inset
4297                     del document.body[arg : argend + 1]
4298                     pre += put_cmd_in_ert("{") + argcontent + put_cmd_in_ert("}")
4299                 pre += put_cmd_in_ert("{")
4300                 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
4301                 endPlain = find_end_of_layout(document.body, beginPlain)
4302                 # Adjust range end
4303                 z = z - len(document.body[i : beginPlain + 1])
4304                 z += len(pre)
4305                 document.body[i : beginPlain + 1] = pre
4306                 post = put_cmd_in_ert("}")
4307                 document.body[z - 2 : z + 1] = post
4308         i += 1
4309
4310
4311 def revert_powerdot_pause(document):
4312     " Reverts powerdot pause layout to ERT "
4313
4314     if document.textclass != "powerdot":
4315         return
4316
4317     i = 0
4318     while True:
4319         i = find_token(document.body, "\\begin_layout Pause", i)
4320         if i == -1:
4321             return
4322         j = find_end_of_layout(document.body, i)
4323         if j == -1:
4324             document.warning("Malformed LyX document: Can't find end of Pause layout")
4325             i += 1
4326             continue
4327         endlay = j
4328         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\pause")
4329         for p in range(i, j):
4330             if p >= endlay:
4331                 break
4332             arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
4333             if arg != -1:
4334                 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
4335                 endPlain = find_end_of_layout(document.body, beginPlain)
4336                 endInset = find_end_of_inset(document.body, p)
4337                 content = document.body[beginPlain + 1 : endPlain]
4338                 # Adjust range end
4339                 endlay = endlay - len(document.body[p : endInset + 1])
4340                 # Remove arg inset
4341                 del document.body[p : endInset + 1]
4342                 subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
4343
4344         document.body[i : i + 1] = subst
4345         i = endlay
4346
4347
4348 def revert_powerdot_itemargs(document):
4349     " Reverts powerdot item arguments to ERT "
4350
4351     if document.textclass != "powerdot":
4352         return
4353
4354     i = 0
4355     list_layouts = ["Itemize", "ItemizeType1", "Enumerate", "EnumerateType1"]
4356     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
4357
4358     while True:
4359         i = find_token(document.body, "\\begin_inset Argument", i)
4360         if i == -1:
4361             return
4362         # Find containing paragraph layout
4363         parent = get_containing_layout(document.body, i)
4364         if parent == False:
4365             document.warning("Malformed LyX document: Can't find parent paragraph layout")
4366             i += 1
4367             continue
4368         parbeg = parent[1]
4369         parend = parent[2]
4370         realparbeg = parent[3]
4371         layoutname = parent[0]
4372         realparend = parend
4373         for p in range(parbeg, parend):
4374             if p >= realparend:
4375                 i = realparend
4376                 break
4377             if layoutname in list_layouts:
4378                 m = rx.match(document.body[p])
4379                 if m:
4380                     argnr = m.group(1)
4381                     if argnr == "item:1":
4382                         j = find_end_of_inset(document.body, i)
4383                         # Find containing paragraph layout
4384                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
4385                         endPlain = find_end_of_layout(document.body, beginPlain)
4386                         content = document.body[beginPlain + 1 : endPlain]
4387                         del document.body[i:j+1]
4388                         subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
4389                         document.body[realparbeg : realparbeg] = subst
4390                     elif argnr == "item:2":
4391                         j = find_end_of_inset(document.body, i)
4392                         # Find containing paragraph layout
4393                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
4394                         endPlain = find_end_of_layout(document.body, beginPlain)
4395                         content = document.body[beginPlain + 1 : endPlain]
4396                         del document.body[i:j+1]
4397                         subst = put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
4398                         document.body[realparbeg : realparbeg] = subst
4399
4400         i = realparend
4401
4402
4403 def revert_powerdot_columns(document):
4404     " Reverts powerdot twocolumn to TeX-code "
4405     if document.textclass != "powerdot":
4406         return
4407
4408     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
4409     i = 0
4410     while True:
4411         i = find_token(document.body, "\\begin_layout Twocolumn", i)
4412         if i == -1:
4413             return
4414         j = find_end_of_layout(document.body, i)
4415         if j == -1:
4416             document.warning("Malformed LyX document: Can't find end of Twocolumn layout")
4417             i += 1
4418             continue
4419         endlay = j
4420         document.body[j : j] = put_cmd_in_ert("}") + document.body[j : j]
4421         endlay += len(put_cmd_in_ert("}"))
4422         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\twocolumn")
4423         for p in range(i, j):
4424             if p >= endlay:
4425                 break
4426             m = rx.match(document.body[p])
4427             if m:
4428                 argnr = m.group(1)
4429                 if argnr == "1":
4430                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
4431                     endPlain = find_end_of_layout(document.body, beginPlain)
4432                     endInset = find_end_of_inset(document.body, p)
4433                     content = document.body[beginPlain + 1 : endPlain]
4434                     # Adjust range end
4435                     endlay = endlay - len(document.body[p : endInset + 1])
4436                     # Remove arg inset
4437                     del document.body[p : endInset + 1]
4438                     subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
4439                 elif argnr == "2":
4440                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
4441                     endPlain = find_end_of_layout(document.body, beginPlain)
4442                     endInset = find_end_of_inset(document.body, p)
4443                     content = document.body[beginPlain + 1 : endPlain]
4444                     # Adjust range end
4445                     endlay = endlay - len(document.body[p : endInset + 1])
4446                     # Remove arg inset
4447                     del document.body[p : endInset + 1]
4448                     subst += put_cmd_in_ert("{") + content + put_cmd_in_ert("}")
4449
4450         subst += put_cmd_in_ert("{")
4451         document.body[i : i + 1] = subst
4452         i = endlay
4453
4454
4455 def revert_mbox_fbox(document):
4456     'Convert revert mbox/fbox boxes to TeX-code'
4457     i = 0
4458     while True:
4459         i = find_token(document.body, "\\begin_inset Box", i)
4460         if i == -1:
4461             return
4462         j = find_token(document.body, "width", i)
4463         if j != i + 7:
4464             document.warning("Malformed LyX document: Can't find box width")
4465             return
4466         width = get_value(document.body, "width", j)
4467         k = find_end_of_inset(document.body, j)
4468         if k == -1:
4469             document.warning("Malformed LyX document: Can't find end of box inset")
4470             i += 1
4471             continue
4472         BeginLayout = find_token(document.body, "\\begin_layout Plain Layout", j)
4473         EndLayout = find_end_of_layout(document.body, BeginLayout)
4474         # replace if width is ""
4475         if (width == '""'):
4476             document.body[EndLayout:k + 1] = put_cmd_in_ert("}")
4477             if document.body[i] == "\\begin_inset Box Frameless":
4478                 document.body[i:BeginLayout + 1] = put_cmd_in_ert("\\mbox{")
4479             if document.body[i] == "\\begin_inset Box Boxed":
4480                 document.body[i:BeginLayout + 1] = put_cmd_in_ert("\\fbox{")
4481         i += 1
4482
4483
4484 def revert_starred_caption(document):
4485     " Reverts unnumbered longtable caption insets "
4486
4487     i = 0
4488     while True:
4489       i = find_token(document.body, "\\begin_inset Caption LongTableNoNumber", i)
4490       if i == -1:
4491           return
4492       # This is not equivalent, but since the caption inset is a full blown
4493       # text inset a true conversion to ERT is too difficult.
4494       document.body[i] = "\\begin_inset Caption Standard"
4495       i += 1
4496
4497
4498 def revert_forced_local_layout(document):
4499     i = 0
4500     while True:
4501         i = find_token(document.header, "\\begin_forced_local_layout", i)
4502         if i == -1:
4503             return
4504         j = find_end_of(document.header, i, "\\begin_forced_local_layout", "\\end_forced_local_layout")
4505         if j == -1:
4506             # this should not happen
4507             break
4508         regexp = re.compile(r'\s*forcelocal', re.IGNORECASE)
4509         k = find_re(document.header, regexp, i, j)
4510         while k != -1:
4511             del document.header[k]
4512             j = j - 1
4513             k = find_re(document.header, regexp, i, j)
4514         k = find_token(document.header, "\\begin_local_layout", 0)
4515         if k == -1:
4516             document.header[i] = "\\begin_local_layout"
4517             document.header[j] = "\\end_local_layout"
4518         else:
4519             l = find_end_of(document.header, k, "\\begin_local_layout", "\\end_local_layout")
4520             if j == -1:
4521                 # this should not happen
4522                 break
4523             lines = document.header[i+1 : j]
4524             if k > i:
4525                 document.header[k+1 : k+1] = lines
4526                 document.header[i   : j  ] = []
4527             else:
4528                 document.header[i   : j  ] = []
4529                 document.header[k+1 : k+1] = lines
4530
4531
4532 def revert_aa1(document):
4533   " Reverts InsetArguments of aa to TeX-code "
4534   if document.textclass == "aa":
4535     i = 0
4536     while True:
4537       if i != -1:
4538         i = find_token(document.body, "\\begin_layout Abstract (structured)", i)
4539       if i != -1:
4540         revert_Argument_to_TeX_brace(document, i, 0, 1, 4, False, False)
4541         i += 1
4542       if i == -1:
4543         return
4544
4545
4546 def revert_aa2(document):
4547   " Reverts InsetArguments of aa to TeX-code "
4548   if document.textclass == "aa":
4549     i = 0
4550     while True:
4551       if i != -1:
4552         i = find_token(document.body, "\\begin_layout Abstract (structured)", i)
4553       if i != -1:
4554         document.body[i] = "\\begin_layout Abstract"
4555         i += 1
4556       if i == -1:
4557         return
4558
4559
4560 def revert_tibetan(document):
4561     "Set the document language for Tibetan to English"
4562
4563     if document.language == "tibetan":
4564         document.language = "english"
4565         i = find_token(document.header, "\\language", 0)
4566         if i != -1:
4567             document.header[i] = "\\language english"
4568     j = 0
4569     while j < len(document.body):
4570         j = find_token(document.body, "\\lang tibetan", j)
4571         if j != -1:
4572             document.body[j] = document.body[j].replace("\\lang tibetan", "\\lang english")
4573             j += 1
4574         else:
4575             j = len(document.body)
4576
4577
4578 #############
4579 #
4580 # Chunk stuff
4581 #
4582 #############
4583
4584 # The idea here is that we will have a sequence of chunk paragraphs.
4585 # We want to convert them to paragraphs in one or several chunk insets.
4586 # Individual chunks are terminated by the character @ on the last line.
4587 # This line will be discarded, and following lines are treated as new
4588 # chunks, which go into their own insets.
4589 # The first line of a chunk should look like: <<CONTENT>>=
4590 # We will discard the delimiters, and put the CONTENT into the
4591 # optional argument of the inset, if the CONTENT is non-empty.
4592 def convert_chunks(document):
4593     first_re = re.compile(r'<<(.*)>>=(.*)')
4594     file_pos = 0
4595     while True:
4596         # find start of a block of chunks
4597         i = find_token(document.body, "\\begin_layout Chunk", file_pos)
4598         if i == -1:
4599             return
4600         start = i
4601         end = -1
4602         contents = []
4603         chunk_started = False
4604
4605         while True:
4606             # process the one we just found
4607             j = find_end_of_layout(document.body, i)
4608             if j == -1:
4609                 document.warning("Malformed LyX documents. Can't find end of Chunk layout!")
4610                 # there is no point continuing, as we will run into the same error again.
4611                 return
4612             this_chunk = "".join(document.body[i + 1:j])
4613
4614             # there may be empty lines between chunks
4615             # we just skip them.
4616             if not chunk_started:
4617                 if this_chunk != "":
4618                     # new chunk starts
4619                     chunk_started = True
4620
4621             if chunk_started:
4622                 contents.append(document.body[i + 1:j])
4623
4624             # look for potential chunk terminator
4625             # on the last line of the chunk paragraph
4626             if document.body[j - 1] == "@":
4627                 break
4628
4629             # look for subsequent chunk paragraph
4630             i = find_token(document.body, "\\begin_layout", j)
4631             if i == -1:
4632                 break
4633
4634             if get_value(document.body, "\\begin_layout", i) != "Chunk":
4635                 break
4636
4637         file_pos = end = j + 1
4638
4639         # The last chunk should simply have an "@" in it
4640         # or at least end with "@" (can happen if @ is
4641         # preceded by a newline)
4642         lastpar = ''
4643         if len(contents) > 0:
4644             lastpar = ''.join(contents[-1])
4645         if not lastpar.endswith("@"):
4646             document.warning("Unexpected chunk content: chunk not terminated by '@'!")
4647             if len(contents) == 0:
4648                 # convert empty chunk layouts to Standard
4649                 document.body[start] = "\\begin_layout Standard"
4650             continue
4651
4652         if lastpar == "@":
4653             # chunk par only contains "@". Just drop it.
4654             contents.pop()
4655         else:
4656             # chunk par contains more. Only drop the "@".
4657             contents[-1].pop()
4658
4659         # The first line should look like: <<CONTENT>>=
4660         # We want the CONTENT
4661         optarg = ' '.join(contents[0])
4662         optarg.strip()
4663         # We can already have real chunk content in
4664         # the first par (separated from the options by a newline).
4665         # We collect such stuff to re-insert it later.
4666         postoptstuff = []
4667
4668         match = first_re.search(optarg)
4669         if match:
4670             optarg = match.groups()[0]
4671             if match.groups()[1] != "":
4672                 postopt = False
4673                 for c in contents[0]:
4674                     if c.endswith(">>="):
4675                         postopt = True
4676                         continue
4677                     if postopt:
4678                         postoptstuff.append(c)
4679             # We have stripped everything. This can be deleted.
4680             contents.pop(0)
4681
4682         newstuff = ['\\begin_layout Standard']
4683
4684         # Maintain paragraph parameters
4685         par_params = ["\\noindent", "\\indent", "\\indent-toggle", "\\leftindent",
4686                       "\\start_of_appendix", "\\paragraph_spacing", "\\align",
4687                       "\\labelwidthstring"]
4688         parms = start + 1
4689         while True:
4690             if document.body[parms].split(' ', 1)[0] not in par_params:
4691                 break
4692             newstuff.extend([document.body[parms]])
4693             parms += 1
4694
4695         newstuff.extend(
4696             ['\\begin_inset Flex Chunk',
4697              'status open', '',
4698              '\\begin_layout Plain Layout', ''])
4699
4700         # If we have a non-empty optional argument, insert it.
4701         if match and optarg != "":
4702             newstuff.extend(
4703                 ['\\begin_inset Argument 1',
4704                  'status open', '',
4705                  '\\begin_layout Plain Layout',
4706                  optarg,
4707                  '\\end_layout', '',
4708                  '\\end_inset', ''])
4709
4710         # Since we already opened a Plain layout, the first paragraph
4711         # does not need to do that.
4712         did_one_par = False
4713         if postoptstuff:
4714             # we need to replace newlines with new layouts
4715             start_newline = -1
4716             started_text = False
4717             for lno in range(0,len(postoptstuff)):
4718                 if postoptstuff[lno].startswith("\\begin_inset Newline newline"):
4719                     start_newline = lno
4720                 elif start_newline != -1:
4721                     if postoptstuff[lno].startswith("\\end_inset"):
4722                         # replace that bit, but only if we already have some text
4723                         # and we're not at the end except for a blank line
4724                         if started_text and \
4725                           (lno != len(postoptstuff) - 2 or postoptstuff[-1] != ""):
4726                             newstuff.extend(['\\end_layout', '\n', '\\begin_layout Plain Layout', '\n'])
4727                         start_newline = -1
4728                         started_text = True
4729                 else:
4730                     newstuff.extend([postoptstuff[lno]])
4731             newstuff.append('\\end_layout')
4732             did_one_par = True
4733         for c in contents:
4734             if did_one_par:
4735                 newstuff.extend(['', '\\begin_layout Plain Layout', ''])
4736             else:
4737                 did_one_par = True
4738             newstuff.extend(c)
4739             newstuff.append('\\end_layout')
4740
4741         newstuff.extend(['', '\\end_inset', '', '\\end_layout', ''])
4742
4743         document.body[start:end] = newstuff
4744
4745         file_pos += len(newstuff) - (end - start)
4746
4747
4748 def revert_chunks(document):
4749     i = 0
4750     while True:
4751         i = find_token(document.body, "\\begin_inset Flex Chunk", i)
4752         if i == -1:
4753             return
4754
4755         iend = find_end_of_inset(document.body, i)
4756         if iend == -1:
4757             document.warning("Can't find end of Chunk!")
4758             i += 1
4759             continue
4760
4761         # Look for optional argument
4762         optarg = ""
4763         ostart = find_token(document.body, "\\begin_inset Argument 1", i, iend)
4764         if ostart != -1:
4765             oend = find_end_of_inset(document.body, ostart)
4766             k = find_token(document.body, "\\begin_layout Plain Layout", ostart, oend)
4767             if k == -1:
4768                 document.warning("Malformed LyX document: Can't find argument contents!")
4769             else:
4770                 m = find_end_of_layout(document.body, k)
4771                 optarg = "".join(document.body[k+1:m])
4772
4773             # We now remove the optional argument, so we have something
4774             # uniform on which to work
4775             document.body[ostart : oend + 1] = []
4776             # iend is now invalid
4777             iend = find_end_of_inset(document.body, i)
4778
4779         retval = get_containing_layout(document.body, i)
4780         if not retval:
4781             document.warning("Can't find containing layout for Chunk!")
4782             i = iend
4783             continue
4784         (lname, lstart, lend, pstart)  = retval
4785         # we now want to work through the various paragraphs, and collect their contents
4786         parlist = []
4787         k = i
4788         while True:
4789             k = find_token(document.body, "\\begin_layout Plain Layout", k, lend)
4790             if k == -1:
4791                 break
4792             j = find_end_of_layout(document.body, k)
4793             if j == -1:
4794                 document.warning("Can't find end of layout inside chunk!")
4795                 break
4796             parlist.append(document.body[k+1:j])
4797             k = j
4798         # we now need to wrap all of these paragraphs in chunks
4799         newlines = []
4800         newlines.extend(["\\begin_layout Chunk", "", "<<" + optarg + ">>=", "\\end_layout", ""])
4801         for stuff in parlist:
4802             newlines.extend(["\\begin_layout Chunk"] + stuff + ["\\end_layout", ""])
4803         newlines.extend(["\\begin_layout Chunk", "", "@", "\\end_layout", ""])
4804         # replace old content with new content
4805         document.body[lstart : lend + 1] = newlines
4806         i = lstart + len(newlines)
4807
4808
4809 ##
4810 # Conversion hub
4811 #
4812
4813 supported_versions = ["2.1.0","2.1"]
4814 convert = [
4815            [414, []],
4816            [415, [convert_undertilde]],
4817            [416, []],
4818            [417, [convert_japanese_encodings]],
4819            [418, [convert_justification]],
4820            [419, []],
4821            [420, [convert_biblio_style]],
4822            [421, [convert_longtable_captions]],
4823            [422, [convert_use_packages]],
4824            [423, [convert_use_mathtools]],
4825            [424, [convert_cite_engine_type]],
4826            # No convert_cancel, since cancel will be loaded automatically
4827            # in format 425 without any possibility to switch it off.
4828            # This has been fixed in format 464.
4829            [425, []],
4830            [426, []],
4831            [427, []],
4832            [428, [convert_cell_rotation]],
4833            [429, [convert_table_rotation]],
4834            [430, [convert_listoflistings]],
4835            [431, [convert_use_amssymb]],
4836            [432, []],
4837            [433, [convert_armenian]],
4838            [434, []],
4839            [435, []],
4840            [436, []],
4841            [437, []],
4842            [438, []],
4843            [439, []],
4844            [440, [convert_mathfonts]],
4845            [441, [convert_mdnomath]],
4846            [442, []],
4847            [443, []],
4848            [444, []],
4849            [445, []],
4850            [446, [convert_latexargs]],
4851            [447, [convert_IEEEtran, convert_AASTeX, convert_AGUTeX, convert_IJMP, convert_SIGPLAN, convert_SIGGRAPH, convert_EuropeCV, convert_Initials, convert_ModernCV]],
4852            [448, [convert_literate]],
4853            [449, []],
4854            [450, []],
4855            [451, [convert_beamerargs, convert_againframe_args, convert_corollary_args, convert_quote_args]],
4856            [452, [convert_beamerblocks]],
4857            [453, [convert_use_stmaryrd]],
4858            [454, [convert_overprint]],
4859            [455, []],
4860            [456, [convert_epigraph]],
4861            [457, [convert_use_stackrel]],
4862            [458, [convert_captioninsets, convert_captionlayouts]],
4863            [459, []],
4864            [460, []],
4865            [461, []],
4866            [462, []],
4867            [463, [convert_encodings]],
4868            [464, [convert_use_cancel]],
4869            [465, [convert_lyxframes, remove_endframes]],
4870            [466, []],
4871            [467, []],
4872            [468, []],
4873            [469, []],
4874            [470, []],
4875            [471, [convert_cite_engine_type_default]],
4876            [472, []],
4877            [473, []],
4878            [474, [convert_chunks, cleanup_beamerargs]],
4879           ]
4880
4881 revert =  [
4882            [473, [revert_chunks]],
4883            [472, [revert_tibetan]],
4884            [471, [revert_aa1,revert_aa2]],
4885            [470, [revert_cite_engine_type_default]],
4886            [469, [revert_forced_local_layout]],
4887            [468, [revert_starred_caption]],
4888            [467, [revert_mbox_fbox]],
4889            [466, [revert_iwona_fonts]],
4890            [465, [revert_powerdot_flexes, revert_powerdot_pause, revert_powerdot_itemargs, revert_powerdot_columns]],
4891            [464, []],
4892            [463, [revert_use_cancel]],
4893            [462, [revert_encodings]],
4894            [461, [revert_new_libertines]],
4895            [460, [revert_kurier_fonts]],
4896            [459, [revert_IEEEtran_3]],
4897            [458, [revert_fragileframe, revert_newframes]],
4898            [457, [revert_captioninsets, revert_captionlayouts]],
4899            [456, [revert_use_stackrel]],
4900            [455, [revert_epigraph]],
4901            [454, [revert_frametitle]],
4902            [453, [revert_overprint]],
4903            [452, [revert_use_stmaryrd]],
4904            [451, [revert_beamerblocks]],
4905            [450, [revert_beamerargs, revert_beamerargs2, revert_beamerargs3, revert_beamerflex]],
4906            [449, [revert_garamondx, revert_garamondx_newtxmath]],
4907            [448, [revert_itemargs]],
4908            [447, [revert_literate]],
4909            [446, [revert_IEEEtran, revert_IEEEtran_2, revert_AASTeX, revert_AGUTeX, revert_IJMP, revert_SIGPLAN, revert_SIGGRAPH, revert_EuropeCV, revert_Initials, revert_ModernCV_3, revert_ModernCV_4]],
4910            [445, [revert_latexargs]],
4911            [444, [revert_uop]],
4912            [443, [revert_biolinum]],
4913            [442, []],
4914            [441, [revert_newtxmath]],
4915            [440, [revert_mdnomath]],
4916            [439, [revert_mathfonts]],
4917            [438, [revert_minionpro]],
4918            [437, [revert_ipadeco, revert_ipachar]],
4919            [436, [revert_texgyre]],
4920            [435, [revert_mathdesign]],
4921            [434, [revert_txtt]],
4922            [433, [revert_libertine]],
4923            [432, [revert_armenian]],
4924            [431, [revert_languages, revert_ancientgreek]],
4925            [430, [revert_use_amssymb]],
4926            [429, [revert_listoflistings]],
4927            [428, [revert_table_rotation]],
4928            [427, [revert_cell_rotation]],
4929            [426, [revert_tipa]],
4930            [425, [revert_verbatim]],
4931            [424, [revert_cancel]],
4932            [423, [revert_cite_engine_type]],
4933            [422, [revert_use_mathtools]],
4934            [421, [revert_use_packages]],
4935            [420, [revert_longtable_captions]],
4936            [419, [revert_biblio_style]],
4937            [418, [revert_australian]],
4938            [417, [revert_justification]],
4939            [416, [revert_japanese_encodings]],
4940            [415, [revert_negative_space, revert_math_spaces]],
4941            [414, [revert_undertilde]],
4942            [413, [revert_visible_space]]
4943           ]
4944
4945
4946 if __name__ == "__main__":
4947     pass