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