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