]> git.lyx.org Git - lyx.git/blob - lib/lyx2lyx/lyx_2_1.py
071936f7f4c0bbe4679c0fefb4ce0c13a8a03732
[lyx.git] / lib / lyx2lyx / lyx_2_1.py
1 # -*- coding: utf-8 -*-
2 # This file is part of lyx2lyx
3 # -*- coding: utf-8 -*-
4 # Copyright (C) 2011 The LyX team
5 #
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19
20 """ Convert files to the file format generated by LyX 2.1"""
21
22 import re, string
23 import unicodedata
24 import sys, os
25
26 # Uncomment only what you need to import, please.
27
28 from parser_tools import count_pars_in_inset, del_token, find_token, find_token_exact, \
29     find_token_backwards, find_end_of, find_end_of_inset, find_end_of_layout, \
30     find_end_of_sequence, find_re, get_option_value, get_containing_layout, \
31     get_value, get_quoted_value, set_option_value
32
33 #from parser_tools import find_token, find_end_of, find_tokens, \
34   #find_end_of_inset, find_end_of_layout, \
35   #is_in_inset, del_token, check_token
36
37 from lyx2lyx_tools import add_to_preamble, put_cmd_in_ert, get_ert
38
39 #from lyx2lyx_tools import insert_to_preamble, \
40 #  lyx2latex, latex_length, revert_flex_inset, \
41 #  revert_font_attrs, hex2ratio, str2bool
42
43 ####################################################################
44 # Private helper functions
45
46 #def remove_option(lines, m, option):
47     #''' removes option from line m. returns whether we did anything '''
48     #l = lines[m].find(option)
49     #if l == -1:
50         #return False
51     #val = lines[m][l:].split('"')[1]
52     #lines[m] = lines[m][:l - 1] + lines[m][l+len(option + '="' + val + '"'):]
53     #return True
54
55
56 def revert_Argument_to_TeX_brace(document, line, endline, n, nmax, environment, opt):
57     '''
58     Reverts an InsetArgument to TeX-code
59     usage:
60     revert_Argument_to_TeX_brace(document, LineOfBegin, LineOfEnd, StartArgument, EndArgument, isEnvironment, isOpt)
61     LineOfBegin is the line  of the \begin_layout or \begin_inset statement
62     LineOfEnd is the line  of the \end_layout or \end_inset statement, if "0" is given, the end of the file is used instead
63     StartArgument is the number of the first argument that needs to be converted
64     EndArgument is the number of the last argument that needs to be converted or the last defined one
65     isEnvironment must be true, if the layout is for a LaTeX environment
66     isOpt must be true, if the argument is an optional one
67     '''
68     lineArg = 0
69     wasOpt = False
70     while lineArg != -1 and n < nmax + 1:
71       lineArg = find_token(document.body, "\\begin_inset Argument " + str(n), line)
72       if lineArg > endline and endline != 0:
73         return wasOpt
74       if lineArg != -1:
75         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
76         # we have to assure that no other inset is in the Argument
77         beginInset = find_token(document.body, "\\begin_inset", beginPlain)
78         endInset = find_token(document.body, "\\end_inset", beginPlain)
79         k = beginPlain + 1
80         l = k
81         while beginInset < endInset and beginInset != -1:
82           beginInset = find_token(document.body, "\\begin_inset", k)
83           endInset = find_token(document.body, "\\end_inset", l)
84           k = beginInset + 1
85           l = endInset + 1
86         if environment == False:
87           if opt == False:
88             document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}{")
89             del(document.body[lineArg : beginPlain + 1])
90             wasOpt = False
91           else:
92             document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
93             document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("[")
94             wasOpt = True
95         else:
96           document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
97           document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("{")
98           wasOpt = False
99         n += 1
100     return wasOpt
101
102
103 def convert_TeX_brace_to_Argument(document, line, n, nmax, inset, environment, opt):
104     '''
105     Converts TeX code for mandatory arguments to an InsetArgument
106     The conversion of TeX code for optional arguments must be done with another routine
107     !!! Be careful if the braces are different in your case as expected here:
108     - "}{" separates mandatory arguments of commands
109     - "}" + "{" separates mandatory arguments of commands
110     - "}" + " " + "{" separates mandatory arguments of commands
111     - { and } surround a mandatory argument of an environment
112     usage:
113     convert_TeX_brace_to_Argument(document, LineOfBeginLayout/Inset, StartArgument, EndArgument, isInset, isEnvironment, isOpt)
114     LineOfBeginLayout/Inset is the line  of the \begin_layout or \begin_inset statement
115     StartArgument is the number of the first ERT that needs to be converted
116     EndArgument is the number of the last ERT that needs to be converted
117     isInset must be true, if braces inside an InsetLayout needs to be converted
118     isEnvironment must be true, if the layout is for a LaTeX environment
119     isOpt must be true, if the argument is an optional one
120     
121     Todo: this routine can currently handle only one mandatory argument of environments
122     '''
123
124     end_layout = find_end_of_layout(document.body, line)
125     lineERT = line
126     endn = line
127     loop = 1
128     while n < nmax + 1:
129       lineERT = find_token(document.body, "\\begin_inset ERT", lineERT, end_layout)
130       if lineERT == -1:
131         break
132       if environment == False:
133         end_ERT = find_end_of_inset(document.body, lineERT)
134         if end_ERT == -1:
135           document.warning("Can't find end of ERT!!")
136           break
137         # Note that this only checks for ][ or }{ at the beginning of a line
138         if opt:
139           bracePair = find_token(document.body, "][", lineERT, end_ERT)
140         else:
141           bracePair = find_token(document.body, "}{", lineERT, end_ERT)
142         if bracePair != -1:
143           end = find_token(document.body, "\\end_inset", bracePair)
144           document.body[lineERT : end_ERT + 1] = ["\\end_layout", "", "\\end_inset"]
145           if loop == 1:
146             # in the case that n > 1 we have optional arguments before
147             # therefore detect them if any
148             if n > 1:
149               # first check if there is an argument
150               lineArg = find_token(document.body, "\\begin_inset Argument", line)
151               if lineArg < lineERT and lineArg != -1:
152                 # we have an argument, so now search backwards for its end
153                 # we must now assure that we don't find other insets like e.g. a newline
154                 endInsetArg = lineERT
155                 endLayoutArg = endInsetArg
156                 while endInsetArg != endLayoutArg + 2 and endInsetArg != -1:
157                   endInsetArg = endInsetArg - 1
158                   endLayoutArg = endInsetArg
159                   endInsetArg = find_token_backwards(document.body, "\\end_inset", endInsetArg)
160                   endLayoutArg = find_token_backwards(document.body, "\\end_layout", endLayoutArg)
161                 line = endInsetArg + 1
162             if inset == False:
163               document.body[line + 1 : line + 1] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
164             else:
165               document.body[line + 4 : line + 4] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
166           else: # if loop != 1
167             document.body[endn : endn] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
168           n += 1
169           endn = end
170           loop += 1
171         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_layout', '', '\\end_inset']
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 count_pars_in_inset(document.body, ertcontfirstline) > 1:
3056                                 # Multipar ERT. Skip this.
3057                                 break
3058                             else:
3059                                 convert_TeX_brace_to_Argument(document, i, 2, 2, False, True, False)
3060                         else:
3061                             break
3062                         j = find_end_of_layout(document.body, i)
3063                         if j == -1:
3064                             document.warning("end of layout not found!")
3065                         k = find_token(document.body, "\\begin_inset Argument", i, j)
3066                         if k == -1:
3067                             document.warning("InsetArgument not found!")
3068                             break
3069                         l = find_end_of_inset(document.body, k)
3070                         m = find_token(document.body, "\\begin_inset ERT", l, j)
3071                         if m == -1:
3072                             break
3073                         ertcontfirstline = m + 5
3074                         parbeg = m
3075             i = j
3076
3077
3078 def convert_overprint(document):
3079     " Convert old beamer overprint layouts to ERT "
3080     
3081     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3082     if document.textclass not in beamer_classes:
3083         return
3084
3085     i = 0
3086     while True:
3087         i = find_token(document.body, "\\begin_layout Overprint", i)
3088         if i == -1:
3089             return
3090         # Find end of sequence
3091         j = find_end_of_sequence(document.body, i)
3092         if j == -1:
3093             document.warning("Malformed LyX document. Cannot find end of Overprint sequence!")
3094             i += 1
3095             continue
3096         endseq = j
3097         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\begin{overprint}")
3098         esubst = list()
3099         if document.body[j] == "\\end_deeper":
3100             esubst = ["", "\\begin_layout Standard"] + put_cmd_in_ert("\\end{overprint}") + ["\\end_layout"]
3101         else:
3102             esubst = ["\\end_layout", "", "\\begin_layout Standard"] + put_cmd_in_ert("\\end{overprint}") + ["\\end_layout"]
3103         endseq = endseq + len(esubst) - len(document.body[j : j])
3104         document.body[j : j] = esubst
3105         argbeg = find_token(document.body, "\\begin_inset Argument 1", i, j)
3106         if argbeg != -1:
3107             argend = find_end_of_layout(document.body, argbeg)
3108             if argend == -1:
3109                 document.warning("Malformed LyX document. Cannot find end of Overprint argument!")
3110                 i += 1
3111                 continue
3112             beginPlain = find_token(document.body, "\\begin_layout Plain Layout", argbeg)
3113             endPlain = find_end_of_layout(document.body, beginPlain)
3114             content = document.body[beginPlain + 1 : endPlain]
3115             # Adjust range end
3116             endseq = endseq - len(document.body[argbeg : argend + 1])
3117             # Remove arg inset
3118             del document.body[argbeg : argend + 1]
3119             subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
3120             
3121         endseq = endseq - len(document.body[i : i])
3122         document.body[i : i] = subst + ["\\end_layout"]
3123         endseq += len(subst)
3124         
3125         for p in range(i, endseq):
3126             if document.body[p] == "\\begin_layout Overprint":
3127                 document.body[p] = "\\begin_layout Standard"
3128
3129         i = endseq
3130
3131
3132 def revert_overprint(document):
3133     " Revert old beamer overprint layouts to ERT "
3134     
3135     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3136     if document.textclass not in beamer_classes:
3137         return
3138
3139     i = 0
3140     while True:
3141         i = find_token(document.body, "\\begin_layout Overprint", i)
3142         if i == -1:
3143             return
3144         # Find end of sequence
3145         j = find_end_of_sequence(document.body, i)
3146         if j == -1:
3147             document.warning("Malformed LyX document. Cannot find end of Overprint sequence!")
3148             i += 1
3149             continue
3150         endseq = j
3151         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\begin{overprint}")
3152         esubst = ["\\end_layout", "", "\\begin_layout Standard"] + put_cmd_in_ert("\\end{overprint}")
3153         endseq = endseq + len(esubst) - len(document.body[j : j])
3154         if document.body[j] == "\\end_deeper":
3155             document.body[j : j] = ["\\end_deeper", ""] + esubst
3156         else:
3157             document.body[j : j] = esubst
3158         r = i
3159         while r < j:
3160             if document.body[r] == "\\begin_deeper":
3161                 s = find_end_of(document.body, r, "\\begin_deeper", "\\end_deeper")
3162                 if s != -1:
3163                     document.body[r] = ""
3164                     document.body[s] = ""
3165                     r = s
3166                     continue
3167             r = r + 1
3168         argbeg = find_token(document.body, "\\begin_inset Argument 1", i, j)
3169         if argbeg != -1:
3170             # Is this really our argument?
3171             nested = find_token(document.body, "\\begin_deeper", i, argbeg)
3172             if nested != -1:
3173                 argend = find_end_of_inset(document.body, argbeg)
3174                 if argend == -1:
3175                     document.warning("Malformed LyX document. Cannot find end of Overprint argument!")
3176                     i += 1
3177                     continue
3178                 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", argbeg)
3179                 endPlain = find_end_of_layout(document.body, beginPlain)
3180                 content = document.body[beginPlain + 1 : endPlain]
3181                 # Adjust range end
3182                 endseq = endseq - len(document.body[argbeg : argend])
3183                 # Remove arg inset
3184                 del document.body[argbeg : argend + 1]
3185                 subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
3186             
3187         endseq = endseq - len(document.body[i : i])
3188         document.body[i : i] = subst + ["\\end_layout"]
3189         endseq += len(subst)
3190      
3191         p = i
3192         while True:
3193             if p >= endseq:
3194                 break
3195             if document.body[p] == "\\begin_layout Overprint":
3196                 q = find_end_of_layout(document.body, p)
3197                 if q == -1:
3198                     document.warning("Malformed LyX document. Cannot find end of Overprint layout!")
3199                     p += 1
3200                     continue
3201                 subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\onslide")
3202                 argbeg = find_token(document.body, "\\begin_inset Argument item:1", p, q)
3203                 if argbeg != -1:
3204                     argend = find_end_of_inset(document.body, argbeg)
3205                     if argend == -1:
3206                         document.warning("Malformed LyX document. Cannot find end of Overprint item argument!")
3207                         p += 1
3208                         continue
3209                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", argbeg)
3210                     endPlain = find_end_of_layout(document.body, beginPlain)
3211                     content = document.body[beginPlain + 1 : endPlain]
3212                     # Adjust range end
3213                     endseq = endseq - len(document.body[argbeg : argend + 1])
3214                     # Remove arg inset
3215                     del document.body[argbeg : argend + 1]
3216                     subst += put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
3217                 endseq = endseq - len(document.body[p : p + 1]) + len(subst)
3218                 document.body[p : p + 1] = subst
3219             p = p + 1
3220
3221         i = endseq
3222
3223
3224 def revert_frametitle(document):
3225     " Reverts beamer frametitle layout to ERT "
3226     
3227     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3228     if document.textclass not in beamer_classes:
3229         return
3230
3231     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
3232     i = 0
3233     while True:
3234         i = find_token(document.body, "\\begin_layout FrameTitle", i)
3235         if i == -1:
3236             return
3237         j = find_end_of_layout(document.body, i)
3238         if j == -1:
3239             document.warning("Malformed LyX document: Can't find end of FrameTitle layout")
3240             i += 1
3241             continue
3242         endlay = j
3243         document.body[j : j] = put_cmd_in_ert("}") + document.body[j : j]
3244         endlay += len(put_cmd_in_ert("}"))
3245         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\frametitle")
3246         for p in range(i, j):
3247             if p >= endlay:
3248                 break
3249             m = rx.match(document.body[p])
3250             if m:
3251                 argnr = m.group(1)
3252                 if argnr == "1":
3253                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
3254                     endPlain = find_end_of_layout(document.body, beginPlain)
3255                     endInset = find_end_of_inset(document.body, p)
3256                     content = document.body[beginPlain + 1 : endPlain]
3257                     # Adjust range end
3258                     endlay = endlay - len(document.body[p : endInset + 1])
3259                     # Remove arg inset
3260                     del document.body[p : endInset + 1]
3261                     subst += put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
3262                 elif argnr == "2":
3263                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
3264                     endPlain = find_end_of_layout(document.body, beginPlain)
3265                     endInset = find_end_of_inset(document.body, p)
3266                     content = document.body[beginPlain + 1 : endPlain]
3267                     # Adjust range end
3268                     endlay = endlay - len(document.body[p : endInset + 1])
3269                     # Remove arg inset
3270                     del document.body[p : endInset + 1]
3271                     subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
3272                     
3273         subst += put_cmd_in_ert("{")
3274         document.body[i : i + 1] = subst
3275         i = endlay
3276
3277
3278 def convert_epigraph(document):
3279     " Converts memoir epigraph to new syntax "
3280     
3281     if document.textclass != "memoir":
3282         return
3283
3284     i = 0
3285     while True:
3286         i = find_token(document.body, "\\begin_layout Epigraph", i)
3287         if i == -1:
3288             return
3289         j = find_end_of_layout(document.body, i)
3290         if j == -1:
3291             document.warning("Malformed LyX document: Can't find end of Epigraph layout")
3292             i += 1
3293             continue
3294         endlay = j
3295         subst = list()
3296         ert = find_token(document.body, "\\begin_inset ERT", i, j)
3297         if ert != -1:
3298             endInset = find_end_of_inset(document.body, ert)
3299             beginPlain = find_token(document.body, "\\begin_layout Plain Layout", ert)
3300             endPlain = find_end_of_layout(document.body, beginPlain)
3301             ertcont = beginPlain + 2
3302             if document.body[ertcont] == "}{":
3303                 # strip off the <
3304                 # Convert to ArgInset
3305                 endlay = endlay - 2 * len(document.body[j])
3306                 begsubst = ['\\begin_inset Argument post:1', 'status collapsed', '',
3307                             '\\begin_layout Plain Layout']
3308                 endsubst = ['\\end_layout', '', '\\end_inset', '', document.body[j]]
3309                 document.body[j : j + 1] = endsubst
3310                 document.body[endInset + 1 : endInset + 1] = begsubst
3311                 # Adjust range end
3312                 endlay += len(begsubst) + len(endsubst)
3313                 endlay = endlay - len(document.body[ert : endInset + 1])
3314                 del document.body[ert : endInset + 1]
3315                     
3316         i = endlay
3317
3318
3319 def revert_epigraph(document):
3320     " Reverts memoir epigraph argument to ERT "
3321     
3322     if document.textclass != "memoir":
3323         return
3324
3325     i = 0
3326     while True:
3327         i = find_token(document.body, "\\begin_layout Epigraph", i)
3328         if i == -1:
3329             return
3330         j = find_end_of_layout(document.body, i)
3331         if j == -1:
3332             document.warning("Malformed LyX document: Can't find end of Epigraph layout")
3333             i += 1
3334             continue
3335         endlay = j
3336         subst = list()
3337         p = find_token(document.body, "\\begin_layout Argument post:1", i, j)
3338         if p != -1:
3339             beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
3340             endPlain = find_end_of_layout(document.body, beginPlain)
3341             endInset = find_end_of_inset(document.body, p)
3342             content = document.body[beginPlain + 1 : endPlain]
3343             # Adjust range end
3344             endlay = endlay - len(document.body[p : endInset + 1])
3345             # Remove arg inset
3346             del document.body[p : endInset + 1]
3347             subst += put_cmd_in_ert("}{") + content
3348         else:
3349             subst += put_cmd_in_ert("}{")
3350                     
3351         document.body[j : j] = subst + document.body[j : j]
3352         i = endlay
3353
3354
3355 def convert_captioninsets(document):
3356     " Converts caption insets to new syntax "
3357     
3358     i = 0
3359     while True:
3360       i = find_token(document.body, "\\begin_inset Caption", i)
3361       if i == -1:
3362           return
3363       document.body[i] = "\\begin_inset Caption Standard"
3364       i += 1
3365
3366
3367 def revert_captioninsets(document):
3368     " Reverts caption insets to old syntax "
3369     
3370     i = 0
3371     while True:
3372       i = find_token(document.body, "\\begin_inset Caption Standard", i)
3373       if i == -1:
3374           return
3375       document.body[i] = "\\begin_inset Caption"
3376       i += 1
3377
3378
3379 def convert_captionlayouts(document):
3380     " Convert caption layouts to caption insets. "
3381
3382     caption_dict = {
3383         "Captionabove":  "Above",
3384         "Captionbelow":  "Below",
3385         "FigCaption"  :  "FigCaption",
3386         "Table_Caption" :  "Table",
3387         "CenteredCaption" : "Centered",
3388         "Bicaption" : "Bicaption",
3389         }
3390
3391     i = 0
3392     while True:
3393         i = find_token(document.body, "\\begin_layout", i)
3394         if i == -1:
3395             return
3396         val = get_value(document.body, "\\begin_layout", i)
3397         if val in caption_dict.keys():
3398             j = find_end_of_layout(document.body, i)
3399             if j == -1:
3400                 document.warning("Malformed LyX document: Missing `\\end_layout'.")
3401                 return
3402
3403             document.body[j:j] = ["\\end_layout", "", "\\end_inset", "", ""]
3404             document.body[i:i+1] = ["\\begin_layout %s" % document.default_layout,
3405                                     "\\begin_inset Caption %s" % caption_dict[val], "",
3406                                     "\\begin_layout %s" % document.default_layout]
3407         i += 1
3408
3409
3410 def revert_captionlayouts(document):
3411     " Revert caption insets to caption layouts. "
3412     
3413     caption_dict = {
3414         "Above" : "Captionabove",
3415         "Below" : "Captionbelow",
3416         "FigCaption"  :  "FigCaption",
3417         "Table" : "Table_Caption",
3418         "Centered" : "CenteredCaption",
3419         "Bicaption" : "Bicaption",
3420         }
3421     
3422     i = 0
3423     rx = re.compile(r'^\\begin_inset Caption (\S+)$')
3424     while True:
3425         i = find_token(document.body, "\\begin_inset Caption", i)
3426         if i == -1:
3427             return
3428
3429         m = rx.match(document.body[i])
3430         val = ""
3431         if m:
3432             val = m.group(1)
3433         if val not in caption_dict.keys():
3434             i += 1
3435             continue
3436         
3437         # We either need to delete the previous \begin_layout line, or we
3438         # need to end the previous layout if this inset is not in the first
3439         # position of the paragraph.
3440         layout_before = find_token_backwards(document.body, "\\begin_layout", i)
3441         if layout_before == -1:
3442             document.warning("Malformed LyX document: Missing `\\begin_layout'.")
3443             return
3444         layout_line = document.body[layout_before]
3445         del_layout_before = True
3446         l = layout_before + 1
3447         while l < i:
3448             if document.body[l] != "":
3449                 del_layout_before = False
3450                 break
3451             l = l + 1
3452         if del_layout_before:
3453             del document.body[layout_before:i]
3454             i = layout_before
3455         else:
3456             document.body[i:i] = ["\\end_layout", ""]
3457             i = i + 2
3458
3459         # Find start of layout in the inset and end of inset
3460         j = find_token(document.body, "\\begin_layout", i)
3461         if j == -1:
3462             document.warning("Malformed LyX document: Missing `\\begin_layout'.")
3463             return
3464         k = find_end_of_inset(document.body, i)
3465         if k == -1:
3466             document.warning("Malformed LyX document: Missing `\\end_inset'.")
3467             return
3468
3469         # We either need to delete the following \end_layout line, or we need
3470         # to restart the old layout if this inset is not at the paragraph end.
3471         layout_after = find_token(document.body, "\\end_layout", k)
3472         if layout_after == -1:
3473             document.warning("Malformed LyX document: Missing `\\end_layout'.")
3474             return
3475         del_layout_after = True
3476         l = k + 1
3477         while l < layout_after:
3478             if document.body[l] != "":
3479                 del_layout_after = False
3480                 break
3481             l = l + 1
3482         if del_layout_after:
3483             del document.body[k+1:layout_after+1]
3484         else:
3485             document.body[k+1:k+1] = [layout_line, ""]
3486
3487         # delete \begin_layout and \end_inset and replace \begin_inset with
3488         # "\begin_layout XXX". This works because we can only have one
3489         # paragraph in the caption inset: The old \end_layout will be recycled.
3490         del document.body[k]
3491         if document.body[k] == "":
3492             del document.body[k]
3493         del document.body[j]
3494         if document.body[j] == "":
3495             del document.body[j]
3496         document.body[i] = "\\begin_layout %s" % caption_dict[val]
3497         if document.body[i+1] == "":
3498             del document.body[i+1]
3499         i += 1
3500
3501
3502 def revert_fragileframe(document):
3503     " Reverts beamer FragileFrame layout to ERT "
3504     
3505     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3506     if document.textclass not in beamer_classes:
3507         return
3508
3509     i = 0
3510     while True:
3511         i = find_token(document.body, "\\begin_layout FragileFrame", i)
3512         if i == -1:
3513             return
3514         # Find end of sequence
3515         j = find_end_of_sequence(document.body, i)
3516         if j == -1:
3517             document.warning("Malformed LyX document. Cannot find end of FragileFrame sequence!")
3518             i += 1
3519             continue
3520         endseq = j
3521         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\begin{frame}")
3522         esubst = ["\\end_layout", "", "\\begin_layout Standard"] + put_cmd_in_ert("\\end{frame}")
3523         endseq = endseq + len(esubst) - len(document.body[j : j])
3524         if document.body[j] == "\\end_deeper":
3525             document.body[j : j] = ["\\end_deeper", ""] + esubst
3526         else:
3527             document.body[j : j] = esubst
3528         for q in range(i, j):
3529             if document.body[q] == "\\begin_layout FragileFrame":
3530                 document.body[q] = "\\begin_layout %s" % document.default_layout
3531         r = i
3532         while r < j:
3533             if document.body[r] == "\\begin_deeper":
3534                 s = find_end_of(document.body, r, "\\begin_deeper", "\\end_deeper")
3535                 if s != -1:
3536                     document.body[r] = ""
3537                     document.body[s] = ""
3538                     r = s
3539                     continue
3540             r = r + 1
3541         for p in range(1, 5):
3542             arg = find_token(document.body, "\\begin_inset Argument %d" % p, i, j)
3543             if arg != -1:
3544                 if p == 1:
3545                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3546                     endPlain = find_end_of_layout(document.body, beginPlain)
3547                     endInset = find_end_of_inset(document.body, arg)
3548                     content = document.body[beginPlain + 1 : endPlain]
3549                     # Adjust range end
3550                     j = j - len(document.body[arg : endInset + 1])
3551                     # Remove arg inset
3552                     del document.body[arg : endInset + 1]
3553                     subst += put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
3554                 elif p == 2:
3555                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3556                     endPlain = find_end_of_layout(document.body, beginPlain)
3557                     endInset = find_end_of_inset(document.body, arg)
3558                     content = document.body[beginPlain + 1 : endPlain]
3559                     # Adjust range end
3560                     j = j - len(document.body[arg : endInset + 1])
3561                     # Remove arg inset
3562                     del document.body[arg : endInset + 1]
3563                     subst += put_cmd_in_ert("[<") + content + put_cmd_in_ert(">]")
3564                 elif p == 3:
3565                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3566                     endPlain = find_end_of_layout(document.body, beginPlain)
3567                     endInset = find_end_of_inset(document.body, arg)
3568                     content = document.body[beginPlain + 1 : endPlain]
3569                     # Adjust range end
3570                     j = j - len(document.body[arg : endInset + 1])
3571                     # Remove arg inset
3572                     del document.body[arg : endInset + 1]
3573                     subst += put_cmd_in_ert("[fragile,") + content + put_cmd_in_ert("]")
3574                 elif p == 4:
3575                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3576                     endPlain = find_end_of_layout(document.body, beginPlain)
3577                     endInset = find_end_of_inset(document.body, arg)
3578                     content = document.body[beginPlain + 1 : endPlain]
3579                     # Adjust range end
3580                     j = j - len(document.body[arg : endInset + 1])
3581                     # Remove arg inset
3582                     del document.body[arg : endInset + 1]
3583                     subst += put_cmd_in_ert("{") + content + put_cmd_in_ert("}")
3584             elif p == 3:
3585                 subst += put_cmd_in_ert("[fragile]")
3586                     
3587         document.body[i : i + 1] = subst
3588         i = j
3589
3590
3591 def revert_newframes(document):
3592     " Reverts beamer Frame and PlainFrame layouts to old forms "
3593     
3594     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3595     if document.textclass not in beamer_classes:
3596         return
3597
3598     frame_dict = {
3599         "Frame" : "BeginFrame",
3600         "PlainFrame" : "BeginPlainFrame",
3601         }
3602
3603     rx = re.compile(r'^\\begin_layout (\S+)$')
3604     i = 0
3605     while True:
3606         i = find_token(document.body, "\\begin_layout", i)
3607         if i == -1:
3608             return
3609
3610         m = rx.match(document.body[i])
3611         val = ""
3612         if m:
3613             val = m.group(1)
3614         if val not in frame_dict.keys():
3615             i += 1
3616             continue
3617         # Find end of sequence
3618         j = find_end_of_sequence(document.body, i)
3619         if j == -1:
3620             document.warning("Malformed LyX document. Cannot find end of Frame sequence!")
3621             i += 1
3622             continue
3623         endseq = j
3624         subst = ["\\begin_layout %s" % frame_dict[val]]
3625         esubst = ["\\end_layout", "", "\\begin_layout EndFrame", "", "\\end_layout"]
3626         endseq = endseq + len(esubst) - len(document.body[j : j])
3627         if document.body[j] == "\\end_deeper":
3628             document.body[j : j] = ["\\end_deeper", ""] + esubst
3629         else:
3630             document.body[j : j] = esubst
3631         for q in range(i, j):
3632             if document.body[q] == "\\begin_layout %s" % val:
3633                 document.body[q] = "\\begin_layout %s" % document.default_layout
3634         r = i
3635         while r < j:
3636             if document.body[r] == "\\begin_deeper":
3637                 s = find_end_of(document.body, r, "\\begin_deeper", "\\end_deeper")
3638                 if s != -1:
3639                     document.body[r] = ""
3640                     document.body[s] = ""
3641                     r = s
3642                     continue
3643             r = r + 1
3644         l = find_end_of_layout(document.body, i)
3645         for p in range(1, 5):
3646             arg = find_token(document.body, "\\begin_inset Argument %d" % p, i, l)
3647             if arg != -1:
3648                 if p == 1:
3649                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3650                     endPlain = find_end_of_layout(document.body, beginPlain)
3651                     endInset = find_end_of_inset(document.body, arg)
3652                     content = document.body[beginPlain + 1 : endPlain]
3653                     # Adjust range end
3654                     l = l - len(document.body[arg : endInset + 1])
3655                     # Remove arg inset
3656                     del document.body[arg : endInset + 1]
3657                     subst += put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
3658                 elif p == 2:
3659                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3660                     endPlain = find_end_of_layout(document.body, beginPlain)
3661                     endInset = find_end_of_inset(document.body, arg)
3662                     content = document.body[beginPlain + 1 : endPlain]
3663                     # Adjust range end
3664                     l = l - len(document.body[arg : endInset + 1])
3665                     # Remove arg inset
3666                     del document.body[arg : endInset + 1]
3667                     subst += put_cmd_in_ert("[<") + content + put_cmd_in_ert(">]")
3668                 elif p == 3:
3669                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3670                     endPlain = find_end_of_layout(document.body, beginPlain)
3671                     endInset = find_end_of_inset(document.body, arg)
3672                     content = document.body[beginPlain + 1 : endPlain]
3673                     # Adjust range end
3674                     l = l - len(document.body[arg : endInset + 1])
3675                     # Remove arg inset
3676                     del document.body[arg : endInset + 1]
3677                     subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
3678                 elif p == 4:
3679                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3680                     endPlain = find_end_of_layout(document.body, beginPlain)
3681                     endInset = find_end_of_inset(document.body, arg)
3682                     content = document.body[beginPlain + 1 : endPlain]
3683                     # Adjust range end
3684                     l = l - len(document.body[arg : endInset + 1])
3685                     # Remove arg inset
3686                     del document.body[arg : endInset + 1]
3687                     subst += content
3688                     
3689         document.body[i : i + 1] = subst
3690         i = j
3691
3692 # known encodings that do not change their names (same LyX and LaTeX names)
3693 known_enc_tuple = ("auto", "default", "ansinew", "applemac", "armscii8", "ascii",
3694     "cp437", "cp437de", "cp850", "cp852", "cp855", "cp858", "cp862", "cp865", "cp866",
3695     "cp1250", "cp1251", "cp1252", "cp1255", "cp1256", "cp1257", "koi8-r", "koi8-u",
3696     "pt154", "pt254", "tis620-0", "utf8", "utf8x", "utf8-plain")
3697
3698 def convert_encodings(document):
3699     "Use the LyX names of the encodings instead of the LaTeX names."
3700     LaTeX2LyX_enc_dict = {
3701         "8859-6":     "iso8859-6",
3702         "8859-8":     "iso8859-8",
3703         "Bg5":        "big5",
3704         "euc":        "euc-jp-platex",
3705         "EUC-JP":     "euc-jp",
3706         "EUC-TW":     "euc-tw",
3707         "GB":         "euc-cn",
3708         "GBK":        "gbk",
3709         "iso88595":   "iso8859-5",
3710         "iso-8859-7": "iso8859-7",
3711         "JIS":        "jis",
3712         "jis":        "jis-platex",
3713         "KS":         "euc-kr",
3714         "l7xenc":     "iso8859-13",
3715         "latin1":     "iso8859-1",
3716         "latin2":     "iso8859-2",
3717         "latin3":     "iso8859-3",
3718         "latin4":     "iso8859-4",
3719         "latin5":     "iso8859-9",
3720         "latin9":     "iso8859-15",
3721         "latin10":    "iso8859-16",
3722         "SJIS":       "shift-jis",
3723         "sjis":       "shift-jis-platex",
3724         "UTF8":       "utf8-cjk"
3725     }
3726     i = find_token(document.header, "\\inputencoding" , 0)
3727     if i == -1:
3728         return
3729     val = get_value(document.header, "\\inputencoding", i)
3730     if val in LaTeX2LyX_enc_dict.keys():
3731         document.header[i] = "\\inputencoding %s" % LaTeX2LyX_enc_dict[val]
3732     elif val not in known_enc_tuple:
3733         document.warning("Ignoring unknown input encoding: `%s'" % val)
3734
3735
3736 def revert_encodings(document):
3737     """Revert to using the LaTeX names of the encodings instead of the LyX names.
3738     Also revert utf8-platex to sjis, the language default when using Japanese.
3739     """
3740     LyX2LaTeX_enc_dict = {
3741         "big5":             "Bg5",
3742         "euc-cn":           "GB",
3743         "euc-kr":           "KS",
3744         "euc-jp":           "EUC-JP",
3745         "euc-jp-platex":    "euc",
3746         "euc-tw":           "EUC-TW",
3747         "gbk":              "GBK",
3748         "iso8859-1":        "latin1",
3749         "iso8859-2":        "latin2",
3750         "iso8859-3":        "latin3",
3751         "iso8859-4":        "latin4",
3752         "iso8859-5":        "iso88595",
3753         "iso8859-6":        "8859-6",
3754         "iso8859-7":        "iso-8859-7",
3755         "iso8859-8":        "8859-8",
3756         "iso8859-9":        "latin5",
3757         "iso8859-13":       "l7xenc",
3758         "iso8859-15":       "latin9",
3759         "iso8859-16":       "latin10",
3760         "jis":              "JIS",
3761         "jis-platex":       "jis",
3762         "shift-jis":        "SJIS",
3763         "shift-jis-platex": "sjis",
3764         "utf8-cjk":         "UTF8",
3765         "utf8-platex":      "sjis"
3766     }
3767     i = find_token(document.header, "\\inputencoding" , 0)
3768     if i == -1:
3769         return
3770     val = get_value(document.header, "\\inputencoding", i)
3771     if val in LyX2LaTeX_enc_dict.keys():
3772         document.header[i] = "\\inputencoding %s" % LyX2LaTeX_enc_dict[val]
3773     elif val not in known_enc_tuple:
3774         document.warning("Ignoring unknown input encoding: `%s'" % val)
3775
3776
3777 def revert_IEEEtran_3(document):
3778   '''
3779   Reverts Flex Insets to TeX-code
3780   '''
3781   if document.textclass == "IEEEtran":
3782     h = 0
3783     i = 0
3784     j = 0
3785     while True:
3786       if h != -1:
3787         h = find_token(document.body, "\\begin_inset Flex Author Mark", h)
3788       if h != -1:
3789         endh = find_end_of_inset(document.body, h)
3790         document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
3791         document.body[h : h + 4] = put_cmd_in_ert("\\IEEEauthorrefmark{")
3792         h = h + 5
3793       if i != -1:
3794         i = find_token(document.body, "\\begin_inset Flex Author Name", i)
3795       if i != -1:
3796         endi = find_end_of_inset(document.body, i)
3797         document.body[endi - 2 : endi + 1] = put_cmd_in_ert("}")
3798         document.body[i : i + 4] = put_cmd_in_ert("\\IEEEauthorblockN{")
3799         i = i + 5
3800       if j != -1:
3801         j = find_token(document.body, "\\begin_inset Flex Author Affiliation", j)
3802       if j != -1:
3803         endj = find_end_of_inset(document.body, j)
3804         document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
3805         document.body[j : j + 4] = put_cmd_in_ert("\\IEEEauthorblockA{")
3806         j = j + 5
3807       if i == -1 and j == -1 and h == -1:
3808         return
3809
3810
3811 def revert_kurier_fonts(document):
3812   " Revert kurier font definition to LaTeX "
3813   
3814   i = find_token(document.header, "\\font_math", 0)
3815   if i != -1:
3816     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
3817       val = get_value(document.header, "\\font_math", i)
3818       if val == "kurier-math":
3819         add_to_preamble(document, "\\let\\Myrmdefault\\rmdefault\n" \
3820           "\\usepackage[math]{kurier}\n" \
3821           "\\renewcommand{\\rmdefault}{\\Myrmdefault}")
3822         document.header[i] = "\\font_math auto"
3823   
3824   if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
3825     kurier_fonts = ["kurier", "kurierc", "kurierl", "kurierlc"]
3826     k = find_token(document.header, "\\font_sans kurier", 0)
3827     if k != -1:
3828       sf = get_value(document.header, "\\font_sans", k)
3829       if sf in kurier_fonts:
3830         add_to_preamble(document, "\\renewcommand{\\sfdefault}{%s}" % sf)
3831         document.header[k] = "\\font_sans default"
3832
3833 def revert_iwona_fonts(document):
3834   " Revert iwona font definition to LaTeX "
3835   
3836   i = find_token(document.header, "\\font_math", 0)
3837   if i != -1:
3838     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
3839       val = get_value(document.header, "\\font_math", i)
3840       if val == "iwona-math":
3841         add_to_preamble(document, "\\let\\Myrmdefault\\rmdefault\n" \
3842           "\\usepackage[math]{iwona}\n" \
3843           "\\renewcommand{\\rmdefault}{\\Myrmdefault}")
3844         document.header[i] = "\\font_math auto"
3845   
3846   if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
3847     iwona_fonts = ["iwona", "iwonac", "iwonal", "iwonalc"]
3848     k = find_token(document.header, "\\font_sans iwona", 0)
3849     if k != -1:
3850       sf = get_value(document.header, "\\font_sans", k)
3851       if sf in iwona_fonts:
3852         add_to_preamble(document, "\\renewcommand{\\sfdefault}{%s}" % sf)
3853         document.header[k] = "\\font_sans default"
3854
3855
3856 def revert_new_libertines(document):
3857     " Revert new libertine font definition to LaTeX "
3858   
3859     if find_token(document.header, "\\use_non_tex_fonts true", 0) != -1:
3860         return
3861
3862     i = find_token(document.header, "\\font_typewriter libertine-mono", 0)
3863     if i != -1:
3864         preamble = "\\usepackage"
3865         sc = find_token(document.header, "\\font_tt_scale", 0)
3866         if sc != -1:
3867             scval = get_value(document.header, "\\font_tt_scale", sc)
3868             if scval != "100":
3869                 preamble += "[scale=%f]" % (float(scval) / 100)
3870                 document.header[sc] = "\\font_tt_scale 100"
3871         preamble += "{libertineMono-type1}"
3872         add_to_preamble(document, [preamble])
3873         document.header[i] = "\\font_typewriter default"
3874    
3875     k = find_token(document.header, "\\font_sans biolinum", 0)
3876     if k != -1:
3877         preamble = "\\usepackage"
3878         options = ""
3879         j = find_token(document.header, "\\font_osf true", 0)
3880         if j != -1:
3881             options += "osf"
3882         else:
3883             options += "lining"
3884         sc = find_token(document.header, "\\font_sf_scale", 0)
3885         if sc != -1:
3886             scval = get_value(document.header, "\\font_sf_scale", sc)
3887             if scval != "100":
3888                 options += ",scale=%f" % (float(scval) / 100)
3889                 document.header[sc] = "\\font_sf_scale 100"
3890         if options != "":
3891             preamble += "[" + options +"]"
3892         preamble += "{biolinum-type1}"
3893         add_to_preamble(document, [preamble])
3894         document.header[k] = "\\font_sans default"
3895
3896
3897 def convert_lyxframes(document):
3898     " Converts old beamer frames to new style "
3899     
3900     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3901     if document.textclass not in beamer_classes:
3902         return
3903    
3904     framebeg = ["BeginFrame", "BeginPlainFrame"]
3905     frameend = ["Frame", "PlainFrame", "EndFrame", "BeginFrame", "BeginPlainFrame", "AgainFrame",
3906                 "Section", "Section*", "Subsection", "Subsection*", "Subsubsection", "Subsubsection*"]
3907     for lay in framebeg:
3908         i = 0
3909         while True:
3910             i = find_token_exact(document.body, "\\begin_layout " + lay, i)
3911             if i == -1:
3912                 break
3913             parent = get_containing_layout(document.body, i)
3914             if parent == False or parent[1] != i:
3915                 document.warning("Wrong parent layout!")
3916                 i += 1
3917                 continue
3918             frametype = parent[0]
3919             j = parent[2]
3920             parbeg = parent[3]
3921             if i != -1:
3922                 # Step I: Convert ERT arguments
3923                 # FIXME: See restrictions in convert_beamerframeargs method
3924                 ertend = convert_beamerframeargs(document, i, parbeg)
3925                 if ertend == -1:
3926                     break
3927                 # Step II: Now rename the layout and convert the title to an argument
3928                 j = find_end_of_layout(document.body, i)
3929                 document.body[j : j + 1] = ['\\end_layout', '', '\\end_inset', '', '\\end_layout']
3930                 if lay == "BeginFrame":
3931                     document.body[i] = "\\begin_layout Frame"
3932                 else:
3933                     document.body[i] = "\\begin_layout PlainFrame"
3934                 document.body[ertend + 1 : ertend + 1] = ['\\begin_inset Argument 4',
3935                                                 'status open', '', '\\begin_layout Plain Layout']
3936                 # Step III: find real frame end
3937                 j = j + 8
3938                 jj = j
3939                 while True:
3940                     fend = find_token(document.body, "\\begin_layout", jj)
3941                     if fend == -1:
3942                         document.warning("Malformed LyX document: No real frame end!")
3943                         return
3944                     val = get_value(document.body, "\\begin_layout", fend)
3945                     if val not in frameend:
3946                         jj = fend + 1
3947                         continue
3948                     old = document.body[fend]
3949                     if val == frametype:
3950                         document.body[fend : fend] = ['\\end_deeper', '', '\\begin_layout Separator', '', '\\end_layout']
3951                     # consider explicit EndFrames between two identical frame types
3952                     elif val == "EndFrame":
3953                         nextlayout = find_token(document.body, "\\begin_layout", fend + 1)
3954                         if nextlayout != -1 and get_value(document.body, "\\begin_layout", nextlayout) == frametype:
3955                             document.body[fend : fend] = ['\\end_deeper', '', '\\begin_layout Separator', '', '\\end_layout']
3956                         else:
3957                             document.body[fend : fend] = ['\\end_deeper']
3958                     else:
3959                         document.body[fend : fend] = ['\\end_deeper']
3960                     document.body[j + 1 : j + 1] = ['', '\\begin_deeper']
3961                     break
3962             i = j
3963
3964
3965 def remove_endframes(document):
3966     " Remove deprecated beamer endframes "
3967     
3968     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3969     if document.textclass not in beamer_classes:
3970         return
3971    
3972     i = 0
3973     while True:
3974         i = find_token_exact(document.body, "\\begin_layout EndFrame", i)
3975         if i == -1:
3976             break
3977         j = find_end_of_layout(document.body, i)
3978         if j == -1:
3979             document.warning("Malformed LyX document: Missing \\end_layout to EndFrame")
3980             i += 1
3981             continue
3982         del document.body[i : j + 1]
3983
3984
3985 def revert_powerdot_flexes(document):
3986     " Reverts powerdot flex insets "
3987     
3988     if document.textclass != "powerdot":
3989         return
3990
3991     flexes = {"Onslide" : "\\onslide",
3992               "Onslide*" : "\\onslide*",
3993               "Onslide+" : "\\onslide+"}
3994     rx = re.compile(r'^\\begin_inset Flex (.+)$')
3995
3996     i = 0
3997     while True:
3998         i = find_token(document.body, "\\begin_inset Flex", i)
3999         if i == -1:
4000             return
4001         m = rx.match(document.body[i])
4002         if m:
4003             flextype = m.group(1)
4004             z = find_end_of_inset(document.body, i)
4005             if z == -1:
4006                 document.warning("Can't find end of Flex " + flextype + " inset.")
4007                 i += 1
4008                 continue
4009             if flextype in flexes:
4010                 pre = put_cmd_in_ert(flexes[flextype])
4011                 arg = find_token(document.body, "\\begin_inset Argument 1", i, z)
4012                 if arg != -1:
4013                     argend = find_end_of_inset(document.body, arg)
4014                     if argend == -1:
4015                         document.warning("Can't find end of Argument!")
4016                         i += 1
4017                         continue
4018                     # Find containing paragraph layout
4019                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
4020                     endPlain = find_end_of_layout(document.body, beginPlain)
4021                     argcontent = document.body[beginPlain + 1 : endPlain]
4022                     # Adjust range end
4023                     z = z - len(document.body[arg : argend + 1])
4024                     # Remove arg inset
4025                     del document.body[arg : argend + 1]
4026                     pre += put_cmd_in_ert("{") + argcontent + put_cmd_in_ert("}")
4027                 pre += put_cmd_in_ert("{")
4028                 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
4029                 endPlain = find_end_of_layout(document.body, beginPlain)
4030                 # Adjust range end
4031                 z = z - len(document.body[i : beginPlain + 1])
4032                 z += len(pre)
4033                 document.body[i : beginPlain + 1] = pre
4034                 post = put_cmd_in_ert("}")
4035                 document.body[z - 2 : z + 1] = post     
4036         i += 1
4037
4038
4039 def revert_powerdot_pause(document):
4040     " Reverts powerdot pause layout to ERT "
4041     
4042     if document.textclass != "powerdot":
4043         return
4044
4045     i = 0
4046     while True:
4047         i = find_token(document.body, "\\begin_layout Pause", i)
4048         if i == -1:
4049             return
4050         j = find_end_of_layout(document.body, i)
4051         if j == -1:
4052             document.warning("Malformed LyX document: Can't find end of Pause layout")
4053             i += 1
4054             continue
4055         endlay = j
4056         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\pause")
4057         for p in range(i, j):
4058             if p >= endlay:
4059                 break
4060             arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
4061             if arg != -1:
4062                 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
4063                 endPlain = find_end_of_layout(document.body, beginPlain)
4064                 endInset = find_end_of_inset(document.body, p)
4065                 content = document.body[beginPlain + 1 : endPlain]
4066                 # Adjust range end
4067                 endlay = endlay - len(document.body[p : endInset + 1])
4068                 # Remove arg inset
4069                 del document.body[p : endInset + 1]
4070                 subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
4071                     
4072         document.body[i : i + 1] = subst
4073         i = endlay
4074
4075
4076 def revert_powerdot_itemargs(document):
4077     " Reverts powerdot item arguments to ERT "
4078     
4079     if document.textclass != "powerdot":
4080         return
4081
4082     i = 0
4083     list_layouts = ["Itemize", "ItemizeType1", "Enumerate", "EnumerateType1"]
4084     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
4085
4086     while True:
4087         i = find_token(document.body, "\\begin_inset Argument", i)
4088         if i == -1:
4089             return
4090         # Find containing paragraph layout
4091         parent = get_containing_layout(document.body, i)
4092         if parent == False:
4093             document.warning("Malformed LyX document: Can't find parent paragraph layout")
4094             i += 1
4095             continue
4096         parbeg = parent[1]
4097         parend = parent[2]
4098         realparbeg = parent[3]
4099         layoutname = parent[0]
4100         realparend = parend
4101         for p in range(parbeg, parend):
4102             if p >= realparend:
4103                 i = realparend
4104                 break
4105             if layoutname in list_layouts:
4106                 m = rx.match(document.body[p])
4107                 if m:
4108                     argnr = m.group(1)
4109                     if argnr == "item:1":
4110                         j = find_end_of_inset(document.body, i)
4111                         # Find containing paragraph layout
4112                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
4113                         endPlain = find_end_of_layout(document.body, beginPlain)
4114                         content = document.body[beginPlain + 1 : endPlain]
4115                         del document.body[i:j+1]
4116                         subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
4117                         document.body[realparbeg : realparbeg] = subst
4118                     elif argnr == "item:2":
4119                         j = find_end_of_inset(document.body, i)
4120                         # Find containing paragraph layout
4121                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
4122                         endPlain = find_end_of_layout(document.body, beginPlain)
4123                         content = document.body[beginPlain + 1 : endPlain]
4124                         del document.body[i:j+1]
4125                         subst = put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
4126                         document.body[realparbeg : realparbeg] = subst
4127         
4128         i = realparend
4129
4130
4131 def revert_powerdot_columns(document):
4132     " Reverts powerdot twocolumn to TeX-code "
4133     if document.textclass != "powerdot":
4134         return
4135
4136     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
4137     i = 0
4138     while True:
4139         i = find_token(document.body, "\\begin_layout Twocolumn", i)
4140         if i == -1:
4141             return
4142         j = find_end_of_layout(document.body, i)
4143         if j == -1:
4144             document.warning("Malformed LyX document: Can't find end of Twocolumn layout")
4145             i += 1
4146             continue
4147         endlay = j
4148         document.body[j : j] = put_cmd_in_ert("}") + document.body[j : j]
4149         endlay += len(put_cmd_in_ert("}"))
4150         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\twocolumn")
4151         for p in range(i, j):
4152             if p >= endlay:
4153                 break
4154             m = rx.match(document.body[p])
4155             if m:
4156                 argnr = m.group(1)
4157                 if argnr == "1":
4158                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
4159                     endPlain = find_end_of_layout(document.body, beginPlain)
4160                     endInset = find_end_of_inset(document.body, p)
4161                     content = document.body[beginPlain + 1 : endPlain]
4162                     # Adjust range end
4163                     endlay = endlay - len(document.body[p : endInset + 1])
4164                     # Remove arg inset
4165                     del document.body[p : endInset + 1]
4166                     subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
4167                 elif argnr == "2":
4168                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
4169                     endPlain = find_end_of_layout(document.body, beginPlain)
4170                     endInset = find_end_of_inset(document.body, p)
4171                     content = document.body[beginPlain + 1 : endPlain]
4172                     # Adjust range end
4173                     endlay = endlay - len(document.body[p : endInset + 1])
4174                     # Remove arg inset
4175                     del document.body[p : endInset + 1]
4176                     subst += put_cmd_in_ert("{") + content + put_cmd_in_ert("}")
4177                     
4178         subst += put_cmd_in_ert("{")
4179         document.body[i : i + 1] = subst
4180         i = endlay
4181
4182
4183 def revert_mbox_fbox(document):
4184     'Convert revert mbox/fbox boxes to TeX-code'
4185     i = 0
4186     while True:
4187         i = find_token(document.body, "\\begin_inset Box", i)
4188         if i == -1:
4189             return
4190         j = find_token(document.body, "width", i)
4191         if j != i + 7:
4192             document.warning("Malformed LyX document: Can't find box width")
4193             return
4194         width = get_value(document.body, "width", j)
4195         k = find_end_of_inset(document.body, j)
4196         if k == -1:
4197             document.warning("Malformed LyX document: Can't find end of box inset")
4198             i += 1
4199             continue
4200         BeginLayout = find_token(document.body, "\\begin_layout Plain Layout", j)
4201         EndLayout = find_token(document.body, "\\end_layout", BeginLayout)
4202         # replace if width is ""
4203         if (width == '""'):
4204             document.body[EndLayout:k + 1] = put_cmd_in_ert("}")
4205             if document.body[i] == "\\begin_inset Box Frameless":
4206                 document.body[i:BeginLayout + 1] = put_cmd_in_ert("\\mbox{")
4207             if document.body[i] == "\\begin_inset Box Boxed":
4208                 document.body[i:BeginLayout + 1] = put_cmd_in_ert("\\fbox{")
4209         i += 1
4210
4211
4212 def revert_starred_caption(document):
4213     " Reverts unnumbered longtable caption insets "
4214     
4215     i = 0
4216     while True:
4217       i = find_token(document.body, "\\begin_inset Caption LongTableNoNumber", i)
4218       if i == -1:
4219           return
4220       # This is not equivalent, but since the caption inset is a full blown
4221       # text inset a true conversion to ERT is too difficult.
4222       document.body[i] = "\\begin_inset Caption Standard"
4223       i += 1
4224
4225
4226 def revert_forced_local_layout(document):
4227     i = 0
4228     while True:
4229         i = find_token(document.header, "\\begin_forced_local_layout", i)
4230         if i == -1:
4231             return
4232         j = find_end_of(document.header, i, "\\begin_forced_local_layout", "\\end_forced_local_layout")
4233         if j == -1:
4234             # this should not happen
4235             break
4236         regexp = re.compile(r'\s*forcelocal', re.IGNORECASE)
4237         k = find_re(document.header, regexp, i, j)
4238         while k != -1:
4239             del document.header[k]
4240             j = j - 1
4241             k = find_re(document.header, regexp, i, j)
4242         k = find_token(document.header, "\\begin_local_layout", 0)
4243         if k == -1:
4244             document.header[i] = "\\begin_local_layout"
4245             document.header[j] = "\\end_local_layout"
4246         else:
4247             l = find_end_of(document.header, k, "\\begin_local_layout", "\\end_local_layout")
4248             if j == -1:
4249                 # this should not happen
4250                 break
4251             lines = document.header[i+1 : j]
4252             if k > i:
4253                 document.header[k+1 : k+1] = lines
4254                 document.header[i   : j  ] = []
4255             else:
4256                 document.header[i   : j  ] = []
4257                 document.header[k+1 : k+1] = lines
4258
4259
4260 def revert_aa1(document):
4261   " Reverts InsetArguments of aa to TeX-code "
4262   if document.textclass == "aa":
4263     i = 0
4264     while True:
4265       if i != -1:
4266         i = find_token(document.body, "\\begin_layout Abstract (structured)", i)
4267       if i != -1:
4268         revert_Argument_to_TeX_brace(document, i, 0, 1, 4, False, False)
4269         i += 1
4270       if i == -1:
4271         return
4272
4273
4274 def revert_aa2(document):
4275   " Reverts InsetArguments of aa to TeX-code "
4276   if document.textclass == "aa":
4277     i = 0
4278     while True:
4279       if i != -1:
4280         i = find_token(document.body, "\\begin_layout Abstract (structured)", i)
4281       if i != -1:
4282         document.body[i] = "\\begin_layout Abstract"
4283         i += 1
4284       if i == -1:
4285         return
4286
4287
4288 def revert_tibetan(document):
4289     "Set the document language for Tibetan to English" 
4290
4291     if document.language == "tibetan":
4292         document.language = "english"
4293         i = find_token(document.header, "\\language", 0) 
4294         if i != -1: 
4295             document.header[i] = "\\language english" 
4296     j = 0
4297     while j < len(document.body): 
4298         j = find_token(document.body, "\\lang tibetan", j)
4299         if j != -1:
4300             document.body[j] = document.body[j].replace("\\lang tibetan", "\\lang english")
4301             j += 1
4302         else:
4303             j = len(document.body)
4304
4305
4306 #############
4307 #
4308 # Chunk stuff
4309 #
4310 #############
4311
4312 # The idea here is that we will have a sequence of chunk paragraphs.
4313 # We want to convert them to paragraphs in one or several chunk insets.
4314 # Individual chunks are terminated by the character @ on the last line.
4315 # This line will be discarded, and following lines are treated as new
4316 # chunks, which go into their own insets.
4317 # The first line of a chunk should look like: <<CONTENT>>=
4318 # We will discard the delimiters, and put the CONTENT into the
4319 # optional argument of the inset, if the CONTENT is non-empty.
4320 def convert_chunks(document):
4321     first_re = re.compile(r'<<(.*)>>=(.*)')
4322     file_pos = 0
4323     while True:
4324         # find start of a block of chunks
4325         i = find_token(document.body, "\\begin_layout Chunk", file_pos)
4326         if i == -1:
4327             return
4328         start = i
4329         end = -1
4330         contents = []
4331         chunk_started = False
4332
4333         while True:
4334             # process the one we just found
4335             j = find_end_of_layout(document.body, i)
4336             if j == -1:
4337                 document.warning("Malformed LyX documents. Can't find end of Chunk layout!")
4338                 # there is no point continuing, as we will run into the same error again.
4339                 return
4340             this_chunk = "".join(document.body[i + 1:j])
4341             
4342             # there may be empty lines between chunks
4343             # we just skip them.
4344             if not chunk_started:
4345                 if this_chunk != "":
4346                     # new chunk starts
4347                     chunk_started = True
4348             
4349             if chunk_started:
4350                 contents.append(document.body[i + 1:j])
4351
4352             # look for potential chunk terminator
4353             # on the last line of the chunk paragraph            
4354             if document.body[j - 1] == "@":
4355                 break
4356
4357             # look for subsequent chunk paragraph
4358             i = find_token(document.body, "\\begin_layout", j)
4359             if i == -1:
4360                 break
4361
4362             if get_value(document.body, "\\begin_layout", i) != "Chunk":
4363                 break
4364
4365         file_pos = end = j + 1
4366         
4367         # The last chunk should simply have an "@" in it
4368         # or at least end with "@" (can happen if @ is
4369         # preceded by a newline)
4370         lastpar = ''.join(contents[-1])
4371         if not lastpar.endswith("@"):
4372             document.warning("Unexpected chunk content: chunk not terminated by '@'!")
4373             continue
4374
4375         if lastpar == "@":
4376             # chunk par only contains "@". Just drop it.
4377             contents.pop()
4378         else:
4379             # chunk par contains more. Only drop the "@".
4380             contents[-1].pop()
4381
4382         # The first line should look like: <<CONTENT>>=
4383         # We want the CONTENT
4384         optarg = ' '.join(contents[0])
4385         optarg.strip()
4386         # We can already have real chunk content in
4387         # the first par (separated from the options by a newline).
4388         # We collect such stuff to re-insert it later.
4389         postoptstuff = []
4390         
4391         match = first_re.search(optarg)
4392         if match:
4393             optarg = match.groups()[0]
4394             if match.groups()[1] != "":
4395                 postopt = False
4396                 for c in contents[0]:
4397                     if c.endswith(">>="):
4398                         postopt = True
4399                         continue
4400                     if postopt:
4401                         postoptstuff.append(c)
4402             # We have stripped everything. This can be deleted.
4403             contents.pop(0)
4404
4405         newstuff = ['\\begin_layout Standard',
4406                     '\\begin_inset Flex Chunk',
4407                     'status open', '',
4408                     '\\begin_layout Plain Layout', '']
4409
4410         # If we have a non-empty optional argument, insert it.
4411         if match and optarg != "":
4412             newstuff.extend(
4413                 ['\\begin_inset Argument 1',
4414                  'status open', '',
4415                  '\\begin_layout Plain Layout',
4416                  optarg,
4417                  '\\end_layout', '',
4418                  '\\end_inset', ''])
4419
4420         # Since we already opened a Plain layout, the first paragraph
4421         # does not need to do that.
4422         did_one_par = False
4423         if postoptstuff:
4424             newstuff.extend(postoptstuff)
4425             newstuff.append('\\end_layout')
4426             did_one_par = True
4427         for c in contents:
4428             if did_one_par:
4429                 newstuff.extend(['', '\\begin_layout Plain Layout', ''])
4430             else:
4431                 did_one_par = True
4432             newstuff.extend(c)
4433             newstuff.append('\\end_layout')
4434
4435         newstuff.extend(['', '\\end_inset', '', '\\end_layout', ''])
4436
4437         document.body[start:end] = newstuff
4438
4439         file_pos += len(newstuff) - (end - start)
4440
4441
4442 def revert_chunks(document):
4443     i = 0
4444     while True:
4445         i = find_token(document.body, "\\begin_inset Flex Chunk", i)
4446         if i == -1:
4447             return
4448
4449         iend = find_end_of_inset(document.body, i)
4450         if iend == -1:
4451             document.warning("Can't find end of Chunk!")
4452             i += 1
4453             continue
4454
4455         # Look for optional argument
4456         have_optarg = False
4457         ostart = find_token(document.body, "\\begin_inset Argument 1", i, iend)
4458         if ostart != -1:
4459             oend = find_end_of_inset(document.body, ostart)
4460             k = find_token(document.body, "\\begin_layout Plain Layout", ostart, oend)
4461             if k == -1:
4462                 document.warning("Malformed LyX document: Can't find argument contents!")
4463             else:
4464                 m = find_end_of_layout(document.body, k)
4465                 optarg = "".join(document.body[k+1:m])
4466                 have_optarg = True
4467
4468             # We now remove the optional argument, so we have something
4469             # uniform on which to work
4470             document.body[ostart : oend + 1] = []
4471             # iend is now invalid
4472             iend = find_end_of_inset(document.body, i)
4473
4474         retval = get_containing_layout(document.body, i)
4475         if not retval:
4476             document.warning("Can't find containing layout for Chunk!")
4477             i = iend
4478             continue
4479         (lname, lstart, lend, pstart)  = retval
4480         # we now want to work through the various paragraphs, and collect their contents
4481         parlist = []
4482         k = i
4483         while True:
4484             k = find_token(document.body, "\\begin_layout Plain Layout", k, lend)
4485             if k == -1:
4486                 break
4487             j = find_end_of_layout(document.body, k)
4488             if j == -1:
4489                 document.warning("Can't find end of layout inside chunk!")
4490                 break
4491             parlist.append(document.body[k+1:j])
4492             k = j
4493         # we now need to wrap all of these paragraphs in chunks
4494         newlines = []
4495         if have_optarg:
4496             newlines.extend(["\\begin_layout Chunk", "", "<<" + optarg + ">>=", "\\end_layout", ""])
4497         for stuff in parlist:
4498             newlines.extend(["\\begin_layout Chunk"] + stuff + ["\\end_layout", ""])
4499         newlines.extend(["\\begin_layout Chunk", "", "@", "\\end_layout", ""])
4500         # replace old content with new content
4501         document.body[lstart : lend + 1] = newlines
4502         i = lstart + len(newlines)
4503         
4504
4505 ##
4506 # Conversion hub
4507 #
4508
4509 supported_versions = ["2.1.0","2.1"]
4510 convert = [
4511            [414, []],
4512            [415, [convert_undertilde]],
4513            [416, []],
4514            [417, [convert_japanese_encodings]],
4515            [418, [convert_justification]],
4516            [419, []],
4517            [420, [convert_biblio_style]],
4518            [421, [convert_longtable_captions]],
4519            [422, [convert_use_packages]],
4520            [423, [convert_use_mathtools]],
4521            [424, [convert_cite_engine_type]],
4522            # No convert_cancel, since cancel will be loaded automatically
4523            # in format 425 without any possibility to switch it off.
4524            # This has been fixed in format 464.
4525            [425, []],
4526            [426, []],
4527            [427, []],
4528            [428, [convert_cell_rotation]],
4529            [429, [convert_table_rotation]],
4530            [430, [convert_listoflistings]],
4531            [431, [convert_use_amssymb]],
4532            [432, []],
4533            [433, [convert_armenian]],
4534            [434, []],
4535            [435, []],
4536            [436, []],
4537            [437, []],
4538            [438, []],
4539            [439, []],
4540            [440, []],
4541            [441, [convert_mdnomath]],
4542            [442, []],
4543            [443, []],
4544            [444, []],
4545            [445, []],
4546            [446, [convert_latexargs]],
4547            [447, [convert_IEEEtran, convert_AASTeX, convert_AGUTeX, convert_IJMP, convert_SIGPLAN, convert_SIGGRAPH, convert_EuropeCV, convert_Initials, convert_ModernCV]],
4548            [448, [convert_literate]],
4549            [449, []],
4550            [450, []],
4551            [451, [convert_beamerargs, convert_againframe_args, convert_corollary_args, convert_quote_args]],
4552            [452, [convert_beamerblocks]],
4553            [453, [convert_use_stmaryrd]],
4554            [454, [convert_overprint]],
4555            [455, []],
4556            [456, [convert_epigraph]],
4557            [457, [convert_use_stackrel]],
4558            [458, [convert_captioninsets, convert_captionlayouts]],
4559            [459, []],
4560            [460, []],
4561            [461, []],
4562            [462, []],
4563            [463, [convert_encodings]],
4564            [464, [convert_use_cancel]],
4565            [465, [convert_lyxframes, remove_endframes]],
4566            [466, []],
4567            [467, []],
4568            [468, []],
4569            [469, []],
4570            [470, []],
4571            [471, [convert_cite_engine_type_default]],
4572            [472, []],
4573            [473, []],
4574            [474, [convert_chunks]],
4575           ]
4576
4577 revert =  [
4578            [473, [revert_chunks]],
4579            [472, [revert_tibetan]],
4580            [471, [revert_aa1,revert_aa2]],
4581            [470, [revert_cite_engine_type_default]],
4582            [469, [revert_forced_local_layout]],
4583            [468, [revert_starred_caption]],
4584            [467, [revert_mbox_fbox]],
4585            [466, [revert_iwona_fonts]],
4586            [465, [revert_powerdot_flexes, revert_powerdot_pause, revert_powerdot_itemargs, revert_powerdot_columns]],
4587            [464, []],
4588            [463, [revert_use_cancel]],
4589            [462, [revert_encodings]],
4590            [461, [revert_new_libertines]],
4591            [460, [revert_kurier_fonts]],
4592            [459, [revert_IEEEtran_3]],
4593            [458, [revert_fragileframe, revert_newframes]],
4594            [457, [revert_captioninsets, revert_captionlayouts]],
4595            [456, [revert_use_stackrel]],
4596            [455, [revert_epigraph]],
4597            [454, [revert_frametitle]],
4598            [453, [revert_overprint]],
4599            [452, [revert_use_stmaryrd]],
4600            [451, [revert_beamerblocks]],
4601            [450, [revert_beamerargs, revert_beamerargs2, revert_beamerargs3, revert_beamerflex]],
4602            [449, [revert_garamondx, revert_garamondx_newtxmath]],
4603            [448, [revert_itemargs]],
4604            [447, [revert_literate]],
4605            [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]],
4606            [445, [revert_latexargs]],
4607            [444, [revert_uop]],
4608            [443, [revert_biolinum]],
4609            [442, []],
4610            [441, [revert_newtxmath]],
4611            [440, [revert_mdnomath]],
4612            [439, [revert_mathfonts]],
4613            [438, [revert_minionpro]],
4614            [437, [revert_ipadeco, revert_ipachar]],
4615            [436, [revert_texgyre]],
4616            [435, [revert_mathdesign]],
4617            [434, [revert_txtt]],
4618            [433, [revert_libertine]],
4619            [432, [revert_armenian]],
4620            [431, [revert_languages, revert_ancientgreek]],
4621            [430, [revert_use_amssymb]],
4622            [429, [revert_listoflistings]],
4623            [428, [revert_table_rotation]],
4624            [427, [revert_cell_rotation]],
4625            [426, [revert_tipa]],
4626            [425, [revert_verbatim]],
4627            [424, [revert_cancel]],
4628            [423, [revert_cite_engine_type]],
4629            [422, [revert_use_mathtools]],
4630            [421, [revert_use_packages]],
4631            [420, [revert_longtable_captions]],
4632            [419, [revert_biblio_style]],
4633            [418, [revert_australian]],
4634            [417, [revert_justification]],
4635            [416, [revert_japanese_encodings]],
4636            [415, [revert_negative_space, revert_math_spaces]],
4637            [414, [revert_undertilde]],
4638            [413, [revert_visible_space]]
4639           ]
4640
4641
4642 if __name__ == "__main__":
4643     pass