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