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