]> git.lyx.org Git - lyx.git/blob - lib/lyx2lyx/lyx_2_1.py
Cmake export tests: Handle attic files with now missing references to png graphics
[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 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_mdnomath(document):
1444     " Change mathdesign font name " 
1445
1446     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
1447         mathdesign_dict = {
1448         "mdbch":  "md-charter",
1449         "mdput":  "md-utopia",
1450         "mdugm":  "md-garamond"
1451         }
1452         i = find_token(document.header, "\\font_roman", 0)
1453         if i == -1:
1454             return
1455         val = get_value(document.header, "\\font_roman", i)
1456         if val in list(mathdesign_dict.keys()):
1457              document.header[i] = "\\font_roman %s" % mathdesign_dict[val]
1458
1459
1460 def revert_newtxmath(document):
1461     " Revert native newtxmath definitions to LaTeX " 
1462
1463     i = find_token(document.header, "\\font_math", 0)
1464     if i == -1:
1465        return
1466     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
1467         val = get_value(document.header, "\\font_math", i)
1468         mathfont_dict = {
1469         "libertine-ntxm":  "\\usepackage[libertine]{newtxmath}",
1470         "minion-ntxm":  "\\usepackage[minion]{newtxmath}",
1471         "newtxmath":  "\\usepackage{newtxmath}",
1472         }
1473         if val in list(mathfont_dict.keys()):
1474             add_to_preamble(document, mathfont_dict[val])
1475             document.header[i] = "\\font_math auto"
1476
1477
1478 def revert_biolinum(document):
1479     " Revert native biolinum font definition to LaTeX " 
1480
1481     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
1482         i = find_token(document.header, "\\font_sans biolinum", 0)
1483         if i != -1:
1484             osf = False
1485             j = find_token(document.header, "\\font_osf true", 0)
1486             if j != -1:
1487                 osf = True
1488             preamble = "\\usepackage"
1489             if not osf:
1490                 preamble += "[lf]"
1491             preamble += "{biolinum-type1}"
1492             add_to_preamble(document, [preamble])
1493             document.header[i] = "\\font_sans default"
1494
1495
1496 def revert_uop(document):
1497     " Revert native URW Classico (Optima) font definition to LaTeX "
1498
1499     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1500         i = find_token(document.header, "\\font_sans uop", 0)
1501         if i != -1:
1502                 preamble = "\\renewcommand{\\sfdefault}{uop}"
1503                 add_to_preamble(document, [preamble])
1504                 document.header[i] = "\\font_sans default"
1505
1506
1507 def convert_latexargs(document):
1508     " Convert InsetArgument to new syntax "
1509
1510     if find_token(document.body, "\\begin_inset Argument", 0) == -1:
1511         # nothing to do.
1512         return
1513
1514     # A list of layouts (document classes) with only optional or no arguments.
1515     # These can be safely converted to the new syntax
1516     # (I took the liberty to add some of my personal layouts/modules here; JSP)
1517     safe_layouts = ["aa", "aapaper", "aastex", "achemso", "acmsiggraph", "AEA",
1518                     "agu-dtd", "agums", "agutex", "amsart", "amsbook", "apa",
1519                     "arab-article", "armenian-article", "article-beamer", "article",
1520                     "beamer", "book", "broadway", "chess", "cl2emult", "ctex-article",
1521                     "ctex-book", "ctex-report", "dinbrief", "docbook-book", "docbook-chapter",
1522                     "docbook", "docbook-section", "doublecol-new", "dtk", "ectaart", "egs",
1523                     "elsarticle", "elsart", "entcs", "europecv", "extarticle", "extbook",
1524                     "extletter", "extreport", "foils", "frletter", "g-brief2", "g-brief",
1525                     "heb-article", "heb-letter", "hollywood", "IEEEtran", "ijmpc", "ijmpd",
1526                     "iopart", "isprs", "jarticle", "jasatex", "jbook", "jgrga", "jreport",
1527                     "jsarticle", "jsbeamer", "jsbook", "jss", "kluwer", "latex8", "letter", "lettre",
1528                     "literate-article", "literate-book", "literate-report", "llncs", "ltugboat",
1529                     "memoir", "moderncv", "mwart", "mwbk", "mwrep", "paper", "powerdot",
1530                     "recipebook", "report", "revtex4", "revtex", "scrartcl", "scrarticle-beamer",
1531                     "scrbook", "scrlettr", "scrlttr2", "scrreprt", "seminar", "siamltex",
1532                     "sigplanconf", "simplecv", "singlecol", "singlecol-new", "slides", "spie",
1533                     "svglobal3", "svglobal", "svjog", "svmono", "svmult", "svprobth", "tarticle",
1534                     "tbook", "treport", "tufte-book", "tufte-handout"]
1535     # A list of "safe" modules, same as above
1536     safe_modules = ["biblatex", "beameraddons", "beamer-resenumerate", "beamersession", "braille",
1537                     "customHeadersFooters", "endnotes", "enumitem", "eqs-within-sections", "figs-within-sections",
1538                     "fix-cm", "fixltx2e", "foottoend", "hanging", "jscharstyles", "knitr", "lilypond",
1539                     "linguistics", "linguisticx", "logicalmkup", "minimalistic", "nomindex", "noweb",
1540                     "pdfcomment", "sweave", "tabs-within-sections", "theorems-ams-bytype",
1541                     "theorems-ams-extended-bytype", "theorems-ams-extended", "theorems-ams", "theorems-bytype",
1542                     "theorems-chap-bytype", "theorems-chap", "theorems-named", "theorems-sec-bytype",
1543                     "theorems-sec", "theorems-starred", "theorems-std", "todonotes"]
1544     # Modules we need to take care of
1545     caveat_modules = ["initials"]
1546     # information about the relevant styles in caveat_modules (number of opt and req args)
1547     # use this if we get more caveat_modules. For now, use hard coding (see below).
1548     # initials = [{'Layout' : 'Initial', 'opt' : 1, 'req' : 1}]
1549
1550     # Is this a known safe layout?
1551     safe_layout = document.textclass in safe_layouts
1552     if not safe_layout:
1553         document.warning("Lyx2lyx knows nothing about textclass '%s'. "
1554                          "Please check if short title insets have been converted correctly."
1555                          % document.textclass)
1556     # Do we use unsafe or unknown modules
1557     mods = document.get_module_list()
1558     unknown_modules = False
1559     used_caveat_modules = list()
1560     for mod in mods:
1561         if mod in safe_modules:
1562             continue
1563         if mod in caveat_modules:
1564             used_caveat_modules.append(mod)
1565             continue
1566         unknown_modules = True
1567         document.warning("Lyx2lyx knows nothing about module '%s'. "
1568                          "Please check if short title insets have been converted correctly."
1569                          % mod)
1570
1571     i = 0
1572     while True:
1573         i = find_token(document.body, "\\begin_inset Argument", i)
1574         if i == -1:
1575             return
1576
1577         if not safe_layout or unknown_modules:
1578             # We cannot do more here since we have no access to this layout.
1579             # InsetArgument itself will do the real work
1580             # (see InsetArgument::updateBuffer())
1581             document.body[i] = "\\begin_inset Argument 999"
1582             i += 1
1583             continue
1584         
1585         # Find containing paragraph layout
1586         parent = get_containing_layout(document.body, i)
1587         if parent == False:
1588             document.warning("Malformed LyX document: Can't find parent paragraph layout")
1589             i += 1
1590             continue
1591         parbeg = parent[1]
1592         parend = parent[2]
1593         allowed_opts = -1
1594         first_req = -1
1595         if len(used_caveat_modules) > 0:
1596             # We know for now that this must be the initials module with the Initial layout
1597             # If we get more such modules, we need some automating.
1598             if parent[0] == "Initial":
1599                 # Layout has 1 opt and 1 req arg.
1600                 # Count the actual arguments
1601                 actualargs = 0
1602                 for p in range(parbeg, parend):
1603                     if document.body[p] == "\\begin_inset Argument":
1604                         actualargs += 1
1605                 if actualargs == 1:
1606                     allowed_opts = 0
1607                     first_req = 2
1608         # Collect all arguments in this paragraph
1609         argnr = 0
1610         for p in range(parbeg, parend):
1611             if document.body[p] == "\\begin_inset Argument":
1612                 argnr += 1
1613                 if allowed_opts != -1:
1614                     # We have less arguments than opt + required.
1615                     # required must take precedence.
1616                     if argnr > allowed_opts and argnr < first_req:
1617                         argnr = first_req
1618                 document.body[p] = "\\begin_inset Argument %d" % argnr
1619         i = parend + 1
1620
1621
1622 def revert_latexargs(document):
1623     " Revert InsetArgument to old syntax "
1624
1625     i = 0
1626     rx = re.compile(r'^\\begin_inset Argument (\d+)$')
1627     args = dict()
1628     while True:
1629         # Search for Argument insets
1630         i = find_token(document.body, "\\begin_inset Argument", i)
1631         if i == -1:
1632             return
1633         m = rx.match(document.body[i])
1634         if not m:
1635             # No ID: inset already reverted
1636             i += 1
1637             continue
1638         # Find containing paragraph layout
1639         parent = get_containing_layout(document.body, i)
1640         if parent == False:
1641             document.warning("Malformed LyX document: Can't find parent paragraph layout")
1642             i += 1
1643             continue
1644         parbeg = parent[1]
1645         parend = parent[2]
1646         # Do not set realparbeg to parent[3], since this does not work if we
1647         # have another inset (e.g. label or index) before the first argument
1648         # inset (this is the case in the user guide of LyX 2.0.8)
1649         realparbeg = -1
1650         # Collect all arguments in this paragraph
1651         realparend = parend
1652         for p in range(parbeg, parend):
1653             m = rx.match(document.body[p])
1654             if m:
1655                 if realparbeg < 0:
1656                     # This is the first argument inset
1657                     realparbeg = p
1658                 val = int(m.group(1))
1659                 j = find_end_of_inset(document.body, p)
1660                 # Revert to old syntax
1661                 document.body[p] = "\\begin_inset Argument"
1662                 if j == -1:
1663                     document.warning("Malformed LyX document: Can't find end of Argument inset")
1664                     continue
1665                 if val > 0:
1666                     args[val] = document.body[p : j + 1]
1667                 # Adjust range end
1668                 realparend = realparend - len(document.body[p : j + 1])
1669                 # Remove arg inset at this position
1670                 del document.body[p : j + 1]
1671             if p >= realparend:
1672                 break
1673         if realparbeg < 0:
1674             # No argument inset found
1675             realparbeg = parent[3]
1676         # Now sort the arg insets
1677         subst = []
1678         for f in sorted(args):
1679             subst += args[f]
1680             del args[f]
1681         # Insert the sorted arg insets at paragraph begin
1682         document.body[realparbeg : realparbeg] = subst
1683
1684         i = realparbeg + 1 + len(subst)
1685
1686
1687 def revert_IEEEtran(document):
1688   '''
1689   Reverts InsetArgument of
1690   Page headings
1691   Biography
1692   Biography without photo
1693   to TeX-code
1694   '''
1695   if document.textclass == "IEEEtran":
1696     i = 0
1697     i2 = 0
1698     j = 0
1699     k = 0
1700     while True:
1701       if i != -1:
1702         i = find_token(document.body, "\\begin_layout Page headings", i)
1703       if i != -1:
1704         revert_Argument_to_TeX_brace(document, i, 0, 1, 1, False, False)
1705         i += 1
1706       if i2 != -1:
1707         i2 = find_token(document.body, "\\begin_inset Flex Paragraph Start", i2)
1708       if i2 != -1:
1709         revert_Argument_to_TeX_brace(document, i2, 0, 1, 1, False, False)
1710         i2 = i2 + 1
1711       if j != -1:
1712         j = find_token(document.body, "\\begin_layout Biography without photo", j)
1713       if j != -1:
1714         revert_Argument_to_TeX_brace(document, j, 0, 1, 1, True, False)
1715         j += 1
1716       if k != -1:
1717         k = find_token(document.body, "\\begin_layout Biography", k)
1718         kA = find_token(document.body, "\\begin_layout Biography without photo", k)
1719         if k == kA and k != -1:
1720           k += 1
1721           continue
1722       if k != -1:
1723         # start with the second argument, therefore 2
1724         revert_Argument_to_TeX_brace(document, k, 0, 2, 2, True, False)
1725         k += 1
1726       if i == -1 and i2 == -1 and j == -1 and k == -1:
1727         return
1728
1729
1730 def revert_IEEEtran_2(document):
1731   '''
1732   Reverts Flex Paragraph Start to TeX-code
1733   '''
1734   if document.textclass == "IEEEtran":
1735     begin = 0
1736     while True:
1737       begin = find_token(document.body, "\\begin_inset Flex Paragraph Start", begin)
1738       if begin == -1:
1739         return
1740       end1 = find_end_of_inset(document.body, begin)
1741       document.body[end1 - 2 : end1 + 1] = put_cmd_in_ert("}")
1742       document.body[begin : begin + 4] = put_cmd_in_ert("\\IEEEPARstart{")
1743       begin = begin + 5
1744
1745
1746 def convert_IEEEtran(document):
1747   '''
1748   Converts ERT of
1749   Page headings
1750   Biography
1751   Biography without photo
1752   to InsetArgument
1753   '''
1754   if document.textclass == "IEEEtran":
1755     i = 0
1756     j = 0
1757     k = 0
1758     while True:
1759       if i != -1:
1760         i = find_token(document.body, "\\begin_layout Page headings", i)
1761       if i != -1:
1762         convert_TeX_brace_to_Argument(document, i, 1, 1, False, False, False)
1763         i += 1
1764       if j != -1:
1765         j = find_token(document.body, "\\begin_layout Biography without photo", j)
1766       if j != -1:
1767         convert_TeX_brace_to_Argument(document, j, 1, 1, False, True, False)
1768         j += 1
1769       if k != -1:
1770         # assure that we don't handle Biography Biography without photo
1771         k = find_token(document.body, "\\begin_layout Biography", k)
1772         kA = find_token(document.body, "\\begin_layout Biography without photo", k - 1)
1773       if k == kA and k != -1:
1774         k += 1
1775         continue
1776       if k != -1:
1777         # the argument we want to convert is the second one
1778         convert_TeX_brace_to_Argument(document, k, 2, 2, False, True, False)
1779         k += 1
1780       if i == -1 and j == -1 and k == -1:
1781         return
1782
1783
1784 def revert_AASTeX(document):
1785   " Reverts InsetArgument of Altaffilation to TeX-code "
1786   if document.textclass == "aastex":
1787     i = 0
1788     while True:
1789       i = find_token(document.body, "\\begin_layout Altaffilation", i)
1790       if i == -1:
1791         return
1792       revert_Argument_to_TeX_brace(document, i, 0, 1, 1, False, False)
1793       i += 1
1794
1795
1796 def convert_AASTeX(document):
1797   " Converts ERT of Altaffilation to InsetArgument "
1798   if document.textclass == "aastex":
1799     i = 0
1800     while True:
1801       i = find_token(document.body, "\\begin_layout Altaffilation", i)
1802       if i == -1:
1803         return
1804       convert_TeX_brace_to_Argument(document, i, 1, 1, False, False, False)
1805       i += 1
1806
1807
1808 def revert_AGUTeX(document):
1809   " Reverts InsetArgument of Author affiliation to TeX-code "
1810   if document.textclass == "agutex":
1811     i = 0
1812     while True:
1813       i = find_token(document.body, "\\begin_layout Author affiliation", i)
1814       if i == -1:
1815         return
1816       revert_Argument_to_TeX_brace(document, i, 0, 1, 1, False, False)
1817       i += 1
1818
1819
1820 def convert_AGUTeX(document):
1821   " Converts ERT of Author affiliation to InsetArgument "
1822   if document.textclass == "agutex":
1823     i = 0
1824     while True:
1825       i = find_token(document.body, "\\begin_layout Author affiliation", i)
1826       if i == -1:
1827         return
1828       convert_TeX_brace_to_Argument(document, i, 1, 1, False, False, False)
1829       i += 1
1830
1831
1832 def revert_IJMP(document):
1833   " Reverts InsetArgument of MarkBoth to TeX-code "
1834   if document.textclass == "ijmpc" or document.textclass == "ijmpd":
1835     i = 0
1836     while True:
1837       i = find_token(document.body, "\\begin_layout MarkBoth", i)
1838       if i == -1:
1839         return
1840       revert_Argument_to_TeX_brace(document, i, 0, 1, 1, False, False)
1841       i += 1
1842
1843
1844 def convert_IJMP(document):
1845   " Converts ERT of MarkBoth to InsetArgument "
1846   if document.textclass == "ijmpc" or document.textclass == "ijmpd":
1847     i = 0
1848     while True:
1849       i = find_token(document.body, "\\begin_layout MarkBoth", i)
1850       if i == -1:
1851         return
1852       convert_TeX_brace_to_Argument(document, i, 1, 1, False, False, False)
1853       i += 1
1854
1855
1856 def revert_SIGPLAN(document):
1857   " Reverts InsetArguments of SIGPLAN to TeX-code "
1858   if document.textclass == "sigplanconf":
1859     i = 0
1860     j = 0
1861     while True:
1862       if i != -1:
1863         i = find_token(document.body, "\\begin_layout Conference", i)
1864       if i != -1:
1865         revert_Argument_to_TeX_brace(document, i, 0, 1, 1, False, False)
1866         i += 1
1867       if j != -1:
1868         j = find_token(document.body, "\\begin_layout Author", j)
1869       if j != -1:
1870         revert_Argument_to_TeX_brace(document, j, 0, 1, 2, False, False)
1871         j += 1
1872       if i == -1 and j == -1:
1873         return
1874
1875
1876 def convert_SIGPLAN(document):
1877   " Converts ERT of SIGPLAN to InsetArgument "
1878   if document.textclass == "sigplanconf":
1879     i = 0
1880     j = 0
1881     while True:
1882       if i != -1:
1883         i = find_token(document.body, "\\begin_layout Conference", i)
1884       if i != -1:
1885         convert_TeX_brace_to_Argument(document, i, 1, 1, False, False, False)
1886         i += 1
1887       if j != -1:
1888         j = find_token(document.body, "\\begin_layout Author", j)
1889       if j != -1:
1890         convert_TeX_brace_to_Argument(document, j, 1, 2, False, False, False)
1891         j += 1
1892       if i == -1 and j == -1:
1893         return
1894
1895
1896 def revert_SIGGRAPH(document):
1897   " Reverts InsetArgument of Flex CRcat to TeX-code "
1898   if document.textclass == "acmsiggraph":
1899     i = 0
1900     while True:
1901       i = find_token(document.body, "\\begin_inset Flex CRcat", i)
1902       if i == -1:
1903         return
1904       revert_Argument_to_TeX_brace(document, i, 0, 1, 3, False, False)
1905       i += 1
1906
1907
1908 def convert_SIGGRAPH(document):
1909   " Converts ERT of Flex CRcat to InsetArgument "
1910   if document.textclass == "acmsiggraph":
1911     i = 0
1912     while True:
1913       i = find_token(document.body, "\\begin_inset Flex CRcat", i)
1914       if i == -1:
1915         return
1916       convert_TeX_brace_to_Argument(document, i, 1, 3, True, False, False)
1917       i += 1
1918
1919
1920 def revert_EuropeCV(document):
1921   " Reverts InsetArguments of europeCV to TeX-code "
1922   if document.textclass == "europecv":
1923     i = 0
1924     j = 0
1925     k = 0
1926     m = 0
1927     while True:
1928       if i != -1:
1929         i = find_token(document.body, "\\begin_layout Item", i)
1930       if i != -1:
1931         revert_Argument_to_TeX_brace(document, i, 0, 2, 2, False, False)
1932         i += 1
1933       if j != -1:
1934         j = find_token(document.body, "\\begin_layout BulletedItem", j)
1935       if j != -1:
1936         revert_Argument_to_TeX_brace(document, j, 0, 2, 2, False, False)
1937         j += 1
1938       if k != -1:
1939         k = find_token(document.body, "\\begin_layout Language", k)
1940       if k != -1:
1941         revert_Argument_to_TeX_brace(document, k, 0, 2, 6, False, False)
1942         k += 1
1943       if m != -1:
1944         m = find_token(document.body, "\\begin_layout LastLanguage", m)
1945       if m != -1:
1946         revert_Argument_to_TeX_brace(document, m, 0, 2, 6, False, False)
1947         m += 1
1948       if i == -1 and j == -1 and k == -1 and m == -1:
1949         return
1950
1951
1952 def convert_EuropeCV(document):
1953   " Converts ERT of europeCV to InsetArgument "
1954   if document.textclass == "europecv":
1955     i = 0
1956     j = 0
1957     k = 0
1958     m = 0
1959     while True:
1960       if i != -1:
1961         i = find_token(document.body, "\\begin_layout Item", i)
1962       if i != -1:
1963         convert_TeX_brace_to_Argument(document, i, 2, 2, False, False, False)
1964         i += 1
1965       if j != -1:
1966         j = find_token(document.body, "\\begin_layout BulletedItem", j)
1967       if j != -1:
1968         convert_TeX_brace_to_Argument(document, j, 2, 2, False, False, False)
1969         j += 1
1970       if k != -1:
1971         k = find_token(document.body, "\\begin_layout Language", k)
1972       if k != -1:
1973         convert_TeX_brace_to_Argument(document, k, 2, 6, False, False, False)
1974         k += 1
1975       if m != -1:
1976         m = find_token(document.body, "\\begin_layout LastLanguage", m)
1977       if m != -1:
1978         convert_TeX_brace_to_Argument(document, m, 2, 6, False, False, False)
1979         m += 1
1980       if i == -1 and j == -1 and k == -1 and m == -1:
1981         return
1982
1983
1984 def revert_ModernCV(document):
1985   " Reverts InsetArguments of modernCV to TeX-code "
1986   if document.textclass == "moderncv":
1987     j = 0
1988     k = 0
1989     m = 0
1990     o = 0
1991     p = 0
1992     while True:
1993       if j != -1:
1994         j = find_token(document.body, "\\begin_layout Entry", j)
1995       if j != -1:
1996         revert_Argument_to_TeX_brace(document, j, 0, 1, 5, False, False)
1997         j += 1
1998       if k != -1:
1999         k = find_token(document.body, "\\begin_layout Item", k)
2000       if k != -1:
2001         revert_Argument_to_TeX_brace(document, k, 0, 1, 1, False, False)
2002         k += 1
2003       if m != -1:
2004         m = find_token(document.body, "\\begin_layout ItemWithComment", m)
2005       if m != -1:
2006         revert_Argument_to_TeX_brace(document, m, 0, 1, 2, False, False)
2007         document.body[m] = document.body[m].replace("\\begin_layout ItemWithComment", "\\begin_layout Language")
2008         m += 1
2009       if o != -1:
2010         o = find_token(document.body, "\\begin_layout DoubleItem", o)
2011       if o != -1:
2012         revert_Argument_to_TeX_brace(document, o, 0, 1, 3, False, False)
2013         document.body[o] = document.body[o].replace("\\begin_layout DoubleItem", "\\begin_layout Computer")
2014         o = o + 1
2015       if p != -1:
2016         p = find_token(document.body, "\\begin_layout Social", p)
2017       if p != -1:
2018         revert_Argument_to_TeX_brace(document, p, 0, 1, 1, False, True)
2019         p = p + 1
2020       if j == -1 and k == -1 and m == -1 and o == -1 and p == -1:
2021         return
2022
2023
2024 def revert_ModernCV_2(document):
2025   " Reverts the Flex:Column inset of modernCV to TeX-code "
2026   if document.textclass == "moderncv":
2027     flex = 0
2028     flexEnd = -1
2029     while True:
2030       flex = find_token(document.body, "\\begin_inset Flex Column", flex)
2031       if flex == -1:
2032         return flexEnd
2033       flexEnd = find_end_of_inset(document.body, flex)
2034       wasOpt = revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, False, True)
2035       revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, False, False)
2036       flexEnd = find_end_of_inset(document.body, flex)
2037       if wasOpt == True:
2038         document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\cvcolumn")
2039       else:
2040         document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\cvcolumn{")
2041       document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("}")
2042       flex += 1
2043
2044
2045 def revert_ModernCV_3(document):
2046   " Reverts the Column style of modernCV to TeX-code "
2047   if document.textclass == "moderncv":
2048     # revert the layouts
2049     revert_ModernCV(document)
2050     p = 0
2051     # get the position of the end of the last column inset
2052     LastFlexEnd = revert_ModernCV_2(document)
2053     while True:
2054       p = find_token(document.body, "\\begin_layout Columns", p)
2055       if p == -1:
2056         return
2057       pEnd = find_end_of_layout(document.body, p)
2058       document.body[p] = document.body[p].replace("\\begin_layout Columns", "\\begin_layout Standard")
2059       if LastFlexEnd != -1:
2060         document.body[p + 1 : p + 1] = put_cmd_in_ert("\\begin{cvcolumns}")
2061         document.body[LastFlexEnd + 24 : LastFlexEnd + 24] = put_cmd_in_ert("\\end{cvcolumns}")
2062       p += 1
2063
2064
2065 def revert_ModernCV_4(document):
2066   " Reverts the style Social to TeX-code "
2067   if document.textclass == "moderncv":
2068     # revert the layouts
2069     revert_ModernCV(document)
2070     p = 0
2071     while True:
2072       p = find_token(document.body, "\\begin_layout Social", p)
2073       if p == -1:
2074         return
2075       pEnd = find_end_of_layout(document.body, p)
2076       document.body[p] = document.body[p].replace("\\begin_layout Social", "\\begin_layout Standard")
2077       document.body[p + 1 : p + 1] = put_cmd_in_ert("\\social")
2078       hasOpt = find_token(document.body, "[", p + 9)
2079       if hasOpt < p + 18:
2080         document.body[p + 30 : p + 30] = put_cmd_in_ert("{")
2081         document.body[p + 41 : p + 41] = put_cmd_in_ert("}")
2082       else:
2083         document.body[p + 11 : p + 11] = put_cmd_in_ert("{")
2084         document.body[p + 21 : p + 21] = put_cmd_in_ert("}")
2085       p += 1
2086
2087
2088 def convert_ModernCV(document):
2089   " Converts ERT of modernCV to InsetArgument "
2090   if document.textclass == "moderncv":
2091     i = 0
2092     j = 0
2093     k = 0
2094     m = 0
2095     o = 0
2096     while True:
2097       if i != -1:
2098         i = find_token(document.body, "\\begin_layout DoubleItem", i)
2099       if i != -1:
2100         convert_TeX_brace_to_Argument(document, i, 1, 1, False, False, False)
2101         document.body[o] = document.body[o].replace("\\begin_layout DoubleItem", "\\begin_layout DoubleListItem")
2102         i += 1
2103       if j != -1:
2104         j = find_token(document.body, "\\begin_layout Entry", j)
2105       if j != -1:
2106         convert_TeX_brace_to_Argument(document, j, 1, 5, False, False, False)
2107         j += 1
2108       if k != -1:
2109         k = find_token(document.body, "\\begin_layout Item", k)
2110       if k != -1:
2111         convert_TeX_brace_to_Argument(document, k, 1, 1, False, False, False)
2112         k += 1
2113       if m != -1:
2114         m = find_token(document.body, "\\begin_layout Language", m)
2115       if m != -1:
2116         convert_TeX_brace_to_Argument(document, m, 1, 2, False, False, False)
2117         m += 1
2118       if i == -1 and j == -1 and k == -1 and m == -1:
2119         return
2120
2121
2122 def revert_Initials(document):
2123   " Reverts InsetArgument of Initial to TeX-code "
2124   i = 0
2125   while True:
2126     i = find_token(document.body, "\\begin_layout Initial", i)
2127     if i == -1:
2128       return
2129     # first arg (optional) and second arg (first mandatory) are supported in LyX 2.0.x
2130     revert_Argument_to_TeX_brace(document, i, 0, 3, 3, False, False)
2131     i += 1
2132
2133
2134 def convert_Initials(document):
2135   " Converts ERT of Initial to InsetArgument "
2136   i = 0
2137   while True:
2138     i = find_token(document.body, "\\begin_layout Initial", i)
2139     if i == -1:
2140       return
2141     convert_TeX_brace_to_Argument(document, i, 3, 3, False, False, False)
2142     i += 1
2143
2144
2145 def revert_literate(document):
2146     " Revert Literate document to old format "
2147     if del_token(document.header, "noweb", 0):
2148       document.textclass = "literate-" + document.textclass
2149       i = 0
2150       while True:
2151         i = find_token(document.body, "\\begin_layout Chunk", i)
2152         if i == -1:
2153           break
2154         document.body[i] = "\\begin_layout Scrap"
2155         i += 1
2156
2157
2158 def convert_literate(document):
2159     " Convert Literate document to new format"
2160     i = find_token(document.header, "\\textclass", 0)    
2161     if (i != -1) and "literate-" in document.header[i]:
2162       document.textclass = document.header[i].replace("\\textclass literate-", "")
2163       j = find_token(document.header, "\\begin_modules", 0)
2164       if (j != -1):
2165         document.header.insert(j + 1, "noweb")
2166       else:
2167         document.header.insert(i + 1, "\\end_modules")
2168         document.header.insert(i + 1, "noweb")
2169         document.header.insert(i + 1, "\\begin_modules")
2170       i = 0
2171       while True:
2172         i = find_token(document.body, "\\begin_layout Scrap", i)
2173         if i == -1:
2174           break
2175         document.body[i] = "\\begin_layout Chunk"
2176         i += 1
2177
2178
2179 def revert_itemargs(document):
2180     " Reverts \\item arguments to TeX-code "
2181     i = 0
2182     while True:
2183         i = find_token(document.body, "\\begin_inset Argument item:", i)
2184         if i == -1:
2185             return
2186         j = find_end_of_inset(document.body, i)
2187         # Find containing paragraph layout
2188         parent = get_containing_layout(document.body, i)
2189         if parent == False:
2190             document.warning("Malformed LyX document: Can't find parent paragraph layout")
2191             i += 1
2192             continue
2193         parbeg = parent[3]
2194         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2195         endPlain = find_end_of_layout(document.body, beginPlain)
2196         content = document.body[beginPlain + 1 : endPlain]
2197         del document.body[i:j+1]
2198         subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
2199         document.body[parbeg : parbeg] = subst
2200         i += 1
2201
2202
2203 def revert_garamondx_newtxmath(document):
2204     " Revert native garamond newtxmath definition to LaTeX " 
2205
2206     i = find_token(document.header, "\\font_math", 0)
2207     if i == -1:
2208        return
2209     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
2210         val = get_value(document.header, "\\font_math", i)
2211         if val == "garamondx-ntxm":
2212             add_to_preamble(document, "\\usepackage[garamondx]{newtxmath}")
2213             document.header[i] = "\\font_math auto"
2214
2215
2216 def revert_garamondx(document):
2217     " Revert native garamond font definition to LaTeX " 
2218
2219     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
2220         i = find_token(document.header, "\\font_roman garamondx", 0)
2221         if i != -1:
2222             osf = False
2223             j = find_token(document.header, "\\font_osf true", 0)
2224             if j != -1:
2225                 osf = True
2226             preamble = "\\usepackage"
2227             if osf:
2228                 preamble += "[osfI]"
2229             preamble += "{garamondx}"
2230             add_to_preamble(document, [preamble])
2231             document.header[i] = "\\font_roman default"
2232
2233
2234 def convert_beamerargs(document):
2235     " Converts beamer arguments to new layout "
2236     
2237     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
2238     if document.textclass not in beamer_classes:
2239         return
2240
2241     shifted_layouts = ["Part", "Section", "Subsection", "Subsubsection"]
2242     list_layouts = ["Itemize", "Enumerate", "Description"]
2243     rx = re.compile(r'^\\begin_inset Argument (\d+)$')
2244
2245     i = 0
2246     while True:
2247         i = find_token(document.body, "\\begin_inset Argument", i)
2248         if i == -1:
2249             return
2250         # Find containing paragraph layout
2251         parent = get_containing_layout(document.body, i)
2252         if parent == False:
2253             document.warning("Malformed LyX document: Can't find parent paragraph layout")
2254             i += 1
2255             continue
2256         parbeg = parent[1]
2257         parend = parent[2]
2258         layoutname = parent[0]
2259         for p in range(parbeg, parend):
2260             if layoutname in shifted_layouts:
2261                 m = rx.match(document.body[p])
2262                 if m:
2263                     argnr = int(m.group(1))
2264                     argnr += 1
2265                     document.body[p] = "\\begin_inset Argument %d" % argnr
2266             if layoutname == "AgainFrame":
2267                 m = rx.match(document.body[p])
2268                 if m:
2269                     document.body[p] = "\\begin_inset Argument 3"
2270                     if document.body[p + 4] == "\\begin_inset ERT":
2271                         if document.body[p + 9].startswith("<"):
2272                             # This is an overlay specification
2273                             # strip off the <
2274                             document.body[p + 9] = document.body[p + 9][1:]
2275                             if document.body[p + 9].endswith(">"):
2276                                 # strip off the >
2277                                 document.body[p + 9] = document.body[p + 9][:-1]
2278                                 # Shift this one
2279                                 document.body[p] = "\\begin_inset Argument 2"
2280             if layoutname in list_layouts:
2281                 m = rx.match(document.body[p])
2282                 if m:
2283                     if m.group(1) == "1":
2284                         if document.body[p + 4] == "\\begin_inset ERT":
2285                             if document.body[p + 9].startswith("<"):
2286                                 # This is an overlay specification
2287                                 # strip off the <
2288                                 document.body[p + 9] = document.body[p + 9][1:]
2289                                 if document.body[p + 9].endswith(">"):
2290                                     # strip off the >
2291                                     document.body[p + 9] = document.body[p + 9][:-1]
2292                         elif document.body[p + 4].startswith("<"):
2293                             # This is an overlay specification (without ERT)
2294                             # strip off the <
2295                             document.body[p + 4] = document.body[p + 4][1:]
2296                             if document.body[p + 4].endswith(">"):
2297                                 # strip off the >
2298                                 document.body[p + 4] = document.body[p + 4][:-1]
2299                         elif layoutname != "Itemize":
2300                             # Shift this one
2301                             document.body[p] = "\\begin_inset Argument 2"
2302         i += 1
2303
2304
2305 #
2306 # Helper function for the frame conversion routines
2307 #
2308 # FIXME: This method currently requires the arguments to be either
2309 #        * In one (whole) ERT each: <ERT>[<arg1>]</ERT><ERT><arg2></ERT><ERT>[arg3]</ERT>
2310 #        * Altogether in one whole ERT: <ERT>[<arg1>]<arg2>[arg3]</ERT>
2311 #        If individual arguments mix ERT and non-ERT or are splitted
2312 #        over several ERTs, the parsing fails.
2313 def convert_beamerframeargs(document, i, parbeg):
2314     ertend = i
2315     while True:
2316         if document.body[parbeg] != "\\begin_inset ERT":
2317             return ertend
2318         ertend = find_end_of_inset(document.body, parbeg)
2319         if ertend == -1:
2320             document.warning("Malformed LyX document: missing ERT \\end_inset")
2321             return ertend
2322         ertcont = parbeg + 5
2323         if document.body[ertcont].startswith("[<"):
2324             # This is a default overlay specification
2325             # strip off the [<
2326             document.body[ertcont] = document.body[ertcont][2:]
2327             if document.body[ertcont].endswith(">]"):
2328                 # strip off the >]
2329                 document.body[ertcont] = document.body[ertcont][:-2]
2330             elif document.body[ertcont].endswith("]"):
2331                 # divide the args
2332                 tok = document.body[ertcont].find('>][')
2333                 if tok != -1:
2334                     subst = [document.body[ertcont][:tok],
2335                               '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 3',
2336                               'status collapsed', '', '\\begin_layout Plain Layout',
2337                               document.body[ertcont][tok + 3:-1]]
2338                     document.body[ertcont : ertcont + 1] = subst
2339                     ertend += 11
2340             # Convert to ArgInset
2341             document.body[parbeg] = "\\begin_inset Argument 2"
2342         elif document.body[ertcont].startswith("<"):
2343             # This is an overlay specification
2344             # strip off the <
2345             document.body[ertcont] = document.body[ertcont][1:]
2346             if document.body[ertcont].endswith(">"):
2347                 # strip off the >
2348                 document.body[ertcont] = document.body[ertcont][:-1]
2349                 # Convert to ArgInset
2350                 document.body[parbeg] = "\\begin_inset Argument 1"
2351             elif document.body[ertcont].endswith(">]"):
2352                 # divide the args
2353                 tok = document.body[ertcont].find('>[<')
2354                 if tok != -1:
2355                     document.body[ertcont : ertcont + 1] = [document.body[ertcont][:tok],
2356                                                     '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 2',
2357                                                     'status collapsed', '', '\\begin_layout Plain Layout',
2358                                                     document.body[ertcont][tok + 3:-2]]
2359                 # Convert to ArgInset
2360                 document.body[parbeg] = "\\begin_inset Argument 1"
2361                 ertend += 11
2362             elif document.body[ertcont].endswith("]"):
2363                 # divide the args
2364                 tok = document.body[ertcont].find('>[<')
2365                 if tok != -1:
2366                     # divide the args
2367                     tokk = document.body[ertcont].find('>][')
2368                     if tokk != -1:
2369                         document.body[ertcont : ertcont + 1] = [document.body[ertcont][:tok],
2370                                                         '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 2',
2371                                                         'status collapsed', '', '\\begin_layout Plain Layout',
2372                                                         document.body[ertcont][tok + 3:tokk],
2373                                                         '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 3',
2374                                                         'status collapsed', '', '\\begin_layout Plain Layout',
2375                                                         document.body[ertcont][tokk + 3:-1]]
2376                         ertend += 22
2377                 else:
2378                     tokk = document.body[ertcont].find('>[')
2379                     if tokk != -1:
2380                         document.body[ertcont : ertcont + 1] = [document.body[ertcont][:tokk],
2381                                                         '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 3',
2382                                                         'status collapsed', '', '\\begin_layout Plain Layout',
2383                                                         document.body[ertcont][tokk + 2:-1]]
2384                         ertend += 11
2385                 # Convert to ArgInset
2386                 document.body[parbeg] = "\\begin_inset Argument 1"
2387         elif document.body[ertcont].startswith("["):
2388             # This is an ERT option
2389             # strip off the [
2390             document.body[ertcont] = document.body[ertcont][1:]
2391             if document.body[ertcont].endswith("]"):
2392                 # strip off the ]
2393                 document.body[ertcont] = document.body[ertcont][:-1]
2394                 # Convert to ArgInset
2395                 document.body[parbeg] = "\\begin_inset Argument 3"
2396         parbeg = ertend + 3
2397         continue
2398     return ertend
2399
2400
2401 def convert_againframe_args(document):
2402     " Converts beamer AgainFrame to new layout "
2403
2404     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
2405     if document.textclass not in beamer_classes:
2406         return
2407    
2408     i = 0
2409     while True:
2410         i = find_token(document.body, "\\begin_layout AgainFrame", i)
2411         if i == -1:
2412             break
2413         parent = get_containing_layout(document.body, i)
2414         if parent[1] != i:
2415             document.warning("Wrong parent layout!")
2416         j = parent[2]
2417         parbeg = parent[3]
2418         if i != -1:
2419             # Convert ERT arguments
2420             # FIXME: See restrictions in convert_beamerframeargs method
2421             ertend = convert_beamerframeargs(document, i, parbeg)
2422             if ertend == -1:
2423                 break
2424         i = j
2425
2426
2427 def convert_corollary_args(document):
2428     " Converts beamer corrolary-style ERT arguments native InsetArgs "
2429     
2430     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
2431     if document.textclass not in beamer_classes:
2432         return
2433    
2434     corollary_layouts = ["Corollary", "Definition", "Definitions", "Example", "Examples", "Fact", "Proof", "Theorem"]
2435     for lay in corollary_layouts:
2436         i = 0
2437         while True:
2438             i = find_token_exact(document.body, "\\begin_layout " + lay, i)
2439             if i == -1:
2440                 break
2441             parent = get_containing_layout(document.body, i)
2442             if parent[1] != i:
2443                 document.warning("Wrong parent layout!")
2444             j = parent[2]
2445             parbeg = parent[3]
2446             if i != -1:
2447                 if document.body[parbeg] == "\\begin_inset ERT":
2448                     ertcontfirstline = parbeg + 5
2449                     # Find the last ERT in this paragraph (which might also be the first)
2450                     lastertbeg = find_token_backwards(document.body, "\\begin_inset ERT", j)
2451                     if lastertbeg == -1:
2452                         document.warning("Last ERT not found!")
2453                         break
2454                     lastertend = find_end_of_inset(document.body, lastertbeg)
2455                     if lastertend == -1:
2456                         document.warning("End of last ERT not found!")
2457                         break
2458                     ertcontlastline = lastertend - 3
2459                     if document.body[ertcontfirstline].startswith("<"):
2460                         # This is an overlay specification
2461                         # strip off the <
2462                         document.body[ertcontfirstline] = document.body[ertcontfirstline][1:]
2463                         if document.body[ertcontlastline].endswith(">"):
2464                             # strip off the >
2465                             document.body[ertcontlastline] = document.body[ertcontlastline][:-1]
2466                             if ertcontfirstline < ertcontlastline:
2467                                 # Multiline ERT. Might contain TeX code.  Embrace in ERT.
2468                                 document.body[ertcontlastline : ertcontlastline + 1] = [
2469                                                                     document.body[ertcontlastline], '\\end_layout', '', '\\end_inset']
2470                                 document.body[ertcontfirstline : ertcontfirstline + 1] = [
2471                                                                     '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 1',
2472                                                                     'status collapsed', '', '\\begin_layout Plain Layout',
2473                                                                     '\\begin_inset ERT', '', 'status open' '', '\\begin_layout Plain Layout',
2474                                                                     document.body[ertcontfirstline]]
2475                             else:
2476                                 # Convert to ArgInset
2477                                 document.body[parbeg] = "\\begin_inset Argument 1"
2478                         elif document.body[ertcontlastline].endswith("]"):
2479                             # divide the args
2480                             tok = document.body[ertcontfirstline].find('>[')
2481                             if tok != -1:
2482                                 if ertcontfirstline < ertcontlastline:
2483                                     # Multiline ERT. Might contain TeX code.  Embrace in ERT.
2484                                     document.body[ertcontlastline : ertcontlastline + 1] = [
2485                                                                         document.body[ertcontlastline], '\\end_layout', '', '\\end_inset']
2486                                     document.body[ertcontfirstline : ertcontfirstline + 1] = [document.body[ertcontfirstline][:tok],
2487                                                                         '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 2',
2488                                                                         'status collapsed', '', '\\begin_layout Plain Layout',
2489                                                                         '\\begin_inset ERT', '', 'status open' '', '\\begin_layout Plain Layout',
2490                                                                         document.body[ertcontfirstline][tok + 2:-1]]
2491                                 else:
2492                                     document.body[ertcontfirstline : ertcontfirstline + 1] = [document.body[ertcontfirstline][:tok],
2493                                                                         '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 2',
2494                                                                         'status collapsed', '', '\\begin_layout Plain Layout',
2495                                                                         document.body[ertcontfirstline][tok + 2:-1]]
2496                             # Convert to ArgInset
2497                             document.body[parbeg] = "\\begin_inset Argument 1"
2498                         i = j
2499                         continue
2500                     elif document.body[ertcontlastline].startswith("["):
2501                         if document.body[ertcontlastline].endswith("]"):
2502                             # This is an ERT option
2503                             # strip off the [
2504                             document.body[ertcontlastline] = document.body[ertcontlastline][1:]
2505                             # strip off the ]
2506                             document.body[ertcontlastline] = document.body[ertcontlastline][:-1]
2507                             # Convert to ArgInset
2508                             document.body[parbeg] = "\\begin_inset Argument 2"
2509                         else:
2510                             convert_TeX_brace_to_Argument(document, i, 2, 2, False, True, True)
2511                     i += 1
2512                     continue
2513             i = j
2514
2515
2516
2517 def convert_quote_args(document):
2518     " Converts beamer quote style ERT args to native InsetArgs "
2519     
2520     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
2521     if document.textclass not in beamer_classes:
2522         return
2523    
2524     quote_layouts = ["Uncover", "Only", "Quotation", "Quote", "Verse"]
2525     for lay in quote_layouts:
2526         i = 0
2527         while True:
2528             i = find_token(document.body, "\\begin_layout " + lay, i)
2529             if i == -1:
2530                 break
2531             parent = get_containing_layout(document.body, i)
2532             if parent[1] != i:
2533                 document.warning("Wrong parent layout!")
2534             j = parent[2]
2535             parbeg = parent[3]
2536             if i != -1:
2537                 if document.body[parbeg] == "\\begin_inset ERT":
2538                     if document.body[i + 6].startswith("<"):
2539                         # This is an overlay specification
2540                         # strip off the <
2541                         document.body[i + 6] = document.body[i + 6][1:]
2542                         if document.body[i + 6].endswith(">"):
2543                             # strip off the >
2544                             document.body[i + 6] = document.body[i + 6][:-1]
2545                             # Convert to ArgInset
2546                             document.body[i + 1] = "\\begin_inset Argument 1"
2547             i = j
2548
2549
2550 def cleanup_beamerargs(document):
2551     " Clean up empty ERTs (conversion artefacts) "
2552
2553     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
2554     if document.textclass not in beamer_classes:
2555         return
2556
2557     i = 0
2558     while True:
2559         i = find_token(document.body, "\\begin_inset Argument", i)
2560         if i == -1:
2561             return
2562         j = find_end_of_inset(document.body, i)
2563         if j == -1:
2564             document.warning("Malformed LyX document: Can't find end of Argument inset")
2565             i += 1
2566             continue
2567         while True:
2568             ertbeg = find_token(document.body, "\\begin_inset ERT", i, j)
2569             if ertbeg == -1:
2570                 break
2571             ertend = find_end_of_inset(document.body, ertbeg)
2572             if ertend == -1:
2573                 document.warning("Malformed LyX document: Can't find end of ERT inset")
2574                 break
2575             stripped = [line for line in document.body[ertbeg : ertend + 1] if line.strip()]
2576             if len(stripped) == 5:
2577                 # This is an empty ERT
2578                 offset = len(document.body[ertbeg : ertend + 1])
2579                 del document.body[ertbeg : ertend + 1]
2580                 j = j - offset
2581             else:
2582                 i = ertend
2583         i += 1
2584
2585
2586 def revert_beamerargs(document):
2587     " Reverts beamer arguments to old layout "
2588     
2589     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
2590     if document.textclass not in beamer_classes:
2591         return
2592
2593     i = 0
2594     list_layouts = ["Itemize", "Enumerate", "Description"]
2595     headings = ["Part", "Section", "Section*", "Subsection", "Subsection*",
2596                 "Subsubsection", "Subsubsection*", "FrameSubtitle", "NoteItem"]
2597     quote_layouts = ["Uncover", "Only", "Quotation", "Quote", "Verse"]
2598     corollary_layouts = ["Corollary", "Definition", "Definitions", "Example", "Examples", "Fact", "Proof", "Theorem"]
2599     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
2600
2601     while True:
2602         i = find_token(document.body, "\\begin_inset Argument", i)
2603         if i == -1:
2604             return
2605         # Find containing paragraph layout
2606         parent = get_containing_layout(document.body, i)
2607         if parent == False:
2608             document.warning("Malformed LyX document: Can't find parent paragraph layout")
2609             i += 1
2610             continue
2611         parbeg = parent[1]
2612         parend = parent[2]
2613         realparbeg = parent[3]
2614         layoutname = parent[0]
2615         realparend = parend
2616         for p in range(parbeg, parend):
2617             if p >= realparend:
2618                 i = realparend
2619                 break
2620             if layoutname in headings:
2621                 m = rx.match(document.body[p])
2622                 if m:
2623                     argnr = m.group(1)
2624                     if argnr == "1":
2625                         # Find containing paragraph layout
2626                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2627                         endPlain = find_end_of_layout(document.body, beginPlain)
2628                         endInset = find_end_of_inset(document.body, p)
2629                         argcontent = document.body[beginPlain + 1 : endPlain]
2630                         # Adjust range end
2631                         realparend = realparend - len(document.body[p : endInset + 1])
2632                         # Remove arg inset
2633                         del document.body[p : endInset + 1]
2634                         if layoutname == "FrameSubtitle":
2635                             pre = put_cmd_in_ert("\\" + layoutname.lower() + "<") + argcontent + put_cmd_in_ert(">")
2636                         elif layoutname == "NoteItem":
2637                             pre = put_cmd_in_ert("\\note<") + argcontent + put_cmd_in_ert(">[item]")
2638                         elif layoutname.endswith('*'):
2639                             pre = put_cmd_in_ert("\\lyxframeend\\" + layoutname.lower()[:-1] + "<") + argcontent + put_cmd_in_ert(">*")
2640                         else:
2641                             pre = put_cmd_in_ert("\\lyxframeend\\" + layoutname.lower() + "<") + argcontent + put_cmd_in_ert(">")
2642                         secarg = find_token(document.body, "\\begin_inset Argument 2", parbeg, parend)
2643                         if secarg != -1:
2644                             # Find containing paragraph layout
2645                             beginPlain = find_token(document.body, "\\begin_layout Plain Layout", secarg)
2646                             endPlain = find_end_of_layout(document.body, beginPlain)
2647                             endInset = find_end_of_inset(document.body, secarg)
2648                             argcontent = document.body[beginPlain + 1 : endPlain]
2649                             # Adjust range end
2650                             realparend = realparend - len(document.body[secarg : endInset + 1])
2651                             del document.body[secarg : endInset + 1]
2652                             pre += put_cmd_in_ert("[") + argcontent + put_cmd_in_ert("]")
2653                         pre += put_cmd_in_ert("{")
2654                         document.body[parbeg] = "\\begin_layout Standard"
2655                         document.body[realparbeg : realparbeg] = pre
2656                         pe = find_end_of_layout(document.body, parbeg)
2657                         post = put_cmd_in_ert("}")
2658                         document.body[pe : pe] = post
2659                         realparend += len(pre) + len(post)
2660             if layoutname == "AgainFrame":
2661                 m = rx.match(document.body[p])
2662                 if m:
2663                     argnr = m.group(1)
2664                     if argnr == "3":
2665                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2666                         endPlain = find_end_of_layout(document.body, beginPlain)
2667                         endInset = find_end_of_inset(document.body, p)
2668                         content = document.body[beginPlain + 1 : endPlain]
2669                         # Adjust range end
2670                         realparend = realparend - len(document.body[p : endInset + 1])
2671                         # Remove arg inset
2672                         del document.body[p : endInset + 1]
2673                         subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
2674                         document.body[realparbeg : realparbeg] = subst
2675             if layoutname == "Overprint":
2676                 m = rx.match(document.body[p])
2677                 if m:
2678                     argnr = m.group(1)
2679                     if argnr == "1":
2680                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2681                         endPlain = find_end_of_layout(document.body, beginPlain)
2682                         endInset = find_end_of_inset(document.body, p)
2683                         content = document.body[beginPlain + 1 : endPlain]
2684                         # Adjust range end
2685                         realparend = realparend - len(document.body[p : endInset + 1])
2686                         # Remove arg inset
2687                         del document.body[p : endInset + 1]
2688                         subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
2689                         document.body[realparbeg : realparbeg] = subst
2690             if layoutname == "OverlayArea":
2691                 m = rx.match(document.body[p])
2692                 if m:
2693                     argnr = m.group(1)
2694                     if argnr == "2":
2695                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2696                         endPlain = find_end_of_layout(document.body, beginPlain)
2697                         endInset = find_end_of_inset(document.body, p)
2698                         content = document.body[beginPlain + 1 : endPlain]
2699                         # Adjust range end
2700                         realparend = realparend - len(document.body[p : endInset + 1])
2701                         # Remove arg inset
2702                         del document.body[p : endInset + 1]
2703                         subst = put_cmd_in_ert("{") + content + put_cmd_in_ert("}")
2704                         document.body[realparbeg : realparbeg] = subst
2705             if layoutname in list_layouts:
2706                 m = rx.match(document.body[p])
2707                 if m:
2708                     argnr = m.group(1)
2709                     if argnr == "1":
2710                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2711                         endPlain = find_end_of_layout(document.body, beginPlain)
2712                         endInset = find_end_of_inset(document.body, p)
2713                         content = document.body[beginPlain + 1 : endPlain]
2714                         subst = put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
2715                         realparend = realparend + len(subst) - len(content)
2716                         document.body[beginPlain + 1 : endPlain] = subst
2717                     elif argnr == "item:1":
2718                         j = find_end_of_inset(document.body, i)
2719                         # Find containing paragraph layout
2720                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2721                         endPlain = find_end_of_layout(document.body, beginPlain)
2722                         content = document.body[beginPlain + 1 : endPlain]
2723                         del document.body[i:j+1]
2724                         if layoutname == "Description":
2725                             # Description only has one (overlay) item arg
2726                             subst = put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
2727                             # This must be put after the first space (begin of decription body
2728                             # in LyX's arkward description list syntax)
2729                             # Try to find that place ...
2730                             rxx = re.compile(r'^([^\\ ]+ )(.*)$')
2731                             for q in range(parbeg, parend):
2732                                 m = rxx.match(document.body[q])
2733                                 if m:
2734                                     # We found it. Now insert the ERT argument just there:
2735                                     document.body[q : q] = [m.group(1), ''] + subst + ['', m.group(2)]
2736                                     break
2737                         else:
2738                             subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
2739                             document.body[realparbeg : realparbeg] = subst
2740                     elif argnr == "item:2":
2741                         j = find_end_of_inset(document.body, i)
2742                         # Find containing paragraph layout
2743                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2744                         endPlain = find_end_of_layout(document.body, beginPlain)
2745                         content = document.body[beginPlain + 1 : endPlain]
2746                         del document.body[i:j+1]
2747                         subst = put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
2748                         document.body[realparbeg : realparbeg] = subst
2749             if layoutname in quote_layouts:
2750                 m = rx.match(document.body[p])
2751                 if m:
2752                     argnr = m.group(1)
2753                     if argnr == "1":
2754                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2755                         endPlain = find_end_of_layout(document.body, beginPlain)
2756                         endInset = find_end_of_inset(document.body, p)
2757                         content = document.body[beginPlain + 1 : endPlain]
2758                         # Adjust range end
2759                         realparend = realparend - len(document.body[p : endInset + 1])
2760                         # Remove arg inset
2761                         del document.body[p : endInset + 1]
2762                         subst = put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
2763                         document.body[realparbeg : realparbeg] = subst
2764             if layoutname in corollary_layouts:
2765                 m = rx.match(document.body[p])
2766                 if m:
2767                     argnr = m.group(1)
2768                     if argnr == "2":
2769                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2770                         endPlain = find_end_of_layout(document.body, beginPlain)
2771                         endInset = find_end_of_inset(document.body, p)
2772                         content = document.body[beginPlain + 1 : endPlain]
2773                         # Adjust range end
2774                         realparend = realparend - len(document.body[p : endInset + 1])
2775                         # Remove arg inset
2776                         del document.body[p : endInset + 1]
2777                         subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
2778                         document.body[realparbeg : realparbeg] = subst
2779         
2780         i = realparend
2781
2782
2783 def revert_beamerargs2(document):
2784     " Reverts beamer arguments to old layout, step 2 "
2785     
2786     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
2787     if document.textclass not in beamer_classes:
2788         return
2789
2790     i = 0
2791     shifted_layouts = ["Part", "Section", "Subsection", "Subsubsection"]
2792     corollary_layouts = ["Corollary", "Definition", "Definitions", "Example", "Examples", "Fact", "Proof", "Theorem"]
2793     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
2794
2795     while True:
2796         i = find_token(document.body, "\\begin_inset Argument", i)
2797         if i == -1:
2798             return
2799         # Find containing paragraph layout
2800         parent = get_containing_layout(document.body, i)
2801         if parent == False:
2802             document.warning("Malformed LyX document: Can't find parent paragraph layout")
2803             i += 1
2804             continue
2805         parbeg = parent[1]
2806         parend = parent[2]
2807         realparbeg = parent[3]
2808         layoutname = parent[0]
2809         realparend = parend
2810         for p in range(parbeg, parend):
2811             if p >= realparend:
2812                 i = realparend
2813                 break
2814             if layoutname in shifted_layouts:
2815                 m = rx.match(document.body[p])
2816                 if m:
2817                     argnr = m.group(1)
2818                     if argnr == "2":
2819                         document.body[p] = "\\begin_inset Argument 1"       
2820             if layoutname in corollary_layouts:
2821                 m = rx.match(document.body[p])
2822                 if m:
2823                     argnr = m.group(1)
2824                     if argnr == "1":
2825                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2826                         endPlain = find_end_of_layout(document.body, beginPlain)
2827                         endInset = find_end_of_inset(document.body, p)
2828                         content = document.body[beginPlain + 1 : endPlain]
2829                         # Adjust range end
2830                         realparend = realparend - len(document.body[p : endInset + 1])
2831                         # Remove arg inset
2832                         del document.body[p : endInset + 1]
2833                         subst = put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
2834                         document.body[realparbeg : realparbeg] = subst
2835             if layoutname == "OverlayArea":
2836                 m = rx.match(document.body[p])
2837                 if m:
2838                     argnr = m.group(1)
2839                     if argnr == "1":
2840                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2841                         endPlain = find_end_of_layout(document.body, beginPlain)
2842                         endInset = find_end_of_inset(document.body, p)
2843                         content = document.body[beginPlain + 1 : endPlain]
2844                         # Adjust range end
2845                         realparend = realparend - len(document.body[p : endInset + 1])
2846                         # Remove arg inset
2847                         del document.body[p : endInset + 1]
2848                         subst = put_cmd_in_ert("{") + content + put_cmd_in_ert("}")
2849                         document.body[realparbeg : realparbeg] = subst
2850             if layoutname == "AgainFrame":
2851                 m = rx.match(document.body[p])
2852                 if m:
2853                     argnr = m.group(1)
2854                     if argnr == "2":
2855                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2856                         endPlain = find_end_of_layout(document.body, beginPlain)
2857                         endInset = find_end_of_inset(document.body, p)
2858                         content = document.body[beginPlain + 1 : endPlain]
2859                         # Adjust range end
2860                         realparend = realparend - len(document.body[p : endInset + 1])
2861                         # Remove arg inset
2862                         del document.body[p : endInset + 1]
2863                         subst = put_cmd_in_ert("[<") + content + put_cmd_in_ert(">]")
2864                         document.body[realparbeg : realparbeg] = subst
2865         i = realparend
2866
2867
2868 def revert_beamerargs3(document):
2869     " Reverts beamer arguments to old layout, step 3 "
2870     
2871     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
2872     if document.textclass not in beamer_classes:
2873         return
2874
2875     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
2876     i = 0
2877     while True:
2878         i = find_token(document.body, "\\begin_inset Argument", i)
2879         if i == -1:
2880             return
2881         # Find containing paragraph layout
2882         parent = get_containing_layout(document.body, i)
2883         if parent == False:
2884             document.warning("Malformed LyX document: Can't find parent paragraph layout")
2885             i += 1
2886             continue
2887         parbeg = parent[1]
2888         parend = parent[2]
2889         realparbeg = parent[3]
2890         layoutname = parent[0]
2891         realparend = parend
2892         for p in range(parbeg, parend):
2893             if p >= realparend:
2894                 i = realparend
2895                 break
2896             if layoutname == "AgainFrame":
2897                 m = rx.match(document.body[p])
2898                 if m:
2899                     argnr = m.group(1)
2900                     if argnr == "1":
2901                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2902                         endPlain = find_end_of_layout(document.body, beginPlain)
2903                         endInset = find_end_of_inset(document.body, p)
2904                         content = document.body[beginPlain + 1 : endPlain]
2905                         # Adjust range end
2906                         realparend = realparend - len(document.body[p : endInset + 1])
2907                         # Remove arg inset
2908                         del document.body[p : endInset + 1]
2909                         subst = put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
2910                         document.body[realparbeg : realparbeg] = subst
2911         i = realparend
2912
2913
2914 def revert_beamerflex(document):
2915     " Reverts beamer Flex insets "
2916     
2917     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
2918     if document.textclass not in beamer_classes:
2919         return
2920
2921     new_flexes = {"Bold" : "\\textbf", "Emphasize" : "\\emph", "Only" : "\\only",
2922                   "Uncover" : "\\uncover", "Visible" : "\\visible",
2923                   "Invisible" : "\\invisible", "Alternative" : "\\alt",
2924                   "Beamer_Note" : "\\note"}
2925     old_flexes = {"Alert" : "\\alert", "Structure" : "\\structure"}
2926     rx = re.compile(r'^\\begin_inset Flex (.+)$')
2927
2928     i = 0
2929     while True:
2930         i = find_token(document.body, "\\begin_inset Flex", i)
2931         if i == -1:
2932             return
2933         m = rx.match(document.body[i])
2934         if m:
2935             flextype = m.group(1)
2936             z = find_end_of_inset(document.body, i)
2937             if z == -1:
2938                 document.warning("Can't find end of Flex " + flextype + " inset.")
2939                 i += 1
2940                 continue
2941             if flextype in new_flexes:
2942                 pre = put_cmd_in_ert(new_flexes[flextype])
2943                 arg = find_token(document.body, "\\begin_inset Argument 1", i, z)
2944                 if arg != -1:
2945                     argend = find_end_of_inset(document.body, arg)
2946                     if argend == -1:
2947                         document.warning("Can't find end of Argument!")
2948                         i += 1
2949                         continue
2950                     # Find containing paragraph layout
2951                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
2952                     endPlain = find_end_of_layout(document.body, beginPlain)
2953                     argcontent = document.body[beginPlain + 1 : endPlain]
2954                     # Adjust range end
2955                     z = z - len(document.body[arg : argend + 1])
2956                     # Remove arg inset
2957                     del document.body[arg : argend + 1]
2958                     pre += put_cmd_in_ert("<") + argcontent + put_cmd_in_ert(">")
2959                 arg = find_token(document.body, "\\begin_inset Argument 2", i, z)
2960                 if arg != -1:
2961                     argend = find_end_of_inset(document.body, arg)
2962                     if argend == -1:
2963                         document.warning("Can't find end of Argument!")
2964                         i += 1
2965                         continue
2966                     # Find containing paragraph layout
2967                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
2968                     endPlain = find_end_of_layout(document.body, beginPlain)
2969                     argcontent = document.body[beginPlain + 1 : endPlain]
2970                     # Adjust range end
2971                     z = z - len(document.body[arg : argend + 1])
2972                     # Remove arg inset
2973                     del document.body[arg : argend + 1]
2974                     if flextype == "Alternative":
2975                         pre += put_cmd_in_ert("{") + argcontent + put_cmd_in_ert("}")
2976                     else:
2977                         pre += put_cmd_in_ert("[") + argcontent + put_cmd_in_ert("]")
2978                 pre += put_cmd_in_ert("{")
2979                 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2980                 endPlain = find_end_of_layout(document.body, beginPlain)
2981                 # Adjust range end
2982                 z = z - len(document.body[i : beginPlain + 1])
2983                 z += len(pre)
2984                 document.body[i : beginPlain + 1] = pre
2985                 post = put_cmd_in_ert("}")
2986                 document.body[z - 2 : z + 1] = post
2987             elif flextype in old_flexes:
2988                 pre = put_cmd_in_ert(old_flexes[flextype])
2989                 arg = find_token(document.body, "\\begin_inset Argument 1", i, z)
2990                 if arg == -1:
2991                     i += 1
2992                     continue
2993                 argend = find_end_of_inset(document.body, arg)
2994                 if argend == -1:
2995                     document.warning("Can't find end of Argument!")
2996                     i += 1
2997                     continue
2998                 # Find containing paragraph layout
2999                 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3000                 endPlain = find_end_of_layout(document.body, beginPlain)
3001                 argcontent = document.body[beginPlain + 1 : endPlain]
3002                 # Adjust range end
3003                 z = z - len(document.body[arg : argend + 1])
3004                 # Remove arg inset
3005                 del document.body[arg : argend + 1]
3006                 pre += put_cmd_in_ert("<") + argcontent + put_cmd_in_ert(">")
3007                 pre += put_cmd_in_ert("{")
3008                 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
3009                 endPlain = find_end_of_layout(document.body, beginPlain)
3010                 # Adjust range end
3011                 z = z - len(document.body[i : beginPlain + 1])
3012                 z += len(pre)
3013                 document.body[i : beginPlain + 1] = pre
3014                 post = put_cmd_in_ert("}")
3015                 document.body[z - 2 : z + 1] = post
3016         
3017         i += 1
3018
3019
3020 def revert_beamerblocks(document):
3021     " Reverts beamer block arguments to ERT "
3022     
3023     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3024     if document.textclass not in beamer_classes:
3025         return
3026
3027     blocks = ["Block", "ExampleBlock", "AlertBlock"]
3028
3029     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
3030     i = 0
3031     while True:
3032         i = find_token(document.body, "\\begin_inset Argument", i)
3033         if i == -1:
3034             return
3035         # Find containing paragraph layout
3036         parent = get_containing_layout(document.body, i)
3037         if parent == False:
3038             document.warning("Malformed LyX document: Can't find parent paragraph layout")
3039             i += 1
3040             continue
3041         parbeg = parent[1]
3042         parend = parent[2]
3043         realparbeg = parent[3]
3044         layoutname = parent[0]
3045         realparend = parend
3046         for p in range(parbeg, parend):
3047             if p >= realparend:
3048                 i = realparend
3049                 break
3050             if layoutname in blocks:
3051                 m = rx.match(document.body[p])
3052                 if m:
3053                     argnr = m.group(1)
3054                     if argnr == "1":
3055                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
3056                         endPlain = find_end_of_layout(document.body, beginPlain)
3057                         endInset = find_end_of_inset(document.body, p)
3058                         content = document.body[beginPlain + 1 : endPlain]
3059                         # Adjust range end
3060                         realparend = realparend - len(document.body[p : endInset + 1])
3061                         # Remove arg inset
3062                         del document.body[p : endInset + 1]
3063                         subst = put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
3064                         document.body[realparbeg : realparbeg] = subst
3065                     elif argnr == "2":
3066                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
3067                         endPlain = find_end_of_layout(document.body, beginPlain)
3068                         endInset = find_end_of_inset(document.body, p)
3069                         content = document.body[beginPlain + 1 : endPlain]
3070                         # Adjust range end
3071                         realparend = realparend - len(document.body[p : endInset + 1])
3072                         # Remove arg inset
3073                         del document.body[p : endInset + 1]
3074                         subst = put_cmd_in_ert("{") + content + put_cmd_in_ert("}")
3075                         document.body[realparbeg : realparbeg] = subst
3076         i = realparend
3077
3078
3079
3080 def convert_beamerblocks(document):
3081     " Converts beamer block ERT args to native InsetArgs "
3082     
3083     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3084     if document.textclass not in beamer_classes:
3085         return
3086    
3087     blocks = ["Block", "ExampleBlock", "AlertBlock"]
3088     for lay in blocks:
3089         i = 0
3090         while True:
3091             i = find_token_exact(document.body, "\\begin_layout " + lay, i)
3092             if i == -1:
3093                 break
3094             parent = get_containing_layout(document.body, i)
3095             if parent == False or parent[1] != i:
3096                 document.warning("Wrong parent layout!")
3097                 i += 1
3098                 continue
3099             parbeg = parent[3]
3100             parend = parent[2]
3101             j = parend
3102             if i != -1:
3103                 # If the paragraph starts with a language switch, adjust parbeg
3104                 if len(document.body[parbeg]) == 0 and parbeg < parend \
3105                 and document.body[parbeg + 1].startswith("\\lang"):
3106                     parbeg += 2
3107                 if document.body[parbeg] == "\\begin_inset ERT":
3108                     ertcontfirstline = parbeg + 5
3109                     lastertbeg = -1
3110                     lastertend = -1
3111                     while True:
3112                         # Find the last ERT in this paragraph used for arguments
3113                         # (which might also be the first)
3114                         lastertbeg = find_token_backwards(document.body, "\\begin_inset ERT", j)
3115                         if lastertbeg == -1:
3116                             document.warning("Last ERT not found!")
3117                             break
3118                         lastertend = find_end_of_inset(document.body, lastertbeg)
3119                         if lastertend == -1:
3120                             document.warning("End of last ERT not found!")
3121                             break
3122                         # Is this ERT really used for an argument?
3123                         # Note: This will fail when non-argument ERTs actually use brackets
3124                         #       (e.g. \pause{})
3125                         regexp = re.compile(r'.*[>\]\}]', re.IGNORECASE)
3126                         cbracket = find_re(document.body, regexp, lastertbeg, lastertend)
3127                         if cbracket != -1:
3128                             break
3129                         if lastertbeg == parbeg:
3130                             break
3131                         j = lastertbeg - 1
3132                     if lastertbeg == -1 or lastertend == -1:
3133                         break
3134                     ertcontlastline = lastertend - 3
3135                     while True:
3136                         if document.body[ertcontfirstline].lstrip().startswith("<"):
3137                             # This is an overlay specification
3138                             # strip off the <
3139                             document.body[ertcontfirstline] = document.body[ertcontfirstline].lstrip()[1:]
3140                             if document.body[ertcontlastline].rstrip().endswith(">"):
3141                                 # strip off the >
3142                                 document.body[ertcontlastline] = document.body[ertcontlastline].rstrip()[:-1]
3143                                 # Convert to ArgInset
3144                                 document.body[parbeg] = "\\begin_inset Argument 1"
3145                             elif document.body[ertcontlastline].rstrip().endswith("}"):
3146                                 # strip off the }
3147                                 document.body[ertcontlastline] = document.body[ertcontlastline].rstrip()[:-1]
3148                                 # divide the args
3149                                 ertcontdivline = ertcontfirstline
3150                                 tok = document.body[ertcontdivline].find('>{')
3151                                 if tok == -1:
3152                                     regexp = re.compile(r'.*>\{', re.IGNORECASE)
3153                                     ertcontdivline = find_re(document.body, regexp, ertcontfirstline, lastertend)
3154                                     tok = document.body[ertcontdivline].find('>{')
3155                                 if tok != -1:
3156                                     if ertcontfirstline < ertcontlastline:
3157                                         # Multiline ERT. Might contain TeX code.  Embrace in ERT.
3158                                         document.body[ertcontlastline : ertcontlastline + 1] = [
3159                                                                             document.body[ertcontlastline], '\\end_layout', '', '\\end_inset']
3160                                         if ertcontdivline == ertcontfirstline:
3161                                             document.body[ertcontdivline : ertcontdivline + 1] = [document.body[ertcontdivline][:tok],
3162                                                                                 '\\end_layout', '', '\\end_inset', '',
3163                                                                                 '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 2',
3164                                                                                 'status collapsed', '', '\\begin_layout Plain Layout',
3165                                                                                 '\\begin_inset ERT', '', 'status open' '', '\\begin_layout Plain Layout',
3166                                                                                 document.body[ertcontdivline][tok + 2:]]
3167                                         else:
3168                                             document.body[ertcontdivline : ertcontdivline + 1] = [document.body[ertcontdivline][:tok],
3169                                                                                 '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 2',
3170                                                                                 'status collapsed', '', '\\begin_layout Plain Layout',
3171                                                                                 '\\begin_inset ERT', '', 'status open' '', '\\begin_layout Plain Layout',
3172                                                                                 document.body[ertcontdivline][tok + 2:]]
3173                                     else:
3174                                         document.body[ertcontdivline : ertcontdivline + 1] = [document.body[ertcontdivline][:tok],
3175                                                                             '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 2',
3176                                                                             'status collapsed', '', '\\begin_layout Plain Layout',
3177                                                                             document.body[ertcontdivline][tok + 2:]]
3178                                 else:
3179                                     # check if have delimiters in two different ERTs
3180                                     tok = document.body[ertcontdivline].find('>')
3181                                     if tok == -1:
3182                                         regexp = re.compile(r'.*>', re.IGNORECASE)
3183                                         ertcontdivline = find_re(document.body, regexp, ertcontfirstline, lastertend)
3184                                         tok = document.body[ertcontdivline].find('>')
3185                                     if tok != -1:
3186                                         tokk = document.body[ertcontdivline].find('{')
3187                                         if tokk == -1:
3188                                             regexp = re.compile(r'.*\{', re.IGNORECASE)
3189                                             ertcontdivlinetwo = find_re(document.body, regexp, ertcontfirstline, lastertend)
3190                                             tokk = document.body[ertcontdivlinetwo].find('{')
3191                                         if tokk != -1:
3192                                             if ertcontfirstline < ertcontlastline:
3193                                                 # Multiline ERT. Might contain TeX code.  Embrace in ERT.
3194                                                 document.body[ertcontlastline : ertcontlastline + 1] = [
3195                                                                                     document.body[ertcontlastline], '\\end_layout', '', '\\end_inset']
3196                                                 document.body[ertcontdivline : ertcontdivlinetwo + 1] = [document.body[ertcontdivline][:tok],
3197                                                                                     '\\end_layout', '', '\\end_inset', '', '\\end_layout', '', 
3198                                                                                     '\\end_inset', '', '', '\\begin_inset Argument 2',
3199                                                                                     'status collapsed', '', '\\begin_layout Plain Layout',
3200                                                                                     '\\begin_inset ERT', '', 'status open' '', '\\begin_layout Plain Layout',
3201                                                                                     document.body[ertcontdivlinetwo][tokk + 1:]]
3202                                             else:
3203                                                 document.body[ertcontdivline : ertcontdivlinetwo + 1] = [document.body[ertcontdivline][:tok],
3204                                                                                     '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 2',
3205                                                                                     'status collapsed', '', '\\begin_layout Plain Layout',
3206                                                                                     document.body[ertcontdivlinetwo][tokk + 1:]]
3207                                 # Convert to ArgInset
3208                                 if ertcontfirstline < ertcontlastline:
3209                                     # Multiline ERT. Might contain TeX code.  Embrace in ERT.
3210                                     document.body[parbeg : parbeg + 1] = ['\\begin_inset Argument 1',
3211                                                                         'status collapsed', '', '\\begin_layout Plain Layout',
3212                                                                         '\\begin_inset ERT', '']
3213                                 else:
3214                                     document.body[parbeg] = "\\begin_inset Argument 1"
3215                         elif document.body[ertcontfirstline].lstrip().startswith("{"):
3216                             # This is the block title
3217                             if document.body[ertcontlastline].rstrip().endswith("}"):
3218                                 # strip off the braces
3219                                 document.body[ertcontfirstline] = document.body[ertcontfirstline].lstrip()[1:]
3220                                 document.body[ertcontlastline] = document.body[ertcontlastline].rstrip()[:-1]
3221                                 if ertcontfirstline < ertcontlastline:
3222                                     # Multiline ERT. Might contain TeX code.  Embrace in ERT.
3223                                     document.body[parend : parend + 1] = [
3224                                                                         document.body[parend], '\\end_inset', '', '\\end_layout']
3225                                     document.body[parbeg : parbeg + 1] = ['\\begin_inset Argument 2',
3226                                                                         'status collapsed', '', '\\begin_layout Plain Layout',
3227                                                                         '\\begin_inset ERT', '']
3228                                 else:
3229                                     # Convert to ArgInset
3230                                     document.body[parbeg] = "\\begin_inset Argument 2"
3231                             # the overlay argument can also follow the title, so ...
3232                             elif document.body[ertcontlastline].rstrip().endswith(">"):
3233                                 # strip off the {
3234                                 document.body[ertcontfirstline] = document.body[ertcontfirstline].lstrip()[1:]
3235                                 # strip off the >
3236                                 document.body[ertcontlastline] = document.body[ertcontlastline].rstrip()[:-1]
3237                                 # divide the args
3238                                 ertcontdivline = ertcontfirstline
3239                                 tok = document.body[ertcontdivline].find('}<')
3240                                 if tok == -1:
3241                                     regexp = re.compile(r'.*\}<', re.IGNORECASE)
3242                                     ertcontdivline = find_re(document.body, regexp, ertcontfirstline, lastertend)
3243                                     tok = document.body[ertcontdivline].find('}<')
3244                                 if tok != -1:
3245                                     if ertcontfirstline < ertcontlastline:
3246                                         # Multiline ERT. Might contain TeX code.  Embrace in ERT.
3247                                         document.body[ertcontlastline : ertcontlastline + 1] = [
3248                                                                             document.body[ertcontlastline], '\\end_layout', '', '\\end_inset']
3249                                         if ertcontdivline == ertcontfirstline:
3250                                             document.body[ertcontdivline : ertcontdivline + 1] = [document.body[ertcontdivline][:tok],
3251                                                                                 '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 1',
3252                                                                                 'status collapsed', '', '\\begin_layout Plain Layout',
3253                                                                                 '\\begin_inset ERT', '', 'status open' '', '\\begin_layout Plain Layout',
3254                                                                                 document.body[ertcontdivline][tok + 2:]]
3255                                         else:
3256                                             document.body[ertcontdivline : ertcontdivline + 1] = [document.body[ertcontdivline][:tok],
3257                                                                                 '\\end_layout', '', '\\end_inset', '',
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', '', '', '\\begin_inset Argument 1',
3265                                                                             'status collapsed', '', '\\begin_layout Plain Layout',
3266                                                                             document.body[ertcontdivline][tok + 2:]]
3267                                 else:
3268                                     # check if have delimiters in two different ERTs
3269                                     tok = document.body[ertcontdivline].find('}')
3270                                     if tok == -1:
3271                                         regexp = re.compile(r'.*\}', re.IGNORECASE)
3272                                         ertcontdivline = find_re(document.body, regexp, ertcontfirstline, lastertend)
3273                                         tok = document.body[ertcontdivline].find('}')
3274                                         if tok != -1:
3275                                             tokk = document.body[ertcontdivline].find('<')
3276                                             if tokk == -1:
3277                                                 regexp = re.compile(r'.*<', re.IGNORECASE)
3278                                                 ertcontdivlinetwo = find_re(document.body, regexp, ertcontfirstline, lastertend)
3279                                                 tokk = document.body[ertcontdivlinetwo].find('<')
3280                                                 if tokk != -1:
3281                                                     if ertcontfirstline < ertcontlastline:
3282                                                         # Multiline ERT. Might contain TeX code.  Embrace in ERT.
3283                                                         document.body[ertcontlastline : ertcontlastline + 1] = [
3284                                                                                             document.body[ertcontlastline], '\\end_layout', '', '\\end_inset']
3285                                                         document.body[ertcontdivline : ertcontdivlinetwo + 1] = [document.body[ertcontdivline][:tok],
3286                                                                                             '\\end_layout', '', '\\end_inset', '', '\\end_layout', '', 
3287                                                                                             '\\end_inset', '', '', '\\begin_inset Argument 1',
3288                                                                                             'status collapsed', '', '\\begin_layout Plain Layout',
3289                                                                                             '\\begin_inset ERT', '', 'status open' '', '\\begin_layout Plain Layout',
3290                                                                                             document.body[ertcontdivlinetwo][tokk + 1:]]
3291                                                     else:
3292                                                         document.body[ertcontdivline : ertcontdivlinetwo + 1] = [document.body[ertcontdivline][:tok],
3293                                                                                             '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 1',
3294                                                                                             'status collapsed', '', '\\begin_layout Plain Layout',
3295                                                                                             document.body[ertcontdivlinetwo][tokk + 1:]]
3296                                 # Convert to ArgInset
3297                                 if ertcontfirstline < ertcontlastline:
3298                                     # Multiline ERT. Might contain TeX code.  Embrace in ERT.
3299                                     document.body[parbeg : parbeg + 1] = ['\\begin_inset Argument 2',
3300                                                                         'status collapsed', '', '\\begin_layout Plain Layout',
3301                                                                         '\\begin_inset ERT', '']
3302                                 else:
3303                                     document.body[parbeg] = "\\begin_inset Argument 2"
3304                             elif count_pars_in_inset(document.body, ertcontfirstline) > 1:
3305                                 # Multipar ERT. Skip this.
3306                                 break
3307                             else:
3308                                 # ERT has contents after the closing bracket. We cannot convert this.
3309                                 # convert_TeX_brace_to_Argument cannot either.
3310                                 #convert_TeX_brace_to_Argument(document, i, 2, 2, False, True, False)
3311                                 break
3312                         else:
3313                             break
3314                         j = find_end_of_layout(document.body, i)
3315                         if j == -1:
3316                             document.warning("end of layout not found!")
3317                         k = find_token(document.body, "\\begin_inset Argument", i, j)
3318                         if k == -1:
3319                             document.warning("InsetArgument not found!")
3320                             break
3321                         l = find_end_of_inset(document.body, k)
3322                         m = find_token(document.body, "\\begin_inset ERT", l, j)
3323                         if m == -1:
3324                             break
3325                         ertcontfirstline = m + 5
3326                         parbeg = m
3327             i = j
3328
3329
3330 def convert_overprint(document):
3331     " Convert old beamer overprint layouts to ERT "
3332     
3333     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3334     if document.textclass not in beamer_classes:
3335         return
3336
3337     i = 0
3338     while True:
3339         i = find_token(document.body, "\\begin_layout Overprint", i)
3340         if i == -1:
3341             return
3342         # Find end of sequence
3343         j = find_end_of_sequence(document.body, i)
3344         if j == -1:
3345             document.warning("Malformed LyX document. Cannot find end of Overprint sequence!")
3346             i += 1
3347             continue
3348         endseq = j
3349         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\begin{overprint}")
3350         esubst = list()
3351         if document.body[j] == "\\end_deeper":
3352             esubst = ["", "\\begin_layout Standard"] + put_cmd_in_ert("\\end{overprint}") + ["\\end_layout"]
3353         else:
3354             esubst = ["\\end_layout", "", "\\begin_layout Standard"] + put_cmd_in_ert("\\end{overprint}") + ["\\end_layout"]
3355         endseq = endseq + len(esubst) - len(document.body[j : j])
3356         document.body[j : j] = esubst
3357         argbeg = find_token(document.body, "\\begin_inset Argument 1", i, j)
3358         if argbeg != -1:
3359             argend = find_end_of_layout(document.body, argbeg)
3360             if argend == -1:
3361                 document.warning("Malformed LyX document. Cannot find end of Overprint argument!")
3362                 i += 1
3363                 continue
3364             beginPlain = find_token(document.body, "\\begin_layout Plain Layout", argbeg)
3365             endPlain = find_end_of_layout(document.body, beginPlain)
3366             content = document.body[beginPlain + 1 : endPlain]
3367             # Adjust range end
3368             endseq = endseq - len(document.body[argbeg : argend + 1])
3369             # Remove arg inset
3370             del document.body[argbeg : argend + 1]
3371             subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
3372             
3373         endseq = endseq - len(document.body[i : i])
3374         document.body[i : i] = subst + ["\\end_layout"]
3375         endseq += len(subst)
3376         
3377         for p in range(i, endseq):
3378             if document.body[p] == "\\begin_layout Overprint":
3379                 document.body[p] = "\\begin_layout Standard"
3380
3381         i = endseq
3382
3383
3384 def revert_overprint(document):
3385     " Revert old beamer overprint layouts to ERT "
3386     
3387     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3388     if document.textclass not in beamer_classes:
3389         return
3390
3391     i = 0
3392     while True:
3393         i = find_token(document.body, "\\begin_layout Overprint", i)
3394         if i == -1:
3395             return
3396         # Find end of sequence
3397         j = find_end_of_sequence(document.body, i)
3398         if j == -1:
3399             document.warning("Malformed LyX document. Cannot find end of Overprint sequence!")
3400             i += 1
3401             continue
3402         endseq = j
3403         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\begin{overprint}")
3404         esubst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\end{overprint}")
3405         endseq = endseq + len(esubst) - len(document.body[j : j])
3406         if document.body[j] == "\\end_deeper":
3407             document.body[j : j] = [""] + esubst + ["", "\\end_layout"]
3408         else:
3409             document.body[j : j] = ["\\end_layout", ""] + esubst
3410         r = i
3411         while r < j:
3412             if document.body[r] == "\\begin_deeper":
3413                 s = find_end_of(document.body, r, "\\begin_deeper", "\\end_deeper")
3414                 if s != -1:
3415                     document.body[r] = ""
3416                     document.body[s] = ""
3417                     r = s
3418                     continue
3419             r = r + 1
3420         argbeg = find_token(document.body, "\\begin_inset Argument 1", i, j)
3421         if argbeg != -1:
3422             # Is this really our argument?
3423             nested = find_token(document.body, "\\begin_deeper", i, argbeg)
3424             if nested != -1:
3425                 argend = find_end_of_inset(document.body, argbeg)
3426                 if argend == -1:
3427                     document.warning("Malformed LyX document. Cannot find end of Overprint argument!")
3428                     i += 1
3429                     continue
3430                 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", argbeg)
3431                 endPlain = find_end_of_layout(document.body, beginPlain)
3432                 content = document.body[beginPlain + 1 : endPlain]
3433                 # Adjust range end
3434                 endseq = endseq - len(document.body[argbeg : argend])
3435                 # Remove arg inset
3436                 del document.body[argbeg : argend + 1]
3437                 subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
3438             
3439         endseq = endseq - len(document.body[i : i])
3440         document.body[i : i] = subst + ["\\end_layout"]
3441         endseq += len(subst)
3442      
3443         p = i
3444         while True:
3445             if p >= endseq:
3446                 break
3447             if document.body[p] == "\\begin_layout Overprint":
3448                 q = find_end_of_layout(document.body, p)
3449                 if q == -1:
3450                     document.warning("Malformed LyX document. Cannot find end of Overprint layout!")
3451                     p += 1
3452                     continue
3453                 subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\onslide")
3454                 argbeg = find_token(document.body, "\\begin_inset Argument item:1", p, q)
3455                 if argbeg != -1:
3456                     argend = find_end_of_inset(document.body, argbeg)
3457                     if argend == -1:
3458                         document.warning("Malformed LyX document. Cannot find end of Overprint item argument!")
3459                         p += 1
3460                         continue
3461                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", argbeg)
3462                     endPlain = find_end_of_layout(document.body, beginPlain)
3463                     content = document.body[beginPlain + 1 : endPlain]
3464                     # Adjust range end
3465                     endseq = endseq - len(document.body[argbeg : argend + 1])
3466                     # Remove arg inset
3467                     del document.body[argbeg : argend + 1]
3468                     subst += put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
3469                 endseq = endseq - len(document.body[p : p + 1]) + len(subst)
3470                 document.body[p : p + 1] = subst
3471             p = p + 1
3472
3473         i = endseq
3474
3475
3476 def revert_frametitle(document):
3477     " Reverts beamer frametitle layout to ERT "
3478     
3479     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3480     if document.textclass not in beamer_classes:
3481         return
3482
3483     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
3484     i = 0
3485     while True:
3486         i = find_token(document.body, "\\begin_layout FrameTitle", i)
3487         if i == -1:
3488             return
3489         j = find_end_of_layout(document.body, i)
3490         if j == -1:
3491             document.warning("Malformed LyX document: Can't find end of FrameTitle layout")
3492             i += 1
3493             continue
3494         endlay = j
3495         document.body[j : j] = put_cmd_in_ert("}") + document.body[j : j]
3496         endlay += len(put_cmd_in_ert("}"))
3497         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\frametitle")
3498         for p in range(i, j):
3499             if p >= endlay:
3500                 break
3501             m = rx.match(document.body[p])
3502             if m:
3503                 argnr = m.group(1)
3504                 if argnr == "1":
3505                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
3506                     endPlain = find_end_of_layout(document.body, beginPlain)
3507                     endInset = find_end_of_inset(document.body, p)
3508                     content = document.body[beginPlain + 1 : endPlain]
3509                     # Adjust range end
3510                     endlay = endlay - len(document.body[p : endInset + 1])
3511                     # Remove arg inset
3512                     del document.body[p : endInset + 1]
3513                     subst += put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
3514                 elif argnr == "2":
3515                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
3516                     endPlain = find_end_of_layout(document.body, beginPlain)
3517                     endInset = find_end_of_inset(document.body, p)
3518                     content = document.body[beginPlain + 1 : endPlain]
3519                     # Adjust range end
3520                     endlay = endlay - len(document.body[p : endInset + 1])
3521                     # Remove arg inset
3522                     del document.body[p : endInset + 1]
3523                     subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
3524                     
3525         subst += put_cmd_in_ert("{")
3526         document.body[i : i + 1] = subst
3527         i = endlay
3528
3529
3530 def convert_epigraph(document):
3531     " Converts memoir epigraph to new syntax "
3532     
3533     if document.textclass != "memoir":
3534         return
3535
3536     i = 0
3537     while True:
3538         i = find_token(document.body, "\\begin_layout Epigraph", i)
3539         if i == -1:
3540             return
3541         j = find_end_of_layout(document.body, i)
3542         if j == -1:
3543             document.warning("Malformed LyX document: Can't find end of Epigraph layout")
3544             i += 1
3545             continue
3546         endlay = j
3547         subst = list()
3548         ert = find_token(document.body, "\\begin_inset ERT", i, j)
3549         if ert != -1:
3550             endInset = find_end_of_inset(document.body, ert)
3551             beginPlain = find_token(document.body, "\\begin_layout Plain Layout", ert)
3552             endPlain = find_end_of_layout(document.body, beginPlain)
3553             ertcont = beginPlain + 2
3554             if document.body[ertcont] == "}{":
3555                 # strip off the <
3556                 # Convert to ArgInset
3557                 endlay = endlay - 2 * len(document.body[j])
3558                 begsubst = ['\\begin_inset Argument post:1', 'status collapsed', '',
3559                             '\\begin_layout Plain Layout']
3560                 endsubst = ['\\end_layout', '', '\\end_inset', '', document.body[j]]
3561                 document.body[j : j + 1] = endsubst
3562                 document.body[endInset + 1 : endInset + 1] = begsubst
3563                 # Adjust range end
3564                 endlay += len(begsubst) + len(endsubst)
3565                 endlay = endlay - len(document.body[ert : endInset + 1])
3566                 del document.body[ert : endInset + 1]
3567                     
3568         i = endlay
3569
3570
3571 def revert_epigraph(document):
3572     " Reverts memoir epigraph argument to ERT "
3573     
3574     if document.textclass != "memoir":
3575         return
3576
3577     i = 0
3578     while True:
3579         i = find_token(document.body, "\\begin_layout Epigraph", i)
3580         if i == -1:
3581             return
3582         j = find_end_of_layout(document.body, i)
3583         if j == -1:
3584             document.warning("Malformed LyX document: Can't find end of Epigraph layout")
3585             i += 1
3586             continue
3587         endlay = j
3588         subst = list()
3589         p = find_token(document.body, "\\begin_layout Argument post:1", i, j)
3590         if p != -1:
3591             beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
3592             endPlain = find_end_of_layout(document.body, beginPlain)
3593             endInset = find_end_of_inset(document.body, p)
3594             content = document.body[beginPlain + 1 : endPlain]
3595             # Adjust range end
3596             endlay = endlay - len(document.body[p : endInset + 1])
3597             # Remove arg inset
3598             del document.body[p : endInset + 1]
3599             subst += put_cmd_in_ert("}{") + content
3600         else:
3601             subst += put_cmd_in_ert("}{")
3602                     
3603         document.body[j : j] = subst + document.body[j : j]
3604         i = endlay
3605
3606
3607 def convert_captioninsets(document):
3608     " Converts caption insets to new syntax "
3609     
3610     i = 0
3611     while True:
3612       i = find_token(document.body, "\\begin_inset Caption", i)
3613       if i == -1:
3614           return
3615       document.body[i] = "\\begin_inset Caption Standard"
3616       i += 1
3617
3618
3619 def revert_captioninsets(document):
3620     " Reverts caption insets to old syntax "
3621     
3622     i = 0
3623     while True:
3624       i = find_token(document.body, "\\begin_inset Caption Standard", i)
3625       if i == -1:
3626           return
3627       document.body[i] = "\\begin_inset Caption"
3628       i += 1
3629
3630
3631 def convert_captionlayouts(document):
3632     " Convert caption layouts to caption insets. "
3633
3634     caption_dict = {
3635         "Captionabove":  "Above",
3636         "Captionbelow":  "Below",
3637         "FigCaption"  :  "FigCaption",
3638         "Table_Caption" :  "Table",
3639         "CenteredCaption" : "Centered",
3640         "Bicaption" : "Bicaption",
3641         }
3642
3643     i = 0
3644     while True:
3645         i = find_token(document.body, "\\begin_layout", i)
3646         if i == -1:
3647             return
3648         val = get_value(document.body, "\\begin_layout", i)
3649         if val in list(caption_dict.keys()):
3650             j = find_end_of_layout(document.body, i)
3651             if j == -1:
3652                 document.warning("Malformed LyX document: Missing `\\end_layout'.")
3653                 return
3654
3655             document.body[j:j] = ["\\end_layout", "", "\\end_inset", "", ""]
3656             document.body[i:i+1] = ["\\begin_layout %s" % document.default_layout,
3657                                     "\\begin_inset Caption %s" % caption_dict[val], "",
3658                                     "\\begin_layout %s" % document.default_layout]
3659         i += 1
3660
3661
3662 def revert_captionlayouts(document):
3663     " Revert caption insets to caption layouts. "
3664     
3665     caption_dict = {
3666         "Above" : "Captionabove",
3667         "Below" : "Captionbelow",
3668         "FigCaption"  :  "FigCaption",
3669         "Table" : "Table_Caption",
3670         "Centered" : "CenteredCaption",
3671         "Bicaption" : "Bicaption",
3672         }
3673     
3674     i = 0
3675     rx = re.compile(r'^\\begin_inset Caption (\S+)$')
3676     while True:
3677         i = find_token(document.body, "\\begin_inset Caption", i)
3678         if i == -1:
3679             return
3680
3681         m = rx.match(document.body[i])
3682         val = ""
3683         if m:
3684             val = m.group(1)
3685         if val not in list(caption_dict.keys()):
3686             i += 1
3687             continue
3688         
3689         # We either need to delete the previous \begin_layout line, or we
3690         # need to end the previous layout if this inset is not in the first
3691         # position of the paragraph.
3692         layout_before = find_token_backwards(document.body, "\\begin_layout", i)
3693         if layout_before == -1:
3694             document.warning("Malformed LyX document: Missing `\\begin_layout'.")
3695             return
3696         layout_line = document.body[layout_before]
3697         del_layout_before = True
3698         l = layout_before + 1
3699         while l < i:
3700             if document.body[l] != "":
3701                 del_layout_before = False
3702                 break
3703             l = l + 1
3704         if del_layout_before:
3705             del document.body[layout_before:i]
3706             i = layout_before
3707         else:
3708             document.body[i:i] = ["\\end_layout", ""]
3709             i = i + 2
3710
3711         # Find start of layout in the inset and end of inset
3712         j = find_token(document.body, "\\begin_layout", i)
3713         if j == -1:
3714             document.warning("Malformed LyX document: Missing `\\begin_layout'.")
3715             return
3716         k = find_end_of_inset(document.body, i)
3717         if k == -1:
3718             document.warning("Malformed LyX document: Missing `\\end_inset'.")
3719             return
3720
3721         # We either need to delete the following \end_layout line, or we need
3722         # to restart the old layout if this inset is not at the paragraph end.
3723         layout_after = find_token(document.body, "\\end_layout", k)
3724         if layout_after == -1:
3725             document.warning("Malformed LyX document: Missing `\\end_layout'.")
3726             return
3727         del_layout_after = True
3728         l = k + 1
3729         while l < layout_after:
3730             if document.body[l] != "":
3731                 del_layout_after = False
3732                 break
3733             l = l + 1
3734         if del_layout_after:
3735             del document.body[k+1:layout_after+1]
3736         else:
3737             document.body[k+1:k+1] = [layout_line, ""]
3738
3739         # delete \begin_layout and \end_inset and replace \begin_inset with
3740         # "\begin_layout XXX". This works because we can only have one
3741         # paragraph in the caption inset: The old \end_layout will be recycled.
3742         del document.body[k]
3743         if document.body[k] == "":
3744             del document.body[k]
3745         del document.body[j]
3746         if document.body[j] == "":
3747             del document.body[j]
3748         document.body[i] = "\\begin_layout %s" % caption_dict[val]
3749         if document.body[i+1] == "":
3750             del document.body[i+1]
3751         i += 1
3752
3753
3754 def revert_fragileframe(document):
3755     " Reverts beamer FragileFrame layout to ERT "
3756     
3757     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3758     if document.textclass not in beamer_classes:
3759         return
3760
3761     i = 0
3762     while True:
3763         i = find_token(document.body, "\\begin_layout FragileFrame", i)
3764         if i == -1:
3765             return
3766         # Find end of sequence
3767         j = find_end_of_sequence(document.body, i)
3768         if j == -1:
3769             document.warning("Malformed LyX document. Cannot find end of FragileFrame sequence!")
3770             i += 1
3771             continue
3772         endseq = j
3773         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\begin{frame}")
3774         esubst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\end{frame}")
3775         endseq = endseq + len(esubst) - len(document.body[j : j])
3776         if document.body[j] == "\\end_deeper":
3777             document.body[j : j] = [""] + esubst + ["", "\\end_layout"]
3778         else:
3779             document.body[j : j] = esubst
3780         for q in range(i, j):
3781             if document.body[q] == "\\begin_layout FragileFrame":
3782                 document.body[q] = "\\begin_layout %s" % document.default_layout
3783         r = i
3784         while r < j:
3785             if document.body[r] == "\\begin_deeper":
3786                 s = find_end_of(document.body, r, "\\begin_deeper", "\\end_deeper")
3787                 if s != -1:
3788                     document.body[r] = ""
3789                     document.body[s] = ""
3790                     r = s
3791                     continue
3792             r = r + 1
3793         for p in range(1, 5):
3794             arg = find_token(document.body, "\\begin_inset Argument %d" % p, i, j)
3795             if arg != -1:
3796                 if p == 1:
3797                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3798                     endPlain = find_end_of_layout(document.body, beginPlain)
3799                     endInset = find_end_of_inset(document.body, arg)
3800                     content = document.body[beginPlain + 1 : endPlain]
3801                     # Adjust range end
3802                     j = j - len(document.body[arg : endInset + 1])
3803                     # Remove arg inset
3804                     del document.body[arg : endInset + 1]
3805                     subst += put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
3806                 elif p == 2:
3807                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3808                     endPlain = find_end_of_layout(document.body, beginPlain)
3809                     endInset = find_end_of_inset(document.body, arg)
3810                     content = document.body[beginPlain + 1 : endPlain]
3811                     # Adjust range end
3812                     j = j - len(document.body[arg : endInset + 1])
3813                     # Remove arg inset
3814                     del document.body[arg : endInset + 1]
3815                     subst += put_cmd_in_ert("[<") + content + put_cmd_in_ert(">]")
3816                 elif p == 3:
3817                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3818                     endPlain = find_end_of_layout(document.body, beginPlain)
3819                     endInset = find_end_of_inset(document.body, arg)
3820                     content = document.body[beginPlain + 1 : endPlain]
3821                     # Adjust range end
3822                     j = j - len(document.body[arg : endInset + 1])
3823                     # Remove arg inset
3824                     del document.body[arg : endInset + 1]
3825                     subst += put_cmd_in_ert("[fragile,") + content + put_cmd_in_ert("]")
3826                 elif p == 4:
3827                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3828                     endPlain = find_end_of_layout(document.body, beginPlain)
3829                     endInset = find_end_of_inset(document.body, arg)
3830                     content = document.body[beginPlain + 1 : endPlain]
3831                     # Adjust range end
3832                     j = j - len(document.body[arg : endInset + 1])
3833                     # Remove arg inset
3834                     del document.body[arg : endInset + 1]
3835                     subst += put_cmd_in_ert("{") + content + put_cmd_in_ert("}")
3836             elif p == 3:
3837                 subst += put_cmd_in_ert("[fragile]")
3838                     
3839         document.body[i : i + 1] = subst
3840         i = j
3841
3842
3843 def revert_newframes(document):
3844     " Reverts beamer Frame and PlainFrame layouts to old forms "
3845     
3846     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3847     if document.textclass not in beamer_classes:
3848         return
3849
3850     frame_dict = {
3851         "Frame" : "BeginFrame",
3852         "PlainFrame" : "BeginPlainFrame",
3853         }
3854
3855     rx = re.compile(r'^\\begin_layout (\S+)$')
3856     i = 0
3857     while True:
3858         i = find_token(document.body, "\\begin_layout", i)
3859         if i == -1:
3860             return
3861
3862         m = rx.match(document.body[i])
3863         val = ""
3864         if m:
3865             val = m.group(1)
3866         if val not in list(frame_dict.keys()):
3867             i += 1
3868             continue
3869         # Find end of sequence
3870         j = find_end_of_sequence(document.body, i)
3871         if j == -1:
3872             document.warning("Malformed LyX document. Cannot find end of Frame sequence!")
3873             i += 1
3874             continue
3875         endseq = j
3876         subst = ["\\begin_layout %s" % frame_dict[val]]
3877         esubst = ["", "\\begin_layout EndFrame", "", "\\end_layout"]
3878         endseq = endseq + len(esubst) - len(document.body[j : j])
3879         if document.body[j] == "\\end_deeper":
3880             document.body[j : j] = esubst
3881         else:
3882             document.body[j+1 : j+1] = esubst
3883         for q in range(i, j):
3884             if document.body[q] == "\\begin_layout %s" % val:
3885                 document.body[q] = "\\begin_layout %s" % document.default_layout
3886         r = i
3887         while r < j:
3888             if document.body[r] == "\\begin_deeper":
3889                 s = find_end_of(document.body, r, "\\begin_deeper", "\\end_deeper")
3890                 if s != -1:
3891                     document.body[r] = ""
3892                     document.body[s] = ""
3893                     r = s
3894                     continue
3895             r = r + 1
3896         l = find_end_of_layout(document.body, i)
3897         for p in range(1, 5):
3898             arg = find_token(document.body, "\\begin_inset Argument %d" % p, i, l)
3899             if arg != -1:
3900                 if p == 1:
3901                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3902                     endPlain = find_end_of_layout(document.body, beginPlain)
3903                     endInset = find_end_of_inset(document.body, arg)
3904                     content = document.body[beginPlain + 1 : endPlain]
3905                     # Adjust range end
3906                     l = l - len(document.body[arg : endInset + 1])
3907                     # Remove arg inset
3908                     del document.body[arg : endInset + 1]
3909                     subst += put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
3910                 elif p == 2:
3911                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3912                     endPlain = find_end_of_layout(document.body, beginPlain)
3913                     endInset = find_end_of_inset(document.body, arg)
3914                     content = document.body[beginPlain + 1 : endPlain]
3915                     # Adjust range end
3916                     l = l - len(document.body[arg : endInset + 1])
3917                     # Remove arg inset
3918                     del document.body[arg : endInset + 1]
3919                     subst += put_cmd_in_ert("[<") + content + put_cmd_in_ert(">]")
3920                 elif p == 3:
3921                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3922                     endPlain = find_end_of_layout(document.body, beginPlain)
3923                     endInset = find_end_of_inset(document.body, arg)
3924                     content = document.body[beginPlain + 1 : endPlain]
3925                     # Adjust range end
3926                     l = l - len(document.body[arg : endInset + 1])
3927                     # Remove arg inset
3928                     del document.body[arg : endInset + 1]
3929                     subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
3930                 elif p == 4:
3931                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3932                     endPlain = find_end_of_layout(document.body, beginPlain)
3933                     endInset = find_end_of_inset(document.body, arg)
3934                     content = document.body[beginPlain + 1 : endPlain]
3935                     # Adjust range end
3936                     l = l - len(document.body[arg : endInset + 1])
3937                     # Remove arg inset
3938                     del document.body[arg : endInset + 1]
3939                     subst += content
3940                     
3941         document.body[i : i + 1] = subst
3942         i = j
3943
3944 # known encodings that do not change their names (same LyX and LaTeX names)
3945 known_enc_tuple = ("auto", "default", "ansinew", "applemac", "armscii8", "ascii",
3946     "cp437", "cp437de", "cp850", "cp852", "cp855", "cp858", "cp862", "cp865", "cp866",
3947     "cp1250", "cp1251", "cp1252", "cp1255", "cp1256", "cp1257", "koi8-r", "koi8-u",
3948     "pt154", "pt254", "tis620-0", "utf8", "utf8x", "utf8-plain")
3949
3950 def convert_encodings(document):
3951     "Use the LyX names of the encodings instead of the LaTeX names."
3952     LaTeX2LyX_enc_dict = {
3953         "8859-6":     "iso8859-6",
3954         "8859-8":     "iso8859-8",
3955         "Bg5":        "big5",
3956         "euc":        "euc-jp-platex",
3957         "EUC-JP":     "euc-jp",
3958         "EUC-TW":     "euc-tw",
3959         "GB":         "euc-cn",
3960         "GBK":        "gbk",
3961         "iso88595":   "iso8859-5",
3962         "iso-8859-7": "iso8859-7",
3963         "JIS":        "jis",
3964         "jis":        "jis-platex",
3965         "KS":         "euc-kr",
3966         "l7xenc":     "iso8859-13",
3967         "latin1":     "iso8859-1",
3968         "latin2":     "iso8859-2",
3969         "latin3":     "iso8859-3",
3970         "latin4":     "iso8859-4",
3971         "latin5":     "iso8859-9",
3972         "latin9":     "iso8859-15",
3973         "latin10":    "iso8859-16",
3974         "SJIS":       "shift-jis",
3975         "sjis":       "shift-jis-platex",
3976         "UTF8":       "utf8-cjk"
3977     }
3978     i = find_token(document.header, "\\inputencoding" , 0)
3979     if i == -1:
3980         return
3981     val = get_value(document.header, "\\inputencoding", i)
3982     if val in list(LaTeX2LyX_enc_dict.keys()):
3983         document.header[i] = "\\inputencoding %s" % LaTeX2LyX_enc_dict[val]
3984     elif val not in known_enc_tuple:
3985         document.warning("Ignoring unknown input encoding: `%s'" % val)
3986
3987
3988 def revert_encodings(document):
3989     """Revert to using the LaTeX names of the encodings instead of the LyX names.
3990     Also revert utf8-platex to sjis, the language default when using Japanese.
3991     """
3992     LyX2LaTeX_enc_dict = {
3993         "big5":             "Bg5",
3994         "euc-cn":           "GB",
3995         "euc-kr":           "KS",
3996         "euc-jp":           "EUC-JP",
3997         "euc-jp-platex":    "euc",
3998         "euc-tw":           "EUC-TW",
3999         "gbk":              "GBK",
4000         "iso8859-1":        "latin1",
4001         "iso8859-2":        "latin2",
4002         "iso8859-3":        "latin3",
4003         "iso8859-4":        "latin4",
4004         "iso8859-5":        "iso88595",
4005         "iso8859-6":        "8859-6",
4006         "iso8859-7":        "iso-8859-7",
4007         "iso8859-8":        "8859-8",
4008         "iso8859-9":        "latin5",
4009         "iso8859-13":       "l7xenc",
4010         "iso8859-15":       "latin9",
4011         "iso8859-16":       "latin10",
4012         "jis":              "JIS",
4013         "jis-platex":       "jis",
4014         "shift-jis":        "SJIS",
4015         "shift-jis-platex": "sjis",
4016         "utf8-cjk":         "UTF8",
4017         "utf8-platex":      "sjis"
4018     }
4019     i = find_token(document.header, "\\inputencoding" , 0)
4020     if i == -1:
4021         return
4022     val = get_value(document.header, "\\inputencoding", i)
4023     if val in list(LyX2LaTeX_enc_dict.keys()):
4024         document.header[i] = "\\inputencoding %s" % LyX2LaTeX_enc_dict[val]
4025     elif val not in known_enc_tuple:
4026         document.warning("Ignoring unknown input encoding: `%s'" % val)
4027
4028
4029 def revert_IEEEtran_3(document):
4030   '''
4031   Reverts Flex Insets to TeX-code
4032   '''
4033   if document.textclass == "IEEEtran":
4034     h = 0
4035     i = 0
4036     j = 0
4037     while True:
4038       if h != -1:
4039         h = find_token(document.body, "\\begin_inset Flex Author Mark", h)
4040       if h != -1:
4041         endh = find_end_of_inset(document.body, h)
4042         document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
4043         document.body[h : h + 4] = put_cmd_in_ert("\\IEEEauthorrefmark{")
4044         h = h + 5
4045       if i != -1:
4046         i = find_token(document.body, "\\begin_inset Flex Author Name", i)
4047       if i != -1:
4048         endi = find_end_of_inset(document.body, i)
4049         document.body[endi - 2 : endi + 1] = put_cmd_in_ert("}")
4050         document.body[i : i + 4] = put_cmd_in_ert("\\IEEEauthorblockN{")
4051         i = i + 5
4052       if j != -1:
4053         j = find_token(document.body, "\\begin_inset Flex Author Affiliation", j)
4054       if j != -1:
4055         endj = find_end_of_inset(document.body, j)
4056         document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
4057         document.body[j : j + 4] = put_cmd_in_ert("\\IEEEauthorblockA{")
4058         j = j + 5
4059       if i == -1 and j == -1 and h == -1:
4060         return
4061
4062
4063 def revert_kurier_fonts(document):
4064   " Revert kurier font definition to LaTeX "
4065   
4066   i = find_token(document.header, "\\font_math", 0)
4067   if i != -1:
4068     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
4069       val = get_value(document.header, "\\font_math", i)
4070       if val == "kurier-math":
4071         add_to_preamble(document, "\\let\\Myrmdefault\\rmdefault\n" \
4072           "\\usepackage[math]{kurier}\n" \
4073           "\\renewcommand{\\rmdefault}{\\Myrmdefault}")
4074         document.header[i] = "\\font_math auto"
4075   
4076   if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
4077     kurier_fonts = ["kurier", "kurierc", "kurierl", "kurierlc"]
4078     k = find_token(document.header, "\\font_sans kurier", 0)
4079     if k != -1:
4080       sf = get_value(document.header, "\\font_sans", k)
4081       if sf in kurier_fonts:
4082         add_to_preamble(document, "\\renewcommand{\\sfdefault}{%s}" % sf)
4083         document.header[k] = "\\font_sans default"
4084
4085 def revert_iwona_fonts(document):
4086   " Revert iwona font definition to LaTeX "
4087   
4088   i = find_token(document.header, "\\font_math", 0)
4089   if i != -1:
4090     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
4091       val = get_value(document.header, "\\font_math", i)
4092       if val == "iwona-math":
4093         add_to_preamble(document, "\\let\\Myrmdefault\\rmdefault\n" \
4094           "\\usepackage[math]{iwona}\n" \
4095           "\\renewcommand{\\rmdefault}{\\Myrmdefault}")
4096         document.header[i] = "\\font_math auto"
4097   
4098   if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
4099     iwona_fonts = ["iwona", "iwonac", "iwonal", "iwonalc"]
4100     k = find_token(document.header, "\\font_sans iwona", 0)
4101     if k != -1:
4102       sf = get_value(document.header, "\\font_sans", k)
4103       if sf in iwona_fonts:
4104         add_to_preamble(document, "\\renewcommand{\\sfdefault}{%s}" % sf)
4105         document.header[k] = "\\font_sans default"
4106
4107
4108 def revert_new_libertines(document):
4109     " Revert new libertine font definition to LaTeX "
4110   
4111     if find_token(document.header, "\\use_non_tex_fonts true", 0) != -1:
4112         return
4113
4114     i = find_token(document.header, "\\font_typewriter libertine-mono", 0)
4115     if i != -1:
4116         preamble = "\\usepackage"
4117         sc = find_token(document.header, "\\font_tt_scale", 0)
4118         if sc != -1:
4119             scval = get_value(document.header, "\\font_tt_scale", sc)
4120             if scval != "100":
4121                 preamble += "[scale=%f]" % (float(scval) / 100)
4122                 document.header[sc] = "\\font_tt_scale 100"
4123         preamble += "{libertineMono-type1}"
4124         add_to_preamble(document, [preamble])
4125         document.header[i] = "\\font_typewriter default"
4126    
4127     k = find_token(document.header, "\\font_sans biolinum", 0)
4128     if k != -1:
4129         preamble = "\\usepackage"
4130         options = ""
4131         j = find_token(document.header, "\\font_osf true", 0)
4132         if j != -1:
4133             options += "osf"
4134         else:
4135             options += "lining"
4136         sc = find_token(document.header, "\\font_sf_scale", 0)
4137         if sc != -1:
4138             scval = get_value(document.header, "\\font_sf_scale", sc)
4139             if scval != "100":
4140                 options += ",scale=%f" % (float(scval) / 100)
4141                 document.header[sc] = "\\font_sf_scale 100"
4142         if options != "":
4143             preamble += "[" + options +"]"
4144         preamble += "{biolinum-type1}"
4145         add_to_preamble(document, [preamble])
4146         document.header[k] = "\\font_sans default"
4147
4148
4149 def convert_lyxframes(document):
4150     " Converts old beamer frames to new style "
4151     
4152     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
4153     if document.textclass not in beamer_classes:
4154         return
4155    
4156     framebeg = ["BeginFrame", "BeginPlainFrame"]
4157     frameend = ["Frame", "PlainFrame", "EndFrame", "BeginFrame", "BeginPlainFrame", "AgainFrame",
4158                 "Section", "Section*", "Subsection", "Subsection*", "Subsubsection", "Subsubsection*"]
4159     for lay in framebeg:
4160         i = 0
4161         while True:
4162             i = find_token_exact(document.body, "\\begin_layout " + lay, i)
4163             if i == -1:
4164                 break
4165             parent = get_containing_layout(document.body, i)
4166             if parent == False or parent[1] != i:
4167                 document.warning("Wrong parent layout!")
4168                 i += 1
4169                 continue
4170             frametype = parent[0]
4171             j = parent[2]
4172             parbeg = parent[3]
4173             if i != -1:
4174                 # Step I: Convert ERT arguments
4175                 # FIXME: See restrictions in convert_beamerframeargs method
4176                 ertend = convert_beamerframeargs(document, i, parbeg)
4177                 if ertend == -1:
4178                     break
4179                 # Step II: Now rename the layout and convert the title to an argument
4180                 j = find_end_of_layout(document.body, i)
4181                 document.body[j : j + 1] = ['\\end_layout', '', '\\end_inset', '', '\\end_layout']
4182                 if lay == "BeginFrame":
4183                     document.body[i] = "\\begin_layout Frame"
4184                 else:
4185                     document.body[i] = "\\begin_layout PlainFrame"
4186                 document.body[ertend + 1 : ertend + 1] = ['\\begin_inset Argument 4',
4187                                                 'status open', '', '\\begin_layout Plain Layout']
4188                 # Step III: find real frame end
4189                 j = j + 8
4190                 jj = j
4191                 inInset = get_containing_inset(document.body, i)
4192                 while True:
4193                     fend = find_token(document.body, "\\begin_layout", jj)
4194                     if fend == -1:
4195                         document.warning("Malformed LyX document: No real frame end!")
4196                         return
4197                     val = get_value(document.body, "\\begin_layout", fend)
4198                     if val not in frameend:
4199                         jj = fend + 1
4200                         continue
4201                     # is this frame nested in an inset (e.g., Note)?
4202                     if inInset != False:
4203                         # if so, end the frame inside the inset
4204                         if inInset[2] < fend:
4205                             fend = inInset[2]
4206                     if val == frametype:
4207                         document.body[fend : fend] = ['\\end_deeper', '', '\\begin_layout Separator', '', '\\end_layout']
4208                     # consider explicit EndFrames between two identical frame types
4209                     elif val == "EndFrame":
4210                         nextlayout = find_token(document.body, "\\begin_layout", fend + 1)
4211                         if nextlayout != -1 and get_value(document.body, "\\begin_layout", nextlayout) == frametype:
4212                             document.body[fend : fend] = ['\\end_deeper', '', '\\begin_layout Separator', '', '\\end_layout']
4213                         else:
4214                             document.body[fend : fend] = ['\\end_deeper']
4215                     else:
4216                         document.body[fend : fend] = ['\\end_deeper']
4217                     document.body[j + 1 : j + 1] = ['', '\\begin_deeper']
4218                     break
4219             i = j
4220
4221
4222 def remove_endframes(document):
4223     " Remove deprecated beamer endframes "
4224     
4225     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
4226     if document.textclass not in beamer_classes:
4227         return
4228    
4229     i = 0
4230     while True:
4231         i = find_token_exact(document.body, "\\begin_layout EndFrame", i)
4232         if i == -1:
4233             break
4234         j = find_end_of_layout(document.body, i)
4235         if j == -1:
4236             document.warning("Malformed LyX document: Missing \\end_layout to EndFrame")
4237             i += 1
4238             continue
4239         del document.body[i : j + 1]
4240
4241
4242 def revert_powerdot_flexes(document):
4243     " Reverts powerdot flex insets "
4244     
4245     if document.textclass != "powerdot":
4246         return
4247
4248     flexes = {"Onslide" : "\\onslide",
4249               "Onslide*" : "\\onslide*",
4250               "Onslide+" : "\\onslide+"}
4251     rx = re.compile(r'^\\begin_inset Flex (.+)$')
4252
4253     i = 0
4254     while True:
4255         i = find_token(document.body, "\\begin_inset Flex", i)
4256         if i == -1:
4257             return
4258         m = rx.match(document.body[i])
4259         if m:
4260             flextype = m.group(1)
4261             z = find_end_of_inset(document.body, i)
4262             if z == -1:
4263                 document.warning("Can't find end of Flex " + flextype + " inset.")
4264                 i += 1
4265                 continue
4266             if flextype in flexes:
4267                 pre = put_cmd_in_ert(flexes[flextype])
4268                 arg = find_token(document.body, "\\begin_inset Argument 1", i, z)
4269                 if arg != -1:
4270                     argend = find_end_of_inset(document.body, arg)
4271                     if argend == -1:
4272                         document.warning("Can't find end of Argument!")
4273                         i += 1
4274                         continue
4275                     # Find containing paragraph layout
4276                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
4277                     endPlain = find_end_of_layout(document.body, beginPlain)
4278                     argcontent = document.body[beginPlain + 1 : endPlain]
4279                     # Adjust range end
4280                     z = z - len(document.body[arg : argend + 1])
4281                     # Remove arg inset
4282                     del document.body[arg : argend + 1]
4283                     pre += put_cmd_in_ert("{") + argcontent + put_cmd_in_ert("}")
4284                 pre += put_cmd_in_ert("{")
4285                 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
4286                 endPlain = find_end_of_layout(document.body, beginPlain)
4287                 # Adjust range end
4288                 z = z - len(document.body[i : beginPlain + 1])
4289                 z += len(pre)
4290                 document.body[i : beginPlain + 1] = pre
4291                 post = put_cmd_in_ert("}")
4292                 document.body[z - 2 : z + 1] = post     
4293         i += 1
4294
4295
4296 def revert_powerdot_pause(document):
4297     " Reverts powerdot pause layout to ERT "
4298     
4299     if document.textclass != "powerdot":
4300         return
4301
4302     i = 0
4303     while True:
4304         i = find_token(document.body, "\\begin_layout Pause", i)
4305         if i == -1:
4306             return
4307         j = find_end_of_layout(document.body, i)
4308         if j == -1:
4309             document.warning("Malformed LyX document: Can't find end of Pause layout")
4310             i += 1
4311             continue
4312         endlay = j
4313         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\pause")
4314         for p in range(i, j):
4315             if p >= endlay:
4316                 break
4317             arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
4318             if arg != -1:
4319                 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
4320                 endPlain = find_end_of_layout(document.body, beginPlain)
4321                 endInset = find_end_of_inset(document.body, p)
4322                 content = document.body[beginPlain + 1 : endPlain]
4323                 # Adjust range end
4324                 endlay = endlay - len(document.body[p : endInset + 1])
4325                 # Remove arg inset
4326                 del document.body[p : endInset + 1]
4327                 subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
4328                     
4329         document.body[i : i + 1] = subst
4330         i = endlay
4331
4332
4333 def revert_powerdot_itemargs(document):
4334     " Reverts powerdot item arguments to ERT "
4335     
4336     if document.textclass != "powerdot":
4337         return
4338
4339     i = 0
4340     list_layouts = ["Itemize", "ItemizeType1", "Enumerate", "EnumerateType1"]
4341     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
4342
4343     while True:
4344         i = find_token(document.body, "\\begin_inset Argument", i)
4345         if i == -1:
4346             return
4347         # Find containing paragraph layout
4348         parent = get_containing_layout(document.body, i)
4349         if parent == False:
4350             document.warning("Malformed LyX document: Can't find parent paragraph layout")
4351             i += 1
4352             continue
4353         parbeg = parent[1]
4354         parend = parent[2]
4355         realparbeg = parent[3]
4356         layoutname = parent[0]
4357         realparend = parend
4358         for p in range(parbeg, parend):
4359             if p >= realparend:
4360                 i = realparend
4361                 break
4362             if layoutname in list_layouts:
4363                 m = rx.match(document.body[p])
4364                 if m:
4365                     argnr = m.group(1)
4366                     if argnr == "item:1":
4367                         j = find_end_of_inset(document.body, i)
4368                         # Find containing paragraph layout
4369                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
4370                         endPlain = find_end_of_layout(document.body, beginPlain)
4371                         content = document.body[beginPlain + 1 : endPlain]
4372                         del document.body[i:j+1]
4373                         subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
4374                         document.body[realparbeg : realparbeg] = subst
4375                     elif argnr == "item:2":
4376                         j = find_end_of_inset(document.body, i)
4377                         # Find containing paragraph layout
4378                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
4379                         endPlain = find_end_of_layout(document.body, beginPlain)
4380                         content = document.body[beginPlain + 1 : endPlain]
4381                         del document.body[i:j+1]
4382                         subst = put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
4383                         document.body[realparbeg : realparbeg] = subst
4384         
4385         i = realparend
4386
4387
4388 def revert_powerdot_columns(document):
4389     " Reverts powerdot twocolumn to TeX-code "
4390     if document.textclass != "powerdot":
4391         return
4392
4393     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
4394     i = 0
4395     while True:
4396         i = find_token(document.body, "\\begin_layout Twocolumn", i)
4397         if i == -1:
4398             return
4399         j = find_end_of_layout(document.body, i)
4400         if j == -1:
4401             document.warning("Malformed LyX document: Can't find end of Twocolumn layout")
4402             i += 1
4403             continue
4404         endlay = j
4405         document.body[j : j] = put_cmd_in_ert("}") + document.body[j : j]
4406         endlay += len(put_cmd_in_ert("}"))
4407         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\twocolumn")
4408         for p in range(i, j):
4409             if p >= endlay:
4410                 break
4411             m = rx.match(document.body[p])
4412             if m:
4413                 argnr = m.group(1)
4414                 if argnr == "1":
4415                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
4416                     endPlain = find_end_of_layout(document.body, beginPlain)
4417                     endInset = find_end_of_inset(document.body, p)
4418                     content = document.body[beginPlain + 1 : endPlain]
4419                     # Adjust range end
4420                     endlay = endlay - len(document.body[p : endInset + 1])
4421                     # Remove arg inset
4422                     del document.body[p : endInset + 1]
4423                     subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
4424                 elif argnr == "2":
4425                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
4426                     endPlain = find_end_of_layout(document.body, beginPlain)
4427                     endInset = find_end_of_inset(document.body, p)
4428                     content = document.body[beginPlain + 1 : endPlain]
4429                     # Adjust range end
4430                     endlay = endlay - len(document.body[p : endInset + 1])
4431                     # Remove arg inset
4432                     del document.body[p : endInset + 1]
4433                     subst += put_cmd_in_ert("{") + content + put_cmd_in_ert("}")
4434                     
4435         subst += put_cmd_in_ert("{")
4436         document.body[i : i + 1] = subst
4437         i = endlay
4438
4439
4440 def revert_mbox_fbox(document):
4441     'Convert revert mbox/fbox boxes to TeX-code'
4442     i = 0
4443     while True:
4444         i = find_token(document.body, "\\begin_inset Box", i)
4445         if i == -1:
4446             return
4447         j = find_token(document.body, "width", i)
4448         if j != i + 7:
4449             document.warning("Malformed LyX document: Can't find box width")
4450             return
4451         width = get_value(document.body, "width", j)
4452         k = find_end_of_inset(document.body, j)
4453         if k == -1:
4454             document.warning("Malformed LyX document: Can't find end of box inset")
4455             i += 1
4456             continue
4457         BeginLayout = find_token(document.body, "\\begin_layout Plain Layout", j)
4458         EndLayout = find_end_of_layout(document.body, BeginLayout)
4459         # replace if width is ""
4460         if (width == '""'):
4461             document.body[EndLayout:k + 1] = put_cmd_in_ert("}")
4462             if document.body[i] == "\\begin_inset Box Frameless":
4463                 document.body[i:BeginLayout + 1] = put_cmd_in_ert("\\mbox{")
4464             if document.body[i] == "\\begin_inset Box Boxed":
4465                 document.body[i:BeginLayout + 1] = put_cmd_in_ert("\\fbox{")
4466         i += 1
4467
4468
4469 def revert_starred_caption(document):
4470     " Reverts unnumbered longtable caption insets "
4471     
4472     i = 0
4473     while True:
4474       i = find_token(document.body, "\\begin_inset Caption LongTableNoNumber", i)
4475       if i == -1:
4476           return
4477       # This is not equivalent, but since the caption inset is a full blown
4478       # text inset a true conversion to ERT is too difficult.
4479       document.body[i] = "\\begin_inset Caption Standard"
4480       i += 1
4481
4482
4483 def revert_forced_local_layout(document):
4484     i = 0
4485     while True:
4486         i = find_token(document.header, "\\begin_forced_local_layout", i)
4487         if i == -1:
4488             return
4489         j = find_end_of(document.header, i, "\\begin_forced_local_layout", "\\end_forced_local_layout")
4490         if j == -1:
4491             # this should not happen
4492             break
4493         regexp = re.compile(r'\s*forcelocal', re.IGNORECASE)
4494         k = find_re(document.header, regexp, i, j)
4495         while k != -1:
4496             del document.header[k]
4497             j = j - 1
4498             k = find_re(document.header, regexp, i, j)
4499         k = find_token(document.header, "\\begin_local_layout", 0)
4500         if k == -1:
4501             document.header[i] = "\\begin_local_layout"
4502             document.header[j] = "\\end_local_layout"
4503         else:
4504             l = find_end_of(document.header, k, "\\begin_local_layout", "\\end_local_layout")
4505             if j == -1:
4506                 # this should not happen
4507                 break
4508             lines = document.header[i+1 : j]
4509             if k > i:
4510                 document.header[k+1 : k+1] = lines
4511                 document.header[i   : j  ] = []
4512             else:
4513                 document.header[i   : j  ] = []
4514                 document.header[k+1 : k+1] = lines
4515
4516
4517 def revert_aa1(document):
4518   " Reverts InsetArguments of aa to TeX-code "
4519   if document.textclass == "aa":
4520     i = 0
4521     while True:
4522       if i != -1:
4523         i = find_token(document.body, "\\begin_layout Abstract (structured)", i)
4524       if i != -1:
4525         revert_Argument_to_TeX_brace(document, i, 0, 1, 4, False, False)
4526         i += 1
4527       if i == -1:
4528         return
4529
4530
4531 def revert_aa2(document):
4532   " Reverts InsetArguments of aa to TeX-code "
4533   if document.textclass == "aa":
4534     i = 0
4535     while True:
4536       if i != -1:
4537         i = find_token(document.body, "\\begin_layout Abstract (structured)", i)
4538       if i != -1:
4539         document.body[i] = "\\begin_layout Abstract"
4540         i += 1
4541       if i == -1:
4542         return
4543
4544
4545 def revert_tibetan(document):
4546     "Set the document language for Tibetan to English" 
4547
4548     if document.language == "tibetan":
4549         document.language = "english"
4550         i = find_token(document.header, "\\language", 0) 
4551         if i != -1: 
4552             document.header[i] = "\\language english" 
4553     j = 0
4554     while j < len(document.body): 
4555         j = find_token(document.body, "\\lang tibetan", j)
4556         if j != -1:
4557             document.body[j] = document.body[j].replace("\\lang tibetan", "\\lang english")
4558             j += 1
4559         else:
4560             j = len(document.body)
4561
4562
4563 #############
4564 #
4565 # Chunk stuff
4566 #
4567 #############
4568
4569 # The idea here is that we will have a sequence of chunk paragraphs.
4570 # We want to convert them to paragraphs in one or several chunk insets.
4571 # Individual chunks are terminated by the character @ on the last line.
4572 # This line will be discarded, and following lines are treated as new
4573 # chunks, which go into their own insets.
4574 # The first line of a chunk should look like: <<CONTENT>>=
4575 # We will discard the delimiters, and put the CONTENT into the
4576 # optional argument of the inset, if the CONTENT is non-empty.
4577 def convert_chunks(document):
4578     first_re = re.compile(r'<<(.*)>>=(.*)')
4579     file_pos = 0
4580     while True:
4581         # find start of a block of chunks
4582         i = find_token(document.body, "\\begin_layout Chunk", file_pos)
4583         if i == -1:
4584             return
4585         start = i
4586         end = -1
4587         contents = []
4588         chunk_started = False
4589
4590         while True:
4591             # process the one we just found
4592             j = find_end_of_layout(document.body, i)
4593             if j == -1:
4594                 document.warning("Malformed LyX documents. Can't find end of Chunk layout!")
4595                 # there is no point continuing, as we will run into the same error again.
4596                 return
4597             this_chunk = "".join(document.body[i + 1:j])
4598             
4599             # there may be empty lines between chunks
4600             # we just skip them.
4601             if not chunk_started:
4602                 if this_chunk != "":
4603                     # new chunk starts
4604                     chunk_started = True
4605             
4606             if chunk_started:
4607                 contents.append(document.body[i + 1:j])
4608
4609             # look for potential chunk terminator
4610             # on the last line of the chunk paragraph
4611             if document.body[j - 1] == "@":
4612                 break
4613
4614             # look for subsequent chunk paragraph
4615             i = find_token(document.body, "\\begin_layout", j)
4616             if i == -1:
4617                 break
4618
4619             if get_value(document.body, "\\begin_layout", i) != "Chunk":
4620                 break
4621
4622         file_pos = end = j + 1
4623
4624         # The last chunk should simply have an "@" in it
4625         # or at least end with "@" (can happen if @ is
4626         # preceded by a newline)
4627         lastpar = ''
4628         if len(contents) > 0:
4629             lastpar = ''.join(contents[-1])
4630         if not lastpar.endswith("@"):
4631             document.warning("Unexpected chunk content: chunk not terminated by '@'!")
4632             if len(contents) == 0:
4633                 # convert empty chunk layouts to Standard
4634                 document.body[start] = "\\begin_layout Standard"
4635             continue
4636
4637         if lastpar == "@":
4638             # chunk par only contains "@". Just drop it.
4639             contents.pop()
4640         else:
4641             # chunk par contains more. Only drop the "@".
4642             contents[-1].pop()
4643
4644         # The first line should look like: <<CONTENT>>=
4645         # We want the CONTENT
4646         optarg = ' '.join(contents[0])
4647         optarg.strip()
4648         # We can already have real chunk content in
4649         # the first par (separated from the options by a newline).
4650         # We collect such stuff to re-insert it later.
4651         postoptstuff = []
4652         
4653         match = first_re.search(optarg)
4654         if match:
4655             optarg = match.groups()[0]
4656             if match.groups()[1] != "":
4657                 postopt = False
4658                 for c in contents[0]:
4659                     if c.endswith(">>="):
4660                         postopt = True
4661                         continue
4662                     if postopt:
4663                         postoptstuff.append(c)
4664             # We have stripped everything. This can be deleted.
4665             contents.pop(0)
4666
4667         newstuff = ['\\begin_layout Standard']
4668
4669         # Maintain paragraph parameters
4670         par_params = ["\\noindent", "\\indent", "\\indent-toggle", "\\leftindent",
4671                       "\\start_of_appendix", "\\paragraph_spacing", "\\align",
4672                       "\\labelwidthstring"]
4673         parms = start + 1
4674         while True:
4675             if document.body[parms].split(' ', 1)[0] not in par_params:
4676                 break
4677             newstuff.extend([document.body[parms]])
4678             parms += 1
4679
4680         newstuff.extend(
4681             ['\\begin_inset Flex Chunk',
4682              'status open', '',
4683              '\\begin_layout Plain Layout', ''])
4684
4685         # If we have a non-empty optional argument, insert it.
4686         if match and optarg != "":
4687             newstuff.extend(
4688                 ['\\begin_inset Argument 1',
4689                  'status open', '',
4690                  '\\begin_layout Plain Layout',
4691                  optarg,
4692                  '\\end_layout', '',
4693                  '\\end_inset', ''])
4694
4695         # Since we already opened a Plain layout, the first paragraph
4696         # does not need to do that.
4697         did_one_par = False
4698         if postoptstuff:
4699             # we need to replace newlines with new layouts
4700             start_newline = -1
4701             started_text = False
4702             for lno in range(0,len(postoptstuff)):
4703                 if postoptstuff[lno].startswith("\\begin_inset Newline newline"):
4704                     start_newline = lno
4705                 elif start_newline != -1:
4706                     if postoptstuff[lno].startswith("\\end_inset"):
4707                         # replace that bit, but only if we already have some text
4708                         # and we're not at the end except for a blank line
4709                         if started_text and \
4710                           (lno != len(postoptstuff) - 2 or postoptstuff[-1] != ""):
4711                             newstuff.extend(['\\end_layout', '\n', '\\begin_layout Plain Layout', '\n'])
4712                         start_newline = -1
4713                         started_text = True
4714                 else:
4715                     newstuff.extend([postoptstuff[lno]])
4716             newstuff.append('\\end_layout')
4717             did_one_par = True
4718         for c in contents:
4719             if did_one_par:
4720                 newstuff.extend(['', '\\begin_layout Plain Layout', ''])
4721             else:
4722                 did_one_par = True
4723             newstuff.extend(c)
4724             newstuff.append('\\end_layout')
4725
4726         newstuff.extend(['', '\\end_inset', '', '\\end_layout', ''])
4727
4728         document.body[start:end] = newstuff
4729
4730         file_pos += len(newstuff) - (end - start)
4731
4732
4733 def revert_chunks(document):
4734     i = 0
4735     while True:
4736         i = find_token(document.body, "\\begin_inset Flex Chunk", i)
4737         if i == -1:
4738             return
4739
4740         iend = find_end_of_inset(document.body, i)
4741         if iend == -1:
4742             document.warning("Can't find end of Chunk!")
4743             i += 1
4744             continue
4745
4746         # Look for optional argument
4747         optarg = ""
4748         ostart = find_token(document.body, "\\begin_inset Argument 1", i, iend)
4749         if ostart != -1:
4750             oend = find_end_of_inset(document.body, ostart)
4751             k = find_token(document.body, "\\begin_layout Plain Layout", ostart, oend)
4752             if k == -1:
4753                 document.warning("Malformed LyX document: Can't find argument contents!")
4754             else:
4755                 m = find_end_of_layout(document.body, k)
4756                 optarg = "".join(document.body[k+1:m])
4757
4758             # We now remove the optional argument, so we have something
4759             # uniform on which to work
4760             document.body[ostart : oend + 1] = []
4761             # iend is now invalid
4762             iend = find_end_of_inset(document.body, i)
4763
4764         retval = get_containing_layout(document.body, i)
4765         if not retval:
4766             document.warning("Can't find containing layout for Chunk!")
4767             i = iend
4768             continue
4769         (lname, lstart, lend, pstart)  = retval
4770         # we now want to work through the various paragraphs, and collect their contents
4771         parlist = []
4772         k = i
4773         while True:
4774             k = find_token(document.body, "\\begin_layout Plain Layout", k, lend)
4775             if k == -1:
4776                 break
4777             j = find_end_of_layout(document.body, k)
4778             if j == -1:
4779                 document.warning("Can't find end of layout inside chunk!")
4780                 break
4781             parlist.append(document.body[k+1:j])
4782             k = j
4783         # we now need to wrap all of these paragraphs in chunks
4784         newlines = []
4785         newlines.extend(["\\begin_layout Chunk", "", "<<" + optarg + ">>=", "\\end_layout", ""])
4786         for stuff in parlist:
4787             newlines.extend(["\\begin_layout Chunk"] + stuff + ["\\end_layout", ""])
4788         newlines.extend(["\\begin_layout Chunk", "", "@", "\\end_layout", ""])
4789         # replace old content with new content
4790         document.body[lstart : lend + 1] = newlines
4791         i = lstart + len(newlines)
4792         
4793
4794 ##
4795 # Conversion hub
4796 #
4797
4798 supported_versions = ["2.1.0","2.1"]
4799 convert = [
4800            [414, []],
4801            [415, [convert_undertilde]],
4802            [416, []],
4803            [417, [convert_japanese_encodings]],
4804            [418, [convert_justification]],
4805            [419, []],
4806            [420, [convert_biblio_style]],
4807            [421, [convert_longtable_captions]],
4808            [422, [convert_use_packages]],
4809            [423, [convert_use_mathtools]],
4810            [424, [convert_cite_engine_type]],
4811            # No convert_cancel, since cancel will be loaded automatically
4812            # in format 425 without any possibility to switch it off.
4813            # This has been fixed in format 464.
4814            [425, []],
4815            [426, []],
4816            [427, []],
4817            [428, [convert_cell_rotation]],
4818            [429, [convert_table_rotation]],
4819            [430, [convert_listoflistings]],
4820            [431, [convert_use_amssymb]],
4821            [432, []],
4822            [433, [convert_armenian]],
4823            [434, []],
4824            [435, []],
4825            [436, []],
4826            [437, []],
4827            [438, []],
4828            [439, []],
4829            [440, []],
4830            [441, [convert_mdnomath]],
4831            [442, []],
4832            [443, []],
4833            [444, []],
4834            [445, []],
4835            [446, [convert_latexargs]],
4836            [447, [convert_IEEEtran, convert_AASTeX, convert_AGUTeX, convert_IJMP, convert_SIGPLAN, convert_SIGGRAPH, convert_EuropeCV, convert_Initials, convert_ModernCV]],
4837            [448, [convert_literate]],
4838            [449, []],
4839            [450, []],
4840            [451, [convert_beamerargs, convert_againframe_args, convert_corollary_args, convert_quote_args]],
4841            [452, [convert_beamerblocks]],
4842            [453, [convert_use_stmaryrd]],
4843            [454, [convert_overprint]],
4844            [455, []],
4845            [456, [convert_epigraph]],
4846            [457, [convert_use_stackrel]],
4847            [458, [convert_captioninsets, convert_captionlayouts]],
4848            [459, []],
4849            [460, []],
4850            [461, []],
4851            [462, []],
4852            [463, [convert_encodings]],
4853            [464, [convert_use_cancel]],
4854            [465, [convert_lyxframes, remove_endframes]],
4855            [466, []],
4856            [467, []],
4857            [468, []],
4858            [469, []],
4859            [470, []],
4860            [471, [convert_cite_engine_type_default]],
4861            [472, []],
4862            [473, []],
4863            [474, [convert_chunks, cleanup_beamerargs]],
4864           ]
4865
4866 revert =  [
4867            [473, [revert_chunks]],
4868            [472, [revert_tibetan]],
4869            [471, [revert_aa1,revert_aa2]],
4870            [470, [revert_cite_engine_type_default]],
4871            [469, [revert_forced_local_layout]],
4872            [468, [revert_starred_caption]],
4873            [467, [revert_mbox_fbox]],
4874            [466, [revert_iwona_fonts]],
4875            [465, [revert_powerdot_flexes, revert_powerdot_pause, revert_powerdot_itemargs, revert_powerdot_columns]],
4876            [464, []],
4877            [463, [revert_use_cancel]],
4878            [462, [revert_encodings]],
4879            [461, [revert_new_libertines]],
4880            [460, [revert_kurier_fonts]],
4881            [459, [revert_IEEEtran_3]],
4882            [458, [revert_fragileframe, revert_newframes]],
4883            [457, [revert_captioninsets, revert_captionlayouts]],
4884            [456, [revert_use_stackrel]],
4885            [455, [revert_epigraph]],
4886            [454, [revert_frametitle]],
4887            [453, [revert_overprint]],
4888            [452, [revert_use_stmaryrd]],
4889            [451, [revert_beamerblocks]],
4890            [450, [revert_beamerargs, revert_beamerargs2, revert_beamerargs3, revert_beamerflex]],
4891            [449, [revert_garamondx, revert_garamondx_newtxmath]],
4892            [448, [revert_itemargs]],
4893            [447, [revert_literate]],
4894            [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]],
4895            [445, [revert_latexargs]],
4896            [444, [revert_uop]],
4897            [443, [revert_biolinum]],
4898            [442, []],
4899            [441, [revert_newtxmath]],
4900            [440, [revert_mdnomath]],
4901            [439, [revert_mathfonts]],
4902            [438, [revert_minionpro]],
4903            [437, [revert_ipadeco, revert_ipachar]],
4904            [436, [revert_texgyre]],
4905            [435, [revert_mathdesign]],
4906            [434, [revert_txtt]],
4907            [433, [revert_libertine]],
4908            [432, [revert_armenian]],
4909            [431, [revert_languages, revert_ancientgreek]],
4910            [430, [revert_use_amssymb]],
4911            [429, [revert_listoflistings]],
4912            [428, [revert_table_rotation]],
4913            [427, [revert_cell_rotation]],
4914            [426, [revert_tipa]],
4915            [425, [revert_verbatim]],
4916            [424, [revert_cancel]],
4917            [423, [revert_cite_engine_type]],
4918            [422, [revert_use_mathtools]],
4919            [421, [revert_use_packages]],
4920            [420, [revert_longtable_captions]],
4921            [419, [revert_biblio_style]],
4922            [418, [revert_australian]],
4923            [417, [revert_justification]],
4924            [416, [revert_japanese_encodings]],
4925            [415, [revert_negative_space, revert_math_spaces]],
4926            [414, [revert_undertilde]],
4927            [413, [revert_visible_space]]
4928           ]
4929
4930
4931 if __name__ == "__main__":
4932     pass