]> git.lyx.org Git - lyx.git/blob - lib/lyx2lyx/lyx_2_1.py
Merge branch 'master' of git.lyx.org:lyx
[lyx.git] / lib / lyx2lyx / lyx_2_1.py
1 # -*- coding: utf-8 -*-
2 # This file is part of lyx2lyx
3 # -*- coding: utf-8 -*-
4 # Copyright (C) 2011 The LyX team
5 #
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19
20 """ Convert files to the file format generated by LyX 2.1"""
21
22 import re, string
23 import unicodedata
24 import sys, os
25
26 # Uncomment only what you need to import, please.
27
28 from parser_tools import count_pars_in_inset, del_token, find_token, find_token_exact, \
29     find_token_backwards, find_end_of, find_end_of_inset, find_end_of_layout, \
30     find_end_of_sequence, find_re, get_option_value, get_containing_layout, \
31     get_value, get_quoted_value, set_option_value
32
33 #from parser_tools import find_token, find_end_of, find_tokens, \
34   #find_end_of_inset, find_end_of_layout, \
35   #is_in_inset, del_token, check_token
36
37 from lyx2lyx_tools import add_to_preamble, put_cmd_in_ert, get_ert
38
39 #from lyx2lyx_tools import insert_to_preamble, \
40 #  lyx2latex, latex_length, revert_flex_inset, \
41 #  revert_font_attrs, hex2ratio, str2bool
42
43 ####################################################################
44 # Private helper functions
45
46 #def remove_option(lines, m, option):
47     #''' removes option from line m. returns whether we did anything '''
48     #l = lines[m].find(option)
49     #if l == -1:
50         #return False
51     #val = lines[m][l:].split('"')[1]
52     #lines[m] = lines[m][:l - 1] + lines[m][l+len(option + '="' + val + '"'):]
53     #return True
54
55
56 def revert_Argument_to_TeX_brace(document, line, endline, n, nmax, environment, opt):
57     '''
58     Reverts an InsetArgument to TeX-code
59     usage:
60     revert_Argument_to_TeX_brace(document, LineOfBegin, LineOfEnd, StartArgument, EndArgument, isEnvironment, isOpt)
61     LineOfBegin is the line  of the \begin_layout or \begin_inset statement
62     LineOfEnd is the line  of the \end_layout or \end_inset statement, if "0" is given, the end of the file is used instead
63     StartArgument is the number of the first argument that needs to be converted
64     EndArgument is the number of the last argument that needs to be converted or the last defined one
65     isEnvironment must be true, if the layout is for a LaTeX environment
66     isOpt must be true, if the argument is an optional one
67     '''
68     lineArg = 0
69     wasOpt = False
70     while lineArg != -1 and n < nmax + 1:
71       lineArg = find_token(document.body, "\\begin_inset Argument " + str(n), line)
72       if lineArg > endline and endline != 0:
73         return wasOpt
74       if lineArg != -1:
75         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
76         # we have to assure that no other inset is in the Argument
77         beginInset = find_token(document.body, "\\begin_inset", beginPlain)
78         endInset = find_token(document.body, "\\end_inset", beginPlain)
79         k = beginPlain + 1
80         l = k
81         while beginInset < endInset and beginInset != -1:
82           beginInset = find_token(document.body, "\\begin_inset", k)
83           endInset = find_token(document.body, "\\end_inset", l)
84           k = beginInset + 1
85           l = endInset + 1
86         if environment == False:
87           if opt == False:
88             document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}{")
89             del(document.body[lineArg : beginPlain + 1])
90             wasOpt = False
91           else:
92             document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
93             document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("[")
94             wasOpt = True
95         else:
96           document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
97           document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("{")
98           wasOpt = False
99         n += 1
100     return wasOpt
101
102
103 def convert_TeX_brace_to_Argument(document, line, n, nmax, inset, environment, opt):
104     '''
105     Converts TeX code for mandatory arguments to an InsetArgument
106     The conversion of TeX code for optional arguments must be done with another routine
107     !!! Be careful if the braces are different in your case as expected here:
108     - "}{" separates mandatory arguments of commands
109     - "}" + "{" separates mandatory arguments of commands
110     - "}" + " " + "{" separates mandatory arguments of commands
111     - { and } surround a mandatory argument of an environment
112     usage:
113     convert_TeX_brace_to_Argument(document, LineOfBeginLayout/Inset, StartArgument, EndArgument, isInset, isEnvironment, isOpt)
114     LineOfBeginLayout/Inset is the line  of the \begin_layout or \begin_inset statement
115     StartArgument is the number of the first ERT that needs to be converted
116     EndArgument is the number of the last ERT that needs to be converted
117     isInset must be true, if braces inside an InsetLayout needs to be converted
118     isEnvironment must be true, if the layout is for a LaTeX environment
119     isOpt must be true, if the argument is an optional one
120     
121     Todo: this routine can currently handle only one mandatory argument of environments
122     '''
123     lineERT = line
124     endn = line
125     loop = 1
126     while lineERT != -1 and n < nmax + 1:
127       lineERT = find_token(document.body, "\\begin_inset ERT", lineERT)
128       if environment == False and lineERT != -1:
129         bracePair = -1
130         if opt:
131           bracePair = find_token(document.body, "][", lineERT)
132         else:
133           bracePair = find_token(document.body, "}{", lineERT)
134         # assure that the "}{" is in this ERT
135         if bracePair == lineERT + 5:
136           end = find_token(document.body, "\\end_inset", bracePair)
137           document.body[lineERT : end + 1] = ["\\end_layout", "", "\\end_inset"]
138           if loop == 1:
139             # in the case that n > 1 we have optional arguments before
140             # therefore detect them if any
141             if n > 1:
142               # first check if there is an argument
143               lineArg = find_token(document.body, "\\begin_inset Argument", line)
144               if lineArg < lineERT and lineArg != -1:
145                 # we have an argument, so now search backwards for its end
146                 # we must now assure that we don't find other insets like e.g. a newline
147                 endInsetArg = lineERT
148                 endLayoutArg = endInsetArg
149                 while endInsetArg != endLayoutArg + 2 and endInsetArg != -1:
150                   endInsetArg = endInsetArg - 1
151                   endLayoutArg = endInsetArg
152                   endInsetArg = find_token_backwards(document.body, "\\end_inset", endInsetArg)
153                   endLayoutArg = find_token_backwards(document.body, "\\end_layout", endLayoutArg)
154                 line = endInsetArg + 1
155             if inset == False:
156               document.body[line + 1 : line + 1] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
157             else:
158               document.body[line + 4 : line + 4] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
159           else:
160             document.body[endn : endn] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
161           n += 1
162           endn = end
163           loop = loop + 1
164         # now check the case that we have "}" + "{" in two ERTs
165         else:
166           endBrace = -1
167           if opt:
168             endBrace = find_token(document.body, "]", lineERT)
169           else:
170             endBrace = find_token(document.body, "}", lineERT)
171           if endBrace == lineERT + 5:
172             beginBrace = -1
173             if opt:
174               beginBrace = find_token(document.body, "[", endBrace)
175             else:
176               beginBrace = find_token(document.body, "{", endBrace)
177             # assure that the ERTs are consecutive (11 or 12 depending if there is a space between the ERTs or not)
178             if beginBrace == endBrace + 11 or beginBrace == endBrace + 12:
179               end = find_token(document.body, "\\end_inset", beginBrace)
180               document.body[lineERT : end + 1] = ["\\end_layout", "", "\\end_inset"]
181               if loop == 1:
182                 # in the case that n > 1 we have optional arguments before
183                 # therefore detect them if any
184                 if n > 1:
185                   # first check if there is an argument
186                   lineArg = find_token(document.body, "\\begin_inset Argument", line)
187                   if lineArg < lineERT and lineArg != -1:
188                     # we have an argument, so now search backwards for its end
189                     # we must now assure that we don't find other insets like e.g. a newline
190                     endInsetArg = lineERT
191                     endLayoutArg = endInsetArg
192                     while endInsetArg != endLayoutArg + 2 and endInsetArg != -1:
193                       endInsetArg = endInsetArg - 1
194                       endLayoutArg = endInsetArg
195                       endInsetArg = find_token_backwards(document.body, "\\end_inset", endInsetArg)
196                       endLayoutArg = find_token_backwards(document.body, "\\end_layout", endLayoutArg)
197                     line = endInsetArg + 1
198                 if inset == False:
199                   document.body[line + 1 : line + 1] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
200                 else:
201                   document.body[line + 4 : line + 4] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
202               else:
203                 document.body[endn : endn] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
204               n += 1
205               loop += 1
206               # set the line where the next argument will be inserted
207               if beginBrace == endBrace + 11:
208                 endn = end - 11
209               else:
210                 endn = end - 12
211             else:
212               lineERT += 1
213           else:
214             lineERT += 1
215       if environment == True and lineERT != -1:
216         opening = -1
217         if opt:
218           opening = find_token(document.body, "[", lineERT)
219         else:
220           opening = find_token(document.body, "{", lineERT)
221         if opening == lineERT + 5: # assure that the "{" is in this ERT
222           end = find_token(document.body, "\\end_inset", opening)
223           document.body[lineERT : end + 1] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
224           n += 1
225           lineERT2 = find_token(document.body, "\\begin_inset ERT", lineERT)
226           closing = -1
227           if opt:
228             closing = find_token(document.body, "]", lineERT)
229           else:
230             closing = find_token(document.body, "}", lineERT2)
231           if closing == lineERT2 + 5: # assure that the "}" is in this ERT
232             end2 = find_token(document.body, "\\end_inset", closing)
233             document.body[lineERT2 : end2 + 1] = ["\\end_layout", "", "\\end_inset"]
234         else:
235           lineERT += 1
236
237
238 ###############################################################################
239 ###
240 ### Conversion and reversion routines
241 ###
242 ###############################################################################
243
244 def revert_visible_space(document):
245     "Revert InsetSpace visible into its ERT counterpart"
246     i = 0
247     while True:
248       i = find_token(document.body, "\\begin_inset space \\textvisiblespace{}", i)
249       if i == -1:
250         return
251       end = find_end_of_inset(document.body, i)
252       subst = put_cmd_in_ert("\\textvisiblespace{}")
253       document.body[i:end + 1] = subst
254
255
256 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                     # Find the last ERT in this paragraph (which might also be the first)
2982                     lastertbeg = find_token_backwards(document.body, "\\begin_inset ERT", j)
2983                     if lastertbeg == -1:
2984                         document.warning("Last ERT not found!")
2985                         break
2986                     lastertend = find_end_of_inset(document.body, lastertbeg)
2987                     if lastertend == -1:
2988                         document.warning("End of last ERT not found!")
2989                         break
2990                     ertcontlastline = lastertend - 3
2991                     while True:
2992                         if document.body[ertcontfirstline].startswith("<"):
2993                             # This is an overlay specification
2994                             # strip off the <
2995                             document.body[ertcontfirstline] = document.body[ertcontfirstline][1:]
2996                             if document.body[ertcontlastline].endswith(">"):
2997                                 # strip off the >
2998                                 document.body[ertcontlastline] = document.body[ertcontlastline][:-1]
2999                                 # Convert to ArgInset
3000                                 document.body[parbeg] = "\\begin_inset Argument 1"
3001                             elif document.body[ertcontlastline].endswith("}"):
3002                                 # strip off the }
3003                                 document.body[ertcontlastline] = document.body[ertcontlastline][:-1]
3004                                 # divide the args
3005                                 ertcontdivline = ertcontfirstline
3006                                 tok = document.body[ertcontdivline].find('>{')
3007                                 if tok == -1:
3008                                     regexp = re.compile(r'.*>\{', re.IGNORECASE)
3009                                     ertcontdivline = find_re(document.body, regexp, ertcontfirstline, ertcontlastline)
3010                                     tok = document.body[ertcontdivline].find('>{')
3011                                 if tok != -1:
3012                                     if ertcontfirstline < ertcontlastline:
3013                                         # Multiline ERT. Might contain TeX code.  Embrace in ERT.
3014                                         document.body[ertcontlastline : ertcontlastline + 1] = [
3015                                                                             document.body[ertcontlastline], '\\end_layout', '', '\\end_inset']
3016                                         document.body[ertcontdivline : ertcontdivline + 1] = [document.body[ertcontdivline][:tok],
3017                                                                             '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 2',
3018                                                                             'status collapsed', '', '\\begin_layout Plain Layout',
3019                                                                             '\\begin_inset ERT', '', 'status open' '', '\\begin_layout Plain Layout',
3020                                                                             document.body[ertcontdivline][tok + 2:]]
3021                                     else:
3022                                         document.body[ertcontdivline : ertcontdivline + 1] = [document.body[ertcontdivline][:tok],
3023                                                                             '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 2',
3024                                                                             'status collapsed', '', '\\begin_layout Plain Layout',
3025                                                                             document.body[ertcontdivline][tok + 2:]]
3026                             # Convert to ArgInset
3027                             document.body[parbeg] = "\\begin_inset Argument 1"
3028                         elif document.body[ertcontfirstline].startswith("{"):
3029                             # This is the block title
3030                             if document.body[ertcontlastline].endswith("}"):
3031                                 # strip off the braces
3032                                 document.body[ertcontfirstline] = document.body[ertcontfirstline][1:]
3033                                 document.body[ertcontlastline] = document.body[ertcontlastline][:-1]
3034                                 if ertcontfirstline < ertcontlastline:
3035                                     # Multiline ERT. Might contain TeX code.  Embrace in ERT.
3036                                     document.body[parend : parend + 1] = [
3037                                                                         document.body[parend], '\\end_layout', '', '\\end_inset']
3038                                     document.body[parbeg : parbeg + 1] = ['\\begin_inset Argument 2',
3039                                                                         'status collapsed', '', '\\begin_layout Plain Layout',
3040                                                                         '\\begin_inset ERT', '']
3041                                 else:
3042                                     # Convert to ArgInset
3043                                     document.body[parbeg] = "\\begin_inset Argument 2"
3044                             elif count_pars_in_inset(document.body, ertcontfirstline) > 1:
3045                                 # Multipar ERT. Skip this.
3046                                 break
3047                             else:
3048                                 convert_TeX_brace_to_Argument(document, i, 2, 2, False, True, False)
3049                         else:
3050                             break
3051                         j = find_end_of_layout(document.body, i)
3052                         if j == -1:
3053                             document.warning("end of layout not found!")
3054                         k = find_token(document.body, "\\begin_inset Argument", i, j)
3055                         if k == -1:
3056                             document.warning("InsetArgument not found!")
3057                             break
3058                         l = find_end_of_inset(document.body, k)
3059                         m = find_token(document.body, "\\begin_inset ERT", l, j)
3060                         if m == -1:
3061                             break
3062                         ertcontfirstline = m + 5
3063                         parbeg = m
3064             i = j
3065
3066
3067 def convert_overprint(document):
3068     " Convert old beamer overprint layouts to ERT "
3069     
3070     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3071     if document.textclass not in beamer_classes:
3072         return
3073
3074     i = 0
3075     while True:
3076         i = find_token(document.body, "\\begin_layout Overprint", i)
3077         if i == -1:
3078             return
3079         # Find end of sequence
3080         j = find_end_of_sequence(document.body, i)
3081         if j == -1:
3082             document.warning("Malformed LyX document. Cannot find end of Overprint sequence!")
3083             i += 1
3084             continue
3085         endseq = j
3086         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\begin{overprint}")
3087         esubst = list()
3088         if document.body[j] == "\\end_deeper":
3089             esubst = ["", "\\begin_layout Standard"] + put_cmd_in_ert("\\end{overprint}") + ["\\end_layout"]
3090         else:
3091             esubst = ["\\end_layout", "", "\\begin_layout Standard"] + put_cmd_in_ert("\\end{overprint}") + ["\\end_layout"]
3092         endseq = endseq + len(esubst) - len(document.body[j : j])
3093         document.body[j : j] = esubst
3094         argbeg = find_token(document.body, "\\begin_inset Argument 1", i, j)
3095         if argbeg != -1:
3096             argend = find_end_of_layout(document.body, argbeg)
3097             if argend == -1:
3098                 document.warning("Malformed LyX document. Cannot find end of Overprint argument!")
3099                 i += 1
3100                 continue
3101             beginPlain = find_token(document.body, "\\begin_layout Plain Layout", argbeg)
3102             endPlain = find_end_of_layout(document.body, beginPlain)
3103             content = document.body[beginPlain + 1 : endPlain]
3104             # Adjust range end
3105             endseq = endseq - len(document.body[argbeg : argend + 1])
3106             # Remove arg inset
3107             del document.body[argbeg : argend + 1]
3108             subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
3109             
3110         endseq = endseq - len(document.body[i : i])
3111         document.body[i : i] = subst + ["\\end_layout"]
3112         endseq += len(subst)
3113         
3114         for p in range(i, endseq):
3115             if document.body[p] == "\\begin_layout Overprint":
3116                 document.body[p] = "\\begin_layout Standard"
3117
3118         i = endseq
3119
3120
3121 def revert_overprint(document):
3122     " Revert old beamer overprint layouts to ERT "
3123     
3124     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3125     if document.textclass not in beamer_classes:
3126         return
3127
3128     i = 0
3129     while True:
3130         i = find_token(document.body, "\\begin_layout Overprint", i)
3131         if i == -1:
3132             return
3133         # Find end of sequence
3134         j = find_end_of_sequence(document.body, i)
3135         if j == -1:
3136             document.warning("Malformed LyX document. Cannot find end of Overprint sequence!")
3137             i += 1
3138             continue
3139         endseq = j
3140         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\begin{overprint}")
3141         esubst = ["\\end_layout", "", "\\begin_layout Standard"] + put_cmd_in_ert("\\end{overprint}")
3142         endseq = endseq + len(esubst) - len(document.body[j : j])
3143         if document.body[j] == "\\end_deeper":
3144             document.body[j : j] = ["\\end_deeper", ""] + esubst
3145         else:
3146             document.body[j : j] = esubst
3147         r = i
3148         while r < j:
3149             if document.body[r] == "\\begin_deeper":
3150                 s = find_end_of(document.body, r, "\\begin_deeper", "\\end_deeper")
3151                 if s != -1:
3152                     document.body[r] = ""
3153                     document.body[s] = ""
3154                     r = s
3155                     continue
3156             r = r + 1
3157         argbeg = find_token(document.body, "\\begin_inset Argument 1", i, j)
3158         if argbeg != -1:
3159             argend = find_end_of_inset(document.body, argbeg)
3160             if argend == -1:
3161                 document.warning("Malformed LyX document. Cannot find end of Overprint argument!")
3162                 i += 1
3163                 continue
3164             beginPlain = find_token(document.body, "\\begin_layout Plain Layout", argbeg)
3165             endPlain = find_end_of_layout(document.body, beginPlain)
3166             content = document.body[beginPlain + 1 : endPlain]
3167             # Adjust range end
3168             endseq = endseq - len(document.body[argbeg : argend])
3169             # Remove arg inset
3170             del document.body[argbeg : argend + 1]
3171             subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
3172             
3173         endseq = endseq - len(document.body[i : i])
3174         document.body[i : i] = subst + ["\\end_layout"]
3175         endseq += len(subst)
3176      
3177         p = i
3178         while True:
3179             if p >= endseq:
3180                 break
3181             if document.body[p] == "\\begin_layout Overprint":
3182                 q = find_end_of_layout(document.body, p)
3183                 if q == -1:
3184                     document.warning("Malformed LyX document. Cannot find end of Overprint layout!")
3185                     p += 1
3186                     continue
3187                 subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\onslide")
3188                 argbeg = find_token(document.body, "\\begin_inset Argument item:1", p, q)
3189                 if argbeg != -1:
3190                     argend = find_end_of_inset(document.body, argbeg)
3191                     if argend == -1:
3192                         document.warning("Malformed LyX document. Cannot find end of Overprint item argument!")
3193                         p += 1
3194                         continue
3195                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", argbeg)
3196                     endPlain = find_end_of_layout(document.body, beginPlain)
3197                     content = document.body[beginPlain + 1 : endPlain]
3198                     # Adjust range end
3199                     endseq = endseq - len(document.body[argbeg : argend + 1])
3200                     # Remove arg inset
3201                     del document.body[argbeg : argend + 1]
3202                     subst += put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
3203                 endseq = endseq - len(document.body[p : p + 1]) + len(subst)
3204                 document.body[p : p + 1] = subst
3205             p = p + 1
3206
3207         i = endseq
3208
3209
3210 def revert_frametitle(document):
3211     " Reverts beamer frametitle layout to ERT "
3212     
3213     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3214     if document.textclass not in beamer_classes:
3215         return
3216
3217     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
3218     i = 0
3219     while True:
3220         i = find_token(document.body, "\\begin_layout FrameTitle", i)
3221         if i == -1:
3222             return
3223         j = find_end_of_layout(document.body, i)
3224         if j == -1:
3225             document.warning("Malformed LyX document: Can't find end of FrameTitle layout")
3226             i += 1
3227             continue
3228         endlay = j
3229         document.body[j : j] = put_cmd_in_ert("}") + document.body[j : j]
3230         endlay += len(put_cmd_in_ert("}"))
3231         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\frametitle")
3232         for p in range(i, j):
3233             if p >= endlay:
3234                 break
3235             m = rx.match(document.body[p])
3236             if m:
3237                 argnr = m.group(1)
3238                 if argnr == "1":
3239                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
3240                     endPlain = find_end_of_layout(document.body, beginPlain)
3241                     endInset = find_end_of_inset(document.body, p)
3242                     content = document.body[beginPlain + 1 : endPlain]
3243                     # Adjust range end
3244                     endlay = endlay - len(document.body[p : endInset + 1])
3245                     # Remove arg inset
3246                     del document.body[p : endInset + 1]
3247                     subst += put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
3248                 elif argnr == "2":
3249                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
3250                     endPlain = find_end_of_layout(document.body, beginPlain)
3251                     endInset = find_end_of_inset(document.body, p)
3252                     content = document.body[beginPlain + 1 : endPlain]
3253                     # Adjust range end
3254                     endlay = endlay - len(document.body[p : endInset + 1])
3255                     # Remove arg inset
3256                     del document.body[p : endInset + 1]
3257                     subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
3258                     
3259         subst += put_cmd_in_ert("{")
3260         document.body[i : i + 1] = subst
3261         i = endlay
3262
3263
3264 def convert_epigraph(document):
3265     " Converts memoir epigraph to new syntax "
3266     
3267     if document.textclass != "memoir":
3268         return
3269
3270     i = 0
3271     while True:
3272         i = find_token(document.body, "\\begin_layout Epigraph", i)
3273         if i == -1:
3274             return
3275         j = find_end_of_layout(document.body, i)
3276         if j == -1:
3277             document.warning("Malformed LyX document: Can't find end of Epigraph layout")
3278             i += 1
3279             continue
3280         endlay = j
3281         subst = list()
3282         ert = find_token(document.body, "\\begin_inset ERT", i, j)
3283         if ert != -1:
3284             endInset = find_end_of_inset(document.body, ert)
3285             beginPlain = find_token(document.body, "\\begin_layout Plain Layout", ert)
3286             endPlain = find_end_of_layout(document.body, beginPlain)
3287             ertcont = beginPlain + 2
3288             if document.body[ertcont] == "}{":
3289                 # strip off the <
3290                 # Convert to ArgInset
3291                 endlay = endlay - 2 * len(document.body[j])
3292                 begsubst = ['\\begin_inset Argument post:1', 'status collapsed', '',
3293                             '\\begin_layout Plain Layout']
3294                 endsubst = ['\\end_layout', '', '\\end_inset', '', document.body[j]]
3295                 document.body[j : j + 1] = endsubst
3296                 document.body[endInset + 1 : endInset + 1] = begsubst
3297                 # Adjust range end
3298                 endlay += len(begsubst) + len(endsubst)
3299                 endlay = endlay - len(document.body[ert : endInset + 1])
3300                 del document.body[ert : endInset + 1]
3301                     
3302         i = endlay
3303
3304
3305 def revert_epigraph(document):
3306     " Reverts memoir epigraph argument to ERT "
3307     
3308     if document.textclass != "memoir":
3309         return
3310
3311     i = 0
3312     while True:
3313         i = find_token(document.body, "\\begin_layout Epigraph", i)
3314         if i == -1:
3315             return
3316         j = find_end_of_layout(document.body, i)
3317         if j == -1:
3318             document.warning("Malformed LyX document: Can't find end of Epigraph layout")
3319             i += 1
3320             continue
3321         endlay = j
3322         subst = list()
3323         p = find_token(document.body, "\\begin_layout Argument post:1", i, j)
3324         if p != -1:
3325             beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
3326             endPlain = find_end_of_layout(document.body, beginPlain)
3327             endInset = find_end_of_inset(document.body, p)
3328             content = document.body[beginPlain + 1 : endPlain]
3329             # Adjust range end
3330             endlay = endlay - len(document.body[p : endInset + 1])
3331             # Remove arg inset
3332             del document.body[p : endInset + 1]
3333             subst += put_cmd_in_ert("}{") + content
3334         else:
3335             subst += put_cmd_in_ert("}{")
3336                     
3337         document.body[j : j] = subst + document.body[j : j]
3338         i = endlay
3339
3340
3341 def convert_captioninsets(document):
3342     " Converts caption insets to new syntax "
3343     
3344     i = 0
3345     while True:
3346       i = find_token(document.body, "\\begin_inset Caption", i)
3347       if i == -1:
3348           return
3349       document.body[i] = "\\begin_inset Caption Standard"
3350       i += 1
3351
3352
3353 def revert_captioninsets(document):
3354     " Reverts caption insets to old syntax "
3355     
3356     i = 0
3357     while True:
3358       i = find_token(document.body, "\\begin_inset Caption Standard", i)
3359       if i == -1:
3360           return
3361       document.body[i] = "\\begin_inset Caption"
3362       i += 1
3363
3364
3365 def convert_captionlayouts(document):
3366     " Convert caption layouts to caption insets. "
3367
3368     caption_dict = {
3369         "Captionabove":  "Above",
3370         "Captionbelow":  "Below",
3371         "FigCaption"  :  "FigCaption",
3372         "Table_Caption" :  "Table",
3373         "CenteredCaption" : "Centered",
3374         "Bicaption" : "Bicaption",
3375         }
3376
3377     i = 0
3378     while True:
3379         i = find_token(document.body, "\\begin_layout", i)
3380         if i == -1:
3381             return
3382         val = get_value(document.body, "\\begin_layout", i)
3383         if val in caption_dict.keys():
3384             j = find_end_of_layout(document.body, i)
3385             if j == -1:
3386                 document.warning("Malformed LyX document: Missing `\\end_layout'.")
3387                 return
3388
3389             document.body[j:j] = ["\\end_layout", "", "\\end_inset", "", ""]
3390             document.body[i:i+1] = ["\\begin_layout %s" % document.default_layout,
3391                                     "\\begin_inset Caption %s" % caption_dict[val], "",
3392                                     "\\begin_layout %s" % document.default_layout]
3393         i += 1
3394
3395
3396 def revert_captionlayouts(document):
3397     " Revert caption insets to caption layouts. "
3398     
3399     caption_dict = {
3400         "Above" : "Captionabove",
3401         "Below" : "Captionbelow",
3402         "FigCaption"  :  "FigCaption",
3403         "Table" : "Table_Caption",
3404         "Centered" : "CenteredCaption",
3405         "Bicaption" : "Bicaption",
3406         }
3407     
3408     i = 0
3409     rx = re.compile(r'^\\begin_inset Caption (\S+)$')
3410     while True:
3411         i = find_token(document.body, "\\begin_inset Caption", i)
3412         if i == -1:
3413             return
3414
3415         m = rx.match(document.body[i])
3416         val = ""
3417         if m:
3418             val = m.group(1)
3419         if val not in caption_dict.keys():
3420             i += 1
3421             continue
3422         
3423         # We either need to delete the previous \begin_layout line, or we
3424         # need to end the previous layout if this inset is not in the first
3425         # position of the paragraph.
3426         layout_before = find_token_backwards(document.body, "\\begin_layout", i)
3427         if layout_before == -1:
3428             document.warning("Malformed LyX document: Missing `\\begin_layout'.")
3429             return
3430         layout_line = document.body[layout_before]
3431         del_layout_before = True
3432         l = layout_before + 1
3433         while l < i:
3434             if document.body[l] != "":
3435                 del_layout_before = False
3436                 break
3437             l = l + 1
3438         if del_layout_before:
3439             del document.body[layout_before:i]
3440             i = layout_before
3441         else:
3442             document.body[i:i] = ["\\end_layout", ""]
3443             i = i + 2
3444
3445         # Find start of layout in the inset and end of inset
3446         j = find_token(document.body, "\\begin_layout", i)
3447         if j == -1:
3448             document.warning("Malformed LyX document: Missing `\\begin_layout'.")
3449             return
3450         k = find_end_of_inset(document.body, i)
3451         if k == -1:
3452             document.warning("Malformed LyX document: Missing `\\end_inset'.")
3453             return
3454
3455         # We either need to delete the following \end_layout line, or we need
3456         # to restart the old layout if this inset is not at the paragraph end.
3457         layout_after = find_token(document.body, "\\end_layout", k)
3458         if layout_after == -1:
3459             document.warning("Malformed LyX document: Missing `\\end_layout'.")
3460             return
3461         del_layout_after = True
3462         l = k + 1
3463         while l < layout_after:
3464             if document.body[l] != "":
3465                 del_layout_after = False
3466                 break
3467             l = l + 1
3468         if del_layout_after:
3469             del document.body[k+1:layout_after+1]
3470         else:
3471             document.body[k+1:k+1] = [layout_line, ""]
3472
3473         # delete \begin_layout and \end_inset and replace \begin_inset with
3474         # "\begin_layout XXX". This works because we can only have one
3475         # paragraph in the caption inset: The old \end_layout will be recycled.
3476         del document.body[k]
3477         if document.body[k] == "":
3478             del document.body[k]
3479         del document.body[j]
3480         if document.body[j] == "":
3481             del document.body[j]
3482         document.body[i] = "\\begin_layout %s" % caption_dict[val]
3483         if document.body[i+1] == "":
3484             del document.body[i+1]
3485         i += 1
3486
3487
3488 def revert_fragileframe(document):
3489     " Reverts beamer FragileFrame layout to ERT "
3490     
3491     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3492     if document.textclass not in beamer_classes:
3493         return
3494
3495     i = 0
3496     while True:
3497         i = find_token(document.body, "\\begin_layout FragileFrame", i)
3498         if i == -1:
3499             return
3500         # Find end of sequence
3501         j = find_end_of_sequence(document.body, i)
3502         if j == -1:
3503             document.warning("Malformed LyX document. Cannot find end of FragileFrame sequence!")
3504             i += 1
3505             continue
3506         endseq = j
3507         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\begin{frame}")
3508         esubst = ["\\end_layout", "", "\\begin_layout Standard"] + put_cmd_in_ert("\\end{frame}")
3509         endseq = endseq + len(esubst) - len(document.body[j : j])
3510         if document.body[j] == "\\end_deeper":
3511             document.body[j : j] = ["\\end_deeper", ""] + esubst
3512         else:
3513             document.body[j : j] = esubst
3514         for q in range(i, j):
3515             if document.body[q] == "\\begin_layout FragileFrame":
3516                 document.body[q] = "\\begin_layout %s" % document.default_layout
3517         r = i
3518         while r < j:
3519             if document.body[r] == "\\begin_deeper":
3520                 s = find_end_of(document.body, r, "\\begin_deeper", "\\end_deeper")
3521                 if s != -1:
3522                     document.body[r] = ""
3523                     document.body[s] = ""
3524                     r = s
3525                     continue
3526             r = r + 1
3527         for p in range(1, 5):
3528             arg = find_token(document.body, "\\begin_inset Argument %d" % p, i, j)
3529             if arg != -1:
3530                 if p == 1:
3531                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3532                     endPlain = find_end_of_layout(document.body, beginPlain)
3533                     endInset = find_end_of_inset(document.body, arg)
3534                     content = document.body[beginPlain + 1 : endPlain]
3535                     # Adjust range end
3536                     j = j - len(document.body[arg : endInset + 1])
3537                     # Remove arg inset
3538                     del document.body[arg : endInset + 1]
3539                     subst += put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
3540                 elif p == 2:
3541                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3542                     endPlain = find_end_of_layout(document.body, beginPlain)
3543                     endInset = find_end_of_inset(document.body, arg)
3544                     content = document.body[beginPlain + 1 : endPlain]
3545                     # Adjust range end
3546                     j = j - len(document.body[arg : endInset + 1])
3547                     # Remove arg inset
3548                     del document.body[arg : endInset + 1]
3549                     subst += put_cmd_in_ert("[<") + content + put_cmd_in_ert(">]")
3550                 elif p == 3:
3551                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3552                     endPlain = find_end_of_layout(document.body, beginPlain)
3553                     endInset = find_end_of_inset(document.body, arg)
3554                     content = document.body[beginPlain + 1 : endPlain]
3555                     # Adjust range end
3556                     j = j - len(document.body[arg : endInset + 1])
3557                     # Remove arg inset
3558                     del document.body[arg : endInset + 1]
3559                     subst += put_cmd_in_ert("[fragile,") + content + put_cmd_in_ert("]")
3560                 elif p == 4:
3561                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3562                     endPlain = find_end_of_layout(document.body, beginPlain)
3563                     endInset = find_end_of_inset(document.body, arg)
3564                     content = document.body[beginPlain + 1 : endPlain]
3565                     # Adjust range end
3566                     j = j - len(document.body[arg : endInset + 1])
3567                     # Remove arg inset
3568                     del document.body[arg : endInset + 1]
3569                     subst += put_cmd_in_ert("{") + content + put_cmd_in_ert("}")
3570             elif p == 3:
3571                 subst += put_cmd_in_ert("[fragile]")
3572                     
3573         document.body[i : i + 1] = subst
3574         i = j
3575
3576
3577 def revert_newframes(document):
3578     " Reverts beamer Frame and PlainFrame layouts to old forms "
3579     
3580     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3581     if document.textclass not in beamer_classes:
3582         return
3583
3584     frame_dict = {
3585         "Frame" : "BeginFrame",
3586         "PlainFrame" : "BeginPlainFrame",
3587         }
3588
3589     rx = re.compile(r'^\\begin_layout (\S+)$')
3590     i = 0
3591     while True:
3592         i = find_token(document.body, "\\begin_layout", i)
3593         if i == -1:
3594             return
3595
3596         m = rx.match(document.body[i])
3597         val = ""
3598         if m:
3599             val = m.group(1)
3600         if val not in frame_dict.keys():
3601             i += 1
3602             continue
3603         # Find end of sequence
3604         j = find_end_of_sequence(document.body, i)
3605         if j == -1:
3606             document.warning("Malformed LyX document. Cannot find end of Frame sequence!")
3607             i += 1
3608             continue
3609         endseq = j
3610         subst = ["\\begin_layout %s" % frame_dict[val]]
3611         esubst = ["\\end_layout", "", "\\begin_layout EndFrame", "", "\\end_layout"]
3612         endseq = endseq + len(esubst) - len(document.body[j : j])
3613         if document.body[j] == "\\end_deeper":
3614             document.body[j : j] = ["\\end_deeper", ""] + esubst
3615         else:
3616             document.body[j : j] = esubst
3617         for q in range(i, j):
3618             if document.body[q] == "\\begin_layout %s" % val:
3619                 document.body[q] = "\\begin_layout %s" % document.default_layout
3620         r = i
3621         while r < j:
3622             if document.body[r] == "\\begin_deeper":
3623                 s = find_end_of(document.body, r, "\\begin_deeper", "\\end_deeper")
3624                 if s != -1:
3625                     document.body[r] = ""
3626                     document.body[s] = ""
3627                     r = s
3628                     continue
3629             r = r + 1
3630         l = find_end_of_layout(document.body, i)
3631         for p in range(1, 5):
3632             arg = find_token(document.body, "\\begin_inset Argument %d" % p, i, l)
3633             if arg != -1:
3634                 if p == 1:
3635                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3636                     endPlain = find_end_of_layout(document.body, beginPlain)
3637                     endInset = find_end_of_inset(document.body, arg)
3638                     content = document.body[beginPlain + 1 : endPlain]
3639                     # Adjust range end
3640                     l = l - len(document.body[arg : endInset + 1])
3641                     # Remove arg inset
3642                     del document.body[arg : endInset + 1]
3643                     subst += put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
3644                 elif p == 2:
3645                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3646                     endPlain = find_end_of_layout(document.body, beginPlain)
3647                     endInset = find_end_of_inset(document.body, arg)
3648                     content = document.body[beginPlain + 1 : endPlain]
3649                     # Adjust range end
3650                     l = l - len(document.body[arg : endInset + 1])
3651                     # Remove arg inset
3652                     del document.body[arg : endInset + 1]
3653                     subst += put_cmd_in_ert("[<") + content + put_cmd_in_ert(">]")
3654                 elif p == 3:
3655                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3656                     endPlain = find_end_of_layout(document.body, beginPlain)
3657                     endInset = find_end_of_inset(document.body, arg)
3658                     content = document.body[beginPlain + 1 : endPlain]
3659                     # Adjust range end
3660                     l = l - len(document.body[arg : endInset + 1])
3661                     # Remove arg inset
3662                     del document.body[arg : endInset + 1]
3663                     subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
3664                 elif p == 4:
3665                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3666                     endPlain = find_end_of_layout(document.body, beginPlain)
3667                     endInset = find_end_of_inset(document.body, arg)
3668                     content = document.body[beginPlain + 1 : endPlain]
3669                     # Adjust range end
3670                     l = l - len(document.body[arg : endInset + 1])
3671                     # Remove arg inset
3672                     del document.body[arg : endInset + 1]
3673                     subst += content
3674                     
3675         document.body[i : i + 1] = subst
3676         i = j
3677
3678 # known encodings that do not change their names (same LyX and LaTeX names)
3679 known_enc_tuple = ("auto", "default", "ansinew", "applemac", "armscii8", "ascii",
3680     "cp437", "cp437de", "cp850", "cp852", "cp855", "cp858", "cp862", "cp865", "cp866",
3681     "cp1250", "cp1251", "cp1252", "cp1255", "cp1256", "cp1257", "koi8-r", "koi8-u",
3682     "pt154", "pt254", "tis620-0", "utf8", "utf8x", "utf8-plain")
3683
3684 def convert_encodings(document):
3685     "Use the LyX names of the encodings instead of the LaTeX names."
3686     LaTeX2LyX_enc_dict = {
3687         "8859-6":     "iso8859-6",
3688         "8859-8":     "iso8859-8",
3689         "Bg5":        "big5",
3690         "euc":        "euc-jp-platex",
3691         "EUC-JP":     "euc-jp",
3692         "EUC-TW":     "euc-tw",
3693         "GB":         "euc-cn",
3694         "GBK":        "gbk",
3695         "iso88595":   "iso8859-5",
3696         "iso-8859-7": "iso8859-7",
3697         "JIS":        "jis",
3698         "jis":        "jis-platex",
3699         "KS":         "euc-kr",
3700         "l7xenc":     "iso8859-13",
3701         "latin1":     "iso8859-1",
3702         "latin2":     "iso8859-2",
3703         "latin3":     "iso8859-3",
3704         "latin4":     "iso8859-4",
3705         "latin5":     "iso8859-9",
3706         "latin9":     "iso8859-15",
3707         "latin10":    "iso8859-16",
3708         "SJIS":       "shift-jis",
3709         "sjis":       "shift-jis-platex",
3710         "UTF8":       "utf8-cjk"
3711     }
3712     i = find_token(document.header, "\\inputencoding" , 0)
3713     if i == -1:
3714         return
3715     val = get_value(document.header, "\\inputencoding", i)
3716     if val in LaTeX2LyX_enc_dict.keys():
3717         document.header[i] = "\\inputencoding %s" % LaTeX2LyX_enc_dict[val]
3718     elif val not in known_enc_tuple:
3719         document.warning("Ignoring unknown input encoding: `%s'" % val)
3720
3721
3722 def revert_encodings(document):
3723     """Revert to using the LaTeX names of the encodings instead of the LyX names.
3724     Also revert utf8-platex to sjis, the language default when using Japanese.
3725     """
3726     LyX2LaTeX_enc_dict = {
3727         "big5":             "Bg5",
3728         "euc-cn":           "GB",
3729         "euc-kr":           "KS",
3730         "euc-jp":           "EUC-JP",
3731         "euc-jp-platex":    "euc",
3732         "euc-tw":           "EUC-TW",
3733         "gbk":              "GBK",
3734         "iso8859-1":        "latin1",
3735         "iso8859-2":        "latin2",
3736         "iso8859-3":        "latin3",
3737         "iso8859-4":        "latin4",
3738         "iso8859-5":        "iso88595",
3739         "iso8859-6":        "8859-6",
3740         "iso8859-7":        "iso-8859-7",
3741         "iso8859-8":        "8859-8",
3742         "iso8859-9":        "latin5",
3743         "iso8859-13":       "l7xenc",
3744         "iso8859-15":       "latin9",
3745         "iso8859-16":       "latin10",
3746         "jis":              "JIS",
3747         "jis-platex":       "jis",
3748         "shift-jis":        "SJIS",
3749         "shift-jis-platex": "sjis",
3750         "utf8-cjk":         "UTF8",
3751         "utf8-platex":      "sjis"
3752     }
3753     i = find_token(document.header, "\\inputencoding" , 0)
3754     if i == -1:
3755         return
3756     val = get_value(document.header, "\\inputencoding", i)
3757     if val in LyX2LaTeX_enc_dict.keys():
3758         document.header[i] = "\\inputencoding %s" % LyX2LaTeX_enc_dict[val]
3759     elif val not in known_enc_tuple:
3760         document.warning("Ignoring unknown input encoding: `%s'" % val)
3761
3762
3763 def revert_IEEEtran_3(document):
3764   '''
3765   Reverts Flex Insets to TeX-code
3766   '''
3767   if document.textclass == "IEEEtran":
3768     h = 0
3769     i = 0
3770     j = 0
3771     while True:
3772       if h != -1:
3773         h = find_token(document.body, "\\begin_inset Flex Author Mark", h)
3774       if h != -1:
3775         endh = find_end_of_inset(document.body, h)
3776         document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
3777         document.body[h : h + 4] = put_cmd_in_ert("\\IEEEauthorrefmark{")
3778         h = h + 5
3779       if i != -1:
3780         i = find_token(document.body, "\\begin_inset Flex Author Name", i)
3781       if i != -1:
3782         endi = find_end_of_inset(document.body, i)
3783         document.body[endi - 2 : endi + 1] = put_cmd_in_ert("}")
3784         document.body[i : i + 4] = put_cmd_in_ert("\\IEEEauthorblockN{")
3785         i = i + 5
3786       if j != -1:
3787         j = find_token(document.body, "\\begin_inset Flex Author Affiliation", j)
3788       if j != -1:
3789         endj = find_end_of_inset(document.body, j)
3790         document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
3791         document.body[j : j + 4] = put_cmd_in_ert("\\IEEEauthorblockA{")
3792         j = j + 5
3793       if i == -1 and j == -1 and h == -1:
3794         return
3795
3796
3797 def revert_kurier_fonts(document):
3798   " Revert kurier font definition to LaTeX "
3799   
3800   i = find_token(document.header, "\\font_math", 0)
3801   if i != -1:
3802     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
3803       val = get_value(document.header, "\\font_math", i)
3804       if val == "kurier-math":
3805         add_to_preamble(document, "\\let\\Myrmdefault\\rmdefault\n" \
3806           "\\usepackage[math]{kurier}\n" \
3807           "\\renewcommand{\\rmdefault}{\\Myrmdefault}")
3808         document.header[i] = "\\font_math auto"
3809   
3810   if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
3811     kurier_fonts = ["kurier", "kurierc", "kurierl", "kurierlc"]
3812     k = find_token(document.header, "\\font_sans kurier", 0)
3813     if k != -1:
3814       sf = get_value(document.header, "\\font_sans", k)
3815       if sf in kurier_fonts:
3816         add_to_preamble(document, "\\renewcommand{\\sfdefault}{%s}" % sf)
3817         document.header[k] = "\\font_sans default"
3818
3819 def revert_iwona_fonts(document):
3820   " Revert iwona font definition to LaTeX "
3821   
3822   i = find_token(document.header, "\\font_math", 0)
3823   if i != -1:
3824     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
3825       val = get_value(document.header, "\\font_math", i)
3826       if val == "iwona-math":
3827         add_to_preamble(document, "\\let\\Myrmdefault\\rmdefault\n" \
3828           "\\usepackage[math]{iwona}\n" \
3829           "\\renewcommand{\\rmdefault}{\\Myrmdefault}")
3830         document.header[i] = "\\font_math auto"
3831   
3832   if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
3833     iwona_fonts = ["iwona", "iwonac", "iwonal", "iwonalc"]
3834     k = find_token(document.header, "\\font_sans iwona", 0)
3835     if k != -1:
3836       sf = get_value(document.header, "\\font_sans", k)
3837       if sf in iwona_fonts:
3838         add_to_preamble(document, "\\renewcommand{\\sfdefault}{%s}" % sf)
3839         document.header[k] = "\\font_sans default"
3840
3841
3842 def revert_new_libertines(document):
3843     " Revert new libertine font definition to LaTeX "
3844   
3845     if find_token(document.header, "\\use_non_tex_fonts true", 0) != -1:
3846         return
3847
3848     i = find_token(document.header, "\\font_typewriter libertine-mono", 0)
3849     if i != -1:
3850         preamble = "\\usepackage"
3851         sc = find_token(document.header, "\\font_tt_scale", 0)
3852         if sc != -1:
3853             scval = get_value(document.header, "\\font_tt_scale", sc)
3854             if scval != "100":
3855                 preamble += "[scale=%f]" % (float(scval) / 100)
3856                 document.header[sc] = "\\font_tt_scale 100"
3857         preamble += "{libertineMono-type1}"
3858         add_to_preamble(document, [preamble])
3859         document.header[i] = "\\font_typewriter default"
3860    
3861     k = find_token(document.header, "\\font_sans biolinum", 0)
3862     if k != -1:
3863         preamble = "\\usepackage"
3864         options = ""
3865         j = find_token(document.header, "\\font_osf true", 0)
3866         if j != -1:
3867             options += "osf"
3868         else:
3869             options += "lining"
3870         sc = find_token(document.header, "\\font_sf_scale", 0)
3871         if sc != -1:
3872             scval = get_value(document.header, "\\font_sf_scale", sc)
3873             if scval != "100":
3874                 options += ",scale=%f" % (float(scval) / 100)
3875                 document.header[sc] = "\\font_sf_scale 100"
3876         if options != "":
3877             preamble += "[" + options +"]"
3878         preamble += "{biolinum-type1}"
3879         add_to_preamble(document, [preamble])
3880         document.header[k] = "\\font_sans default"
3881
3882
3883 def convert_lyxframes(document):
3884     " Converts old beamer frames to new style "
3885     
3886     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3887     if document.textclass not in beamer_classes:
3888         return
3889    
3890     framebeg = ["BeginFrame", "BeginPlainFrame"]
3891     frameend = ["Frame", "PlainFrame", "EndFrame", "BeginFrame", "BeginPlainFrame", "AgainFrame",
3892                 "Section", "Section*", "Subsection", "Subsection*", "Subsubsection", "Subsubsection*"]
3893     for lay in framebeg:
3894         i = 0
3895         while True:
3896             i = find_token_exact(document.body, "\\begin_layout " + lay, i)
3897             if i == -1:
3898                 break
3899             parent = get_containing_layout(document.body, i)
3900             if parent == False or parent[1] != i:
3901                 document.warning("Wrong parent layout!")
3902                 i += 1
3903                 continue
3904             frametype = parent[0]
3905             j = parent[2]
3906             parbeg = parent[3]
3907             if i != -1:
3908                 # Step I: Convert ERT arguments
3909                 # FIXME: See restrictions in convert_beamerframeargs method
3910                 ertend = convert_beamerframeargs(document, i, parbeg)
3911                 if ertend == -1:
3912                     break
3913                 # Step II: Now rename the layout and convert the title to an argument
3914                 j = find_end_of_layout(document.body, i)
3915                 document.body[j : j + 1] = ['\\end_layout', '', '\\end_inset', '', '\\end_layout']
3916                 if lay == "BeginFrame":
3917                     document.body[i] = "\\begin_layout Frame"
3918                 else:
3919                     document.body[i] = "\\begin_layout PlainFrame"
3920                 document.body[ertend + 1 : ertend + 1] = ['\\begin_inset Argument 4',
3921                                                 'status open', '', '\\begin_layout Plain Layout']
3922                 # Step III: find real frame end
3923                 j = j + 8
3924                 jj = j
3925                 while True:
3926                     fend = find_token(document.body, "\\begin_layout", jj)
3927                     if fend == -1:
3928                         document.warning("Malformed LyX document: No real frame end!")
3929                         return
3930                     val = get_value(document.body, "\\begin_layout", fend)
3931                     if val not in frameend:
3932                         jj = fend + 1
3933                         continue
3934                     old = document.body[fend]
3935                     if val == frametype:
3936                         document.body[fend : fend] = ['\\end_deeper', '', '\\begin_layout Separator', '', '\\end_layout']
3937                     # consider explicit EndFrames between two identical frame types
3938                     elif val == "EndFrame":
3939                         nextlayout = find_token(document.body, "\\begin_layout", fend + 1)
3940                         if nextlayout != -1 and get_value(document.body, "\\begin_layout", nextlayout) == frametype:
3941                             document.body[fend : fend] = ['\\end_deeper', '', '\\begin_layout Separator', '', '\\end_layout']
3942                         else:
3943                             document.body[fend : fend] = ['\\end_deeper']
3944                     else:
3945                         document.body[fend : fend] = ['\\end_deeper']
3946                     document.body[j + 1 : j + 1] = ['', '\\begin_deeper']
3947                     break
3948             i = j
3949
3950
3951 def remove_endframes(document):
3952     " Remove deprecated beamer endframes "
3953     
3954     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3955     if document.textclass not in beamer_classes:
3956         return
3957    
3958     i = 0
3959     while True:
3960         i = find_token_exact(document.body, "\\begin_layout EndFrame", i)
3961         if i == -1:
3962             break
3963         j = find_end_of_layout(document.body, i)
3964         if j == -1:
3965             document.warning("Malformed LyX document: Missing \\end_layout to EndFrame")
3966             i += 1
3967             continue
3968         del document.body[i : j + 1]
3969
3970
3971 def revert_powerdot_flexes(document):
3972     " Reverts powerdot flex insets "
3973     
3974     if document.textclass != "powerdot":
3975         return
3976
3977     flexes = {"Onslide" : "\\onslide",
3978               "Onslide*" : "\\onslide*",
3979               "Onslide+" : "\\onslide+"}
3980     rx = re.compile(r'^\\begin_inset Flex (.+)$')
3981
3982     i = 0
3983     while True:
3984         i = find_token(document.body, "\\begin_inset Flex", i)
3985         if i == -1:
3986             return
3987         m = rx.match(document.body[i])
3988         if m:
3989             flextype = m.group(1)
3990             z = find_end_of_inset(document.body, i)
3991             if z == -1:
3992                 document.warning("Can't find end of Flex " + flextype + " inset.")
3993                 i += 1
3994                 continue
3995             if flextype in flexes:
3996                 pre = put_cmd_in_ert(flexes[flextype])
3997                 arg = find_token(document.body, "\\begin_inset Argument 1", i, z)
3998                 if arg != -1:
3999                     argend = find_end_of_inset(document.body, arg)
4000                     if argend == -1:
4001                         document.warning("Can't find end of Argument!")
4002                         i += 1
4003                         continue
4004                     # Find containing paragraph layout
4005                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
4006                     endPlain = find_end_of_layout(document.body, beginPlain)
4007                     argcontent = document.body[beginPlain + 1 : endPlain]
4008                     # Adjust range end
4009                     z = z - len(document.body[arg : argend + 1])
4010                     # Remove arg inset
4011                     del document.body[arg : argend + 1]
4012                     pre += put_cmd_in_ert("{") + argcontent + put_cmd_in_ert("}")
4013                 pre += put_cmd_in_ert("{")
4014                 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
4015                 endPlain = find_end_of_layout(document.body, beginPlain)
4016                 # Adjust range end
4017                 z = z - len(document.body[i : beginPlain + 1])
4018                 z += len(pre)
4019                 document.body[i : beginPlain + 1] = pre
4020                 post = put_cmd_in_ert("}")
4021                 document.body[z - 2 : z + 1] = post     
4022         i += 1
4023
4024
4025 def revert_powerdot_pause(document):
4026     " Reverts powerdot pause layout to ERT "
4027     
4028     if document.textclass != "powerdot":
4029         return
4030
4031     i = 0
4032     while True:
4033         i = find_token(document.body, "\\begin_layout Pause", i)
4034         if i == -1:
4035             return
4036         j = find_end_of_layout(document.body, i)
4037         if j == -1:
4038             document.warning("Malformed LyX document: Can't find end of Pause layout")
4039             i += 1
4040             continue
4041         endlay = j
4042         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\pause")
4043         for p in range(i, j):
4044             if p >= endlay:
4045                 break
4046             arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
4047             if arg != -1:
4048                 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
4049                 endPlain = find_end_of_layout(document.body, beginPlain)
4050                 endInset = find_end_of_inset(document.body, p)
4051                 content = document.body[beginPlain + 1 : endPlain]
4052                 # Adjust range end
4053                 endlay = endlay - len(document.body[p : endInset + 1])
4054                 # Remove arg inset
4055                 del document.body[p : endInset + 1]
4056                 subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
4057                     
4058         document.body[i : i + 1] = subst
4059         i = endlay
4060
4061
4062 def revert_powerdot_itemargs(document):
4063     " Reverts powerdot item arguments to ERT "
4064     
4065     if document.textclass != "powerdot":
4066         return
4067
4068     i = 0
4069     list_layouts = ["Itemize", "ItemizeType1", "Enumerate", "EnumerateType1"]
4070     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
4071
4072     while True:
4073         i = find_token(document.body, "\\begin_inset Argument", i)
4074         if i == -1:
4075             return
4076         # Find containing paragraph layout
4077         parent = get_containing_layout(document.body, i)
4078         if parent == False:
4079             document.warning("Malformed LyX document: Can't find parent paragraph layout")
4080             i += 1
4081             continue
4082         parbeg = parent[1]
4083         parend = parent[2]
4084         realparbeg = parent[3]
4085         layoutname = parent[0]
4086         realparend = parend
4087         for p in range(parbeg, parend):
4088             if p >= realparend:
4089                 i = realparend
4090                 break
4091             if layoutname in list_layouts:
4092                 m = rx.match(document.body[p])
4093                 if m:
4094                     argnr = m.group(1)
4095                     if argnr == "item:1":
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                     elif argnr == "item:2":
4105                         j = find_end_of_inset(document.body, i)
4106                         # Find containing paragraph layout
4107                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
4108                         endPlain = find_end_of_layout(document.body, beginPlain)
4109                         content = document.body[beginPlain + 1 : endPlain]
4110                         del document.body[i:j+1]
4111                         subst = put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
4112                         document.body[realparbeg : realparbeg] = subst
4113         
4114         i = realparend
4115
4116
4117 def revert_powerdot_columns(document):
4118     " Reverts powerdot twocolumn to TeX-code "
4119     if document.textclass != "powerdot":
4120         return
4121
4122     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
4123     i = 0
4124     while True:
4125         i = find_token(document.body, "\\begin_layout Twocolumn", i)
4126         if i == -1:
4127             return
4128         j = find_end_of_layout(document.body, i)
4129         if j == -1:
4130             document.warning("Malformed LyX document: Can't find end of Twocolumn layout")
4131             i += 1
4132             continue
4133         endlay = j
4134         document.body[j : j] = put_cmd_in_ert("}") + document.body[j : j]
4135         endlay += len(put_cmd_in_ert("}"))
4136         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\twocolumn")
4137         for p in range(i, j):
4138             if p >= endlay:
4139                 break
4140             m = rx.match(document.body[p])
4141             if m:
4142                 argnr = m.group(1)
4143                 if argnr == "1":
4144                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
4145                     endPlain = find_end_of_layout(document.body, beginPlain)
4146                     endInset = find_end_of_inset(document.body, p)
4147                     content = document.body[beginPlain + 1 : endPlain]
4148                     # Adjust range end
4149                     endlay = endlay - len(document.body[p : endInset + 1])
4150                     # Remove arg inset
4151                     del document.body[p : endInset + 1]
4152                     subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
4153                 elif argnr == "2":
4154                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
4155                     endPlain = find_end_of_layout(document.body, beginPlain)
4156                     endInset = find_end_of_inset(document.body, p)
4157                     content = document.body[beginPlain + 1 : endPlain]
4158                     # Adjust range end
4159                     endlay = endlay - len(document.body[p : endInset + 1])
4160                     # Remove arg inset
4161                     del document.body[p : endInset + 1]
4162                     subst += put_cmd_in_ert("{") + content + put_cmd_in_ert("}")
4163                     
4164         subst += put_cmd_in_ert("{")
4165         document.body[i : i + 1] = subst
4166         i = endlay
4167
4168
4169 def revert_mbox_fbox(document):
4170     'Convert revert mbox/fbox boxes to TeX-code'
4171     i = 0
4172     while True:
4173         i = find_token(document.body, "\\begin_inset Box", i)
4174         if i == -1:
4175             return
4176         j = find_token(document.body, "width", i)
4177         if j != i + 7:
4178             document.warning("Malformed LyX document: Can't find box width")
4179             return
4180         width = get_value(document.body, "width", j)
4181         k = find_end_of_inset(document.body, j)
4182         if k == -1:
4183             document.warning("Malformed LyX document: Can't find end of box inset")
4184             i += 1
4185             continue
4186         BeginLayout = find_token(document.body, "\\begin_layout Plain Layout", j)
4187         EndLayout = find_token(document.body, "\\end_layout", BeginLayout)
4188         # replace if width is ""
4189         if (width == '""'):
4190             document.body[EndLayout:k + 1] = put_cmd_in_ert("}")
4191             if document.body[i] == "\\begin_inset Box Frameless":
4192                 document.body[i:BeginLayout + 1] = put_cmd_in_ert("\\mbox{")
4193             if document.body[i] == "\\begin_inset Box Boxed":
4194                 document.body[i:BeginLayout + 1] = put_cmd_in_ert("\\fbox{")
4195         i += 1
4196
4197
4198 def revert_starred_caption(document):
4199     " Reverts unnumbered longtable caption insets "
4200     
4201     i = 0
4202     while True:
4203       i = find_token(document.body, "\\begin_inset Caption LongTableNoNumber", i)
4204       if i == -1:
4205           return
4206       # This is not equivalent, but since the caption inset is a full blown
4207       # text inset a true conversion to ERT is too difficult.
4208       document.body[i] = "\\begin_inset Caption Standard"
4209       i += 1
4210
4211
4212 def revert_forced_local_layout(document):
4213     i = 0
4214     while True:
4215         i = find_token(document.header, "\\begin_forced_local_layout", i)
4216         if i == -1:
4217             return
4218         j = find_end_of(document.header, i, "\\begin_forced_local_layout", "\\end_forced_local_layout")
4219         if j == -1:
4220             # this should not happen
4221             break
4222         regexp = re.compile(r'\s*forcelocal', re.IGNORECASE)
4223         k = find_re(document.header, regexp, i, j)
4224         while k != -1:
4225             del document.header[k]
4226             j = j - 1
4227             k = find_re(document.header, regexp, i, j)
4228         k = find_token(document.header, "\\begin_local_layout", 0)
4229         if k == -1:
4230             document.header[i] = "\\begin_local_layout"
4231             document.header[j] = "\\end_local_layout"
4232         else:
4233             l = find_end_of(document.header, k, "\\begin_local_layout", "\\end_local_layout")
4234             if j == -1:
4235                 # this should not happen
4236                 break
4237             lines = document.header[i+1 : j]
4238             if k > i:
4239                 document.header[k+1 : k+1] = lines
4240                 document.header[i   : j  ] = []
4241             else:
4242                 document.header[i   : j  ] = []
4243                 document.header[k+1 : k+1] = lines
4244
4245
4246 def revert_aa1(document):
4247   " Reverts InsetArguments of aa to TeX-code "
4248   if document.textclass == "aa":
4249     i = 0
4250     while True:
4251       if i != -1:
4252         i = find_token(document.body, "\\begin_layout Abstract (structured)", i)
4253       if i != -1:
4254         revert_Argument_to_TeX_brace(document, i, 0, 1, 4, False, False)
4255         i += 1
4256       if i == -1:
4257         return
4258
4259
4260 def revert_aa2(document):
4261   " Reverts InsetArguments of aa to TeX-code "
4262   if document.textclass == "aa":
4263     i = 0
4264     while True:
4265       if i != -1:
4266         i = find_token(document.body, "\\begin_layout Abstract (structured)", i)
4267       if i != -1:
4268         document.body[i] = "\\begin_layout Abstract"
4269         i += 1
4270       if i == -1:
4271         return
4272
4273
4274 def revert_tibetan(document):
4275     "Set the document language for Tibetan to English" 
4276
4277     if document.language == "tibetan":
4278         document.language = "english"
4279         i = find_token(document.header, "\\language", 0) 
4280         if i != -1: 
4281             document.header[i] = "\\language english" 
4282     j = 0
4283     while j < len(document.body): 
4284         j = find_token(document.body, "\\lang tibetan", j)
4285         if j != -1:
4286             document.body[j] = document.body[j].replace("\\lang tibetan", "\\lang english")
4287             j += 1
4288         else:
4289             j = len(document.body)
4290
4291
4292 #############
4293 #
4294 # Chunk stuff
4295 #
4296 #############
4297
4298 # The idea here is that we will have a sequence of chunk paragraphs.
4299 # We want to convert them to paragraphs in one or several chunk insets.
4300 # Individual chunks are terminated by the character @ on the last line.
4301 # This line will be discarded, and following lines are treated as new
4302 # chunks, which go into their own insets.
4303 # The first line of a chunk should look like: <<CONTENT>>=
4304 # We will discard the delimiters, and put the CONTENT into the
4305 # optional argument of the inset, if the CONTENT is non-empty.
4306 def convert_chunks(document):
4307     first_re = re.compile(r'<<(.*)>>=(.*)')
4308     file_pos = 0
4309     while True:
4310         # find start of a block of chunks
4311         i = find_token(document.body, "\\begin_layout Chunk", file_pos)
4312         if i == -1:
4313             return
4314         start = i
4315         end = -1
4316         contents = []
4317         chunk_started = False
4318
4319         while True:
4320             # process the one we just found
4321             j = find_end_of_layout(document.body, i)
4322             if j == -1:
4323                 document.warning("Malformed LyX documents. Can't find end of Chunk layout!")
4324                 # there is no point continuing, as we will run into the same error again.
4325                 return
4326             this_chunk = "".join(document.body[i + 1:j])
4327             
4328             # there may be empty lines between chunks
4329             # we just skip them.
4330             if not chunk_started:
4331                 if this_chunk != "":
4332                     # new chunk starts
4333                     chunk_started = True
4334             
4335             if chunk_started:
4336                 contents.append(document.body[i + 1:j])
4337
4338             # look for potential chunk terminator
4339             # on the last line of the chunk paragraph            
4340             if document.body[j - 1] == "@":
4341                 break
4342
4343             # look for subsequent chunk paragraph
4344             i = find_token(document.body, "\\begin_layout", j)
4345             if i == -1:
4346                 break
4347
4348             if get_value(document.body, "\\begin_layout", i) != "Chunk":
4349                 break
4350
4351         file_pos = end = j + 1
4352         
4353         # The last chunk should simply have an "@" in it
4354         # or at least end with "@" (can happen if @ is
4355         # preceded by a newline)
4356         lastpar = ''.join(contents[-1])
4357         if not lastpar.endswith("@"):
4358             document.warning("Unexpected chunk content: chunk not terminated by '@'!")
4359             continue
4360
4361         if lastpar == "@":
4362             # chunk par only contains "@". Just drop it.
4363             contents.pop()
4364         else:
4365             # chunk par contains more. Only drop the "@".
4366             contents[-1].pop()
4367
4368         # The first line should look like: <<CONTENT>>=
4369         # We want the CONTENT
4370         optarg = ' '.join(contents[0])
4371         optarg.strip()
4372         # We can already have real chunk content in
4373         # the first par (separated from the options by a newline).
4374         # We collect such stuff to re-insert it later.
4375         postoptstuff = []
4376         
4377         match = first_re.search(optarg)
4378         if match:
4379             optarg = match.groups()[0]
4380             if match.groups()[1] != "":
4381                 postopt = False
4382                 for c in contents[0]:
4383                     if c.endswith(">>="):
4384                         postopt = True
4385                         continue
4386                     if postopt:
4387                         postoptstuff.append(c)
4388             # We have stripped everything. This can be deleted.
4389             contents.pop(0)
4390
4391         newstuff = ['\\begin_layout Standard',
4392                     '\\begin_inset Flex Chunk',
4393                     'status open', '',
4394                     '\\begin_layout Plain Layout', '']
4395
4396         # If we have a non-empty optional argument, insert it.
4397         if match and optarg != "":
4398             newstuff.extend(
4399                 ['\\begin_inset Argument 1',
4400                  'status open', '',
4401                  '\\begin_layout Plain Layout',
4402                  optarg,
4403                  '\\end_layout', '',
4404                  '\\end_inset', ''])
4405
4406         # Since we already opened a Plain layout, the first paragraph
4407         # does not need to do that.
4408         did_one_par = False
4409         if postoptstuff:
4410             newstuff.extend(postoptstuff)
4411             newstuff.append('\\end_layout')
4412             did_one_par = True
4413         for c in contents:
4414             if did_one_par:
4415                 newstuff.extend(['', '\\begin_layout Plain Layout', ''])
4416             else:
4417                 did_one_par = True
4418             newstuff.extend(c)
4419             newstuff.append('\\end_layout')
4420
4421         newstuff.extend(['', '\\end_inset', '', '\\end_layout', ''])
4422
4423         document.body[start:end] = newstuff
4424
4425         file_pos += len(newstuff) - (end - start)
4426
4427
4428 def revert_chunks(document):
4429     i = 0
4430     while True:
4431         i = find_token(document.body, "\\begin_inset Flex Chunk", i)
4432         if i == -1:
4433             return
4434
4435         iend = find_end_of_inset(document.body, i)
4436         if iend == -1:
4437             document.warning("Can't find end of Chunk!")
4438             i += 1
4439             continue
4440
4441         # Look for optional argument
4442         have_optarg = False
4443         ostart = find_token(document.body, "\\begin_inset Argument 1", i, iend)
4444         if ostart != -1:
4445             oend = find_end_of_inset(document.body, ostart)
4446             k = find_token(document.body, "\\begin_layout Plain Layout", ostart, oend)
4447             if k == -1:
4448                 document.warning("Malformed LyX document: Can't find argument contents!")
4449             else:
4450                 m = find_end_of_layout(document.body, k)
4451                 optarg = "".join(document.body[k+1:m])
4452                 have_optarg = True
4453
4454             # We now remove the optional argument, so we have something
4455             # uniform on which to work
4456             document.body[ostart : oend + 1] = []
4457             # iend is now invalid
4458             iend = find_end_of_inset(document.body, i)
4459
4460         retval = get_containing_layout(document.body, i)
4461         if not retval:
4462             document.warning("Can't find containing layout for Chunk!")
4463             i = iend
4464             continue
4465         (lname, lstart, lend, pstart)  = retval
4466         # we now want to work through the various paragraphs, and collect their contents
4467         parlist = []
4468         k = i
4469         while True:
4470             k = find_token(document.body, "\\begin_layout Plain Layout", k, lend)
4471             if k == -1:
4472                 break
4473             j = find_end_of_layout(document.body, k)
4474             if j == -1:
4475                 document.warning("Can't find end of layout inside chunk!")
4476                 break
4477             parlist.append(document.body[k+1:j])
4478             k = j
4479         # we now need to wrap all of these paragraphs in chunks
4480         newlines = []
4481         if have_optarg:
4482             newlines.extend(["\\begin_layout Chunk", "", "<<" + optarg + ">>=", "\\end_layout", ""])
4483         for stuff in parlist:
4484             newlines.extend(["\\begin_layout Chunk"] + stuff + ["\\end_layout", ""])
4485         newlines.extend(["\\begin_layout Chunk", "", "@", "\\end_layout", ""])
4486         # replace old content with new content
4487         document.body[lstart : lend + 1] = newlines
4488         i = lstart + len(newlines)
4489         
4490
4491 ##
4492 # Conversion hub
4493 #
4494
4495 supported_versions = ["2.1.0","2.1"]
4496 convert = [
4497            [414, []],
4498            [415, [convert_undertilde]],
4499            [416, []],
4500            [417, [convert_japanese_encodings]],
4501            [418, [convert_justification]],
4502            [419, []],
4503            [420, [convert_biblio_style]],
4504            [421, [convert_longtable_captions]],
4505            [422, [convert_use_packages]],
4506            [423, [convert_use_mathtools]],
4507            [424, [convert_cite_engine_type]],
4508            # No convert_cancel, since cancel will be loaded automatically
4509            # in format 425 without any possibility to switch it off.
4510            # This has been fixed in format 464.
4511            [425, []],
4512            [426, []],
4513            [427, []],
4514            [428, [convert_cell_rotation]],
4515            [429, [convert_table_rotation]],
4516            [430, [convert_listoflistings]],
4517            [431, [convert_use_amssymb]],
4518            [432, []],
4519            [433, [convert_armenian]],
4520            [434, []],
4521            [435, []],
4522            [436, []],
4523            [437, []],
4524            [438, []],
4525            [439, []],
4526            [440, []],
4527            [441, [convert_mdnomath]],
4528            [442, []],
4529            [443, []],
4530            [444, []],
4531            [445, []],
4532            [446, [convert_latexargs]],
4533            [447, [convert_IEEEtran, convert_AASTeX, convert_AGUTeX, convert_IJMP, convert_SIGPLAN, convert_SIGGRAPH, convert_EuropeCV, convert_Initials, convert_ModernCV]],
4534            [448, [convert_literate]],
4535            [449, []],
4536            [450, []],
4537            [451, [convert_beamerargs, convert_againframe_args, convert_corollary_args, convert_quote_args]],
4538            [452, [convert_beamerblocks]],
4539            [453, [convert_use_stmaryrd]],
4540            [454, [convert_overprint]],
4541            [455, []],
4542            [456, [convert_epigraph]],
4543            [457, [convert_use_stackrel]],
4544            [458, [convert_captioninsets, convert_captionlayouts]],
4545            [459, []],
4546            [460, []],
4547            [461, []],
4548            [462, []],
4549            [463, [convert_encodings]],
4550            [464, [convert_use_cancel]],
4551            [465, [convert_lyxframes, remove_endframes]],
4552            [466, []],
4553            [467, []],
4554            [468, []],
4555            [469, []],
4556            [470, []],
4557            [471, [convert_cite_engine_type_default]],
4558            [472, []],
4559            [473, []],
4560            [474, [convert_chunks]],
4561           ]
4562
4563 revert =  [
4564            [473, [revert_chunks]],
4565            [472, [revert_tibetan]],
4566            [471, [revert_aa1,revert_aa2]],
4567            [470, [revert_cite_engine_type_default]],
4568            [469, [revert_forced_local_layout]],
4569            [468, [revert_starred_caption]],
4570            [467, [revert_mbox_fbox]],
4571            [466, [revert_iwona_fonts]],
4572            [465, [revert_powerdot_flexes, revert_powerdot_pause, revert_powerdot_itemargs, revert_powerdot_columns]],
4573            [464, []],
4574            [463, [revert_use_cancel]],
4575            [462, [revert_encodings]],
4576            [461, [revert_new_libertines]],
4577            [460, [revert_kurier_fonts]],
4578            [459, [revert_IEEEtran_3]],
4579            [458, [revert_fragileframe, revert_newframes]],
4580            [457, [revert_captioninsets, revert_captionlayouts]],
4581            [456, [revert_use_stackrel]],
4582            [455, [revert_epigraph]],
4583            [454, [revert_frametitle]],
4584            [453, [revert_overprint]],
4585            [452, [revert_use_stmaryrd]],
4586            [451, [revert_beamerblocks]],
4587            [450, [revert_beamerargs, revert_beamerargs2, revert_beamerargs3, revert_beamerflex]],
4588            [449, [revert_garamondx, revert_garamondx_newtxmath]],
4589            [448, [revert_itemargs]],
4590            [447, [revert_literate]],
4591            [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]],
4592            [445, [revert_latexargs]],
4593            [444, [revert_uop]],
4594            [443, [revert_biolinum]],
4595            [442, []],
4596            [441, [revert_newtxmath]],
4597            [440, [revert_mdnomath]],
4598            [439, [revert_mathfonts]],
4599            [438, [revert_minionpro]],
4600            [437, [revert_ipadeco, revert_ipachar]],
4601            [436, [revert_texgyre]],
4602            [435, [revert_mathdesign]],
4603            [434, [revert_txtt]],
4604            [433, [revert_libertine]],
4605            [432, [revert_armenian]],
4606            [431, [revert_languages, revert_ancientgreek]],
4607            [430, [revert_use_amssymb]],
4608            [429, [revert_listoflistings]],
4609            [428, [revert_table_rotation]],
4610            [427, [revert_cell_rotation]],
4611            [426, [revert_tipa]],
4612            [425, [revert_verbatim]],
4613            [424, [revert_cancel]],
4614            [423, [revert_cite_engine_type]],
4615            [422, [revert_use_mathtools]],
4616            [421, [revert_use_packages]],
4617            [420, [revert_longtable_captions]],
4618            [419, [revert_biblio_style]],
4619            [418, [revert_australian]],
4620            [417, [revert_justification]],
4621            [416, [revert_japanese_encodings]],
4622            [415, [revert_negative_space, revert_math_spaces]],
4623            [414, [revert_undertilde]],
4624            [413, [revert_visible_space]]
4625           ]
4626
4627
4628 if __name__ == "__main__":
4629     pass