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