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