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