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