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