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