]> git.lyx.org Git - lyx.git/blob - lib/lyx2lyx/lyx_2_1.py
Fix AttributeError with Python 3.
[lyx.git] / lib / lyx2lyx / lyx_2_1.py
1 # -*- coding: utf-8 -*-
2 # This file is part of lyx2lyx
3 # Copyright (C) 2011 The LyX team
4 #
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18
19 """ Convert files to the file format generated by LyX 2.1"""
20
21 import re, string
22 import unicodedata
23 import sys, os
24
25 # Uncomment only what you need to import, please.
26
27 from parser_tools import count_pars_in_inset, del_complete_lines, del_token, \
28     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], 'endhead') == 'true':
560                     document.body[begin_row] = set_option_value(document.body[begin_row], 'endhead', 'false')
561                 if get_option_value(document.body[begin_row], 'endfoot') == 'true':
562                     document.body[begin_row] = set_option_value(document.body[begin_row], 'endfoot', 'false')
563                 if get_option_value(document.body[begin_row], 'endlastfoot') == 'true':
564                     document.body[begin_row] = set_option_value(document.body[begin_row], 'endlastfoot', 'false')
565             begin_row = end_row
566         # since there could be a tabular inside this one, we
567         # cannot jump to end.
568         begin_table += 1
569
570
571 def convert_longtable_captions(document):
572     "Add a firsthead flag to caption rows"
573     handle_longtable_captions(document, True)
574
575
576 def revert_longtable_captions(document):
577     "remove head/foot flag from caption rows"
578     handle_longtable_captions(document, False)
579
580
581 def convert_use_packages(document):
582     "use_xxx yyy => use_package xxx yyy"
583     packages = ["amsmath", "esint", "mathdots", "mhchem", "undertilde"]
584     for p in packages:
585         i = find_token(document.header, "\\use_%s" % p, 0)
586         if i != -1:
587             value = get_value(document.header, "\\use_%s" % p, i)
588             document.header[i] = "\\use_package %s %s" % (p, value)
589
590
591 def revert_use_packages(document):
592     "use_package xxx yyy => use_xxx yyy"
593     packages = ["amsmath", "esint", "mhchem", "mathdots", "undertilde"]
594     # the order is arbitrary for the use_package version, and not all packages need to be given.
595     # Ensure a complete list and correct order (important for older LyX versions and especially lyx2lyx)
596     # first loop: find line with first package
597     j = -1
598     for p in packages:
599         regexp = re.compile(r'(\\use_package\s+%s)' % p)
600         i = find_re(document.header, regexp, 0)
601         if i != -1 and (j < 0 or i < j):
602             j = i
603     # second loop: replace or insert packages in front of all existing ones
604     for p in packages:
605         regexp = re.compile(r'(\\use_package\s+%s)' % p)
606         i = find_re(document.header, regexp, 0)
607         if i != -1:
608             value = get_value(document.header, "\\use_package %s" % p, i).split()[1]
609             del document.header[i]
610             document.header.insert(j, "\\use_%s %s" % (p, value))
611         else:
612             document.header.insert(j, "\\use_%s 1" % p)
613         j += 1
614
615
616 def convert_use_package(document, pkg, commands, oldauto):
617     # oldauto defines how the version we are converting from behaves:
618     # if it is true, the old version uses the package automatically.
619     # if it is false, the old version never uses the package.
620     i = find_token(document.header, "\\use_package")
621     if i == -1:
622         document.warning("Malformed LyX document: Can't find \\use_package.")
623         return;
624     packageline = "\\usepackage{%s}" % pkg
625     if (del_complete_lines(document.preamble,
626                            ['% Added by lyx2lyx', packageline]) or
627         del_complete_lines(document.preamble, [packageline])):
628         # package was loaded in the preamble, convert this to header setting
629         document.header.insert(i + 1, "\\use_package " + pkg + " 2") # on
630     # If oldauto is true we have two options:
631     # We can either set the package to auto - this is correct for files in
632     # format 425 to 463, and may create a conflict for older files which use
633     # any command in commands with a different definition.
634     # Or we can look whether any command in commands is used, and set it to
635     # auto if not and to off if yes. This will not create a conflict, but will
636     # create uncompilable documents for files in format 425 to 463, which use
637     # any command in commands.
638     # We choose the first option since its error is less likely.
639     elif oldauto:
640         document.header.insert(i + 1, "\\use_package " + pkg + " 1") # auto
641     else:
642         j = 0
643         while True:
644             j = find_token(document.body, '\\begin_inset Formula', j)
645             if j == -1:
646                 break
647             k = find_end_of_inset(document.body, j)
648             if k == -1:
649                 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(j))
650                 j += 1
651                 continue
652             code = "\n".join(document.body[j:k])
653             for c in commands:
654                 if code.find("\\%s" % c) != -1:
655                     # at least one of the commands was found - need to switch package off
656                     document.header.insert(i + 1, "\\use_package " + pkg + " 0") # off
657                     return
658             j = k
659         # no command was found - set to auto (bug 9069)
660         document.header.insert(i + 1, "\\use_package " + pkg + " 1") # auto
661
662
663 def revert_use_package(document, pkg, commands, oldauto):
664     # oldauto defines how the version we are reverting to behaves:
665     # if it is true, the old version uses the package automatically.
666     # if it is false, the old version never uses the package.
667     regexp = re.compile(r'(\\use_package\s+%s)' % pkg)
668     i = find_re(document.header, regexp, 0)
669     value = "1" # default is auto
670     if i != -1:
671         value = get_value(document.header, "\\use_package" , i).split()[1]
672         del document.header[i]
673     if value == "2": # on
674         add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
675     elif value == "1" and not oldauto: # auto
676         i = 0
677         while True:
678             i = find_token(document.body, '\\begin_inset Formula', i)
679             if i == -1:
680                 return
681             j = find_end_of_inset(document.body, i)
682             if j == -1:
683                 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
684                 i += 1
685                 continue
686             code = "\n".join(document.body[i:j])
687             for c in commands:
688                 if code.find("\\%s" % c) != -1:
689                     add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
690                     return
691             i = j
692
693
694 mathtools_commands = ["mathclap", "mathllap", "mathrlap", \
695                 "lgathered", "rgathered", "vcentcolon", "dblcolon", \
696                 "coloneqq", "Coloneqq", "coloneq", "Coloneq", "eqqcolon", \
697                 "Eqqcolon", "eqcolon", "Eqcolon", "colonapprox", \
698                 "Colonapprox", "colonsim", "Colonsim"]
699 def convert_use_mathtools(document):
700     "insert use_package mathtools"
701     convert_use_package(document, "mathtools", mathtools_commands, False)
702
703
704 def revert_use_mathtools(document):
705     "remove use_package mathtools"
706     revert_use_package(document, "mathtools", mathtools_commands, False)
707
708
709 # commands provided by stmaryrd.sty but LyX uses other packages:
710 # boxdot lightning, bigtriangledown, bigtriangleup
711 stmaryrd_commands = ["shortleftarrow", "shortrightarrow", "shortuparrow", \
712                     "shortdownarrow", "Yup", "Ydown", "Yleft", "Yright", \
713                     "varcurlyvee", "varcurlywedge", "minuso", "baro", \
714                     "sslash", "bbslash", "moo", "varotimes", "varoast", \
715                     "varobar", "varodot", "varoslash", "varobslash", \
716                     "varocircle", "varoplus", "varominus", "boxast", \
717                     "boxbar", "boxslash", "boxbslash", "boxcircle", \
718                     "boxbox", "boxempty", "merge", "vartimes", \
719                     "fatsemi", "sswarrow", "ssearrow", "curlywedgeuparrow", \
720                     "curlywedgedownarrow", "fatslash", "fatbslash", "lbag", \
721                     "rbag", "varbigcirc", "leftrightarroweq", \
722                     "curlyveedownarrow", "curlyveeuparrow", "nnwarrow", \
723                     "nnearrow", "leftslice", "rightslice", "varolessthan", \
724                     "varogreaterthan", "varovee", "varowedge", "talloblong", \
725                     "interleave", "obar", "obslash", "olessthan", \
726                     "ogreaterthan", "ovee", "owedge", "oblong", "inplus", \
727                     "niplus", "nplus", "subsetplus", "supsetplus", \
728                     "subsetpluseq", "supsetpluseq", "Lbag", "Rbag", \
729                     "llbracket", "rrbracket", "llparenthesis", \
730                     "rrparenthesis", "binampersand", "bindnasrepma", \
731                     "trianglelefteqslant", "trianglerighteqslant", \
732                     "ntrianglelefteqslant", "ntrianglerighteqslant", \
733                     "llfloor", "rrfloor", "llceil", "rrceil", "arrownot", \
734                     "Arrownot", "Mapstochar", "mapsfromchar", "Mapsfromchar", \
735                     "leftrightarrowtriangle", "leftarrowtriangle", \
736                     "rightarrowtriangle", \
737                     "bigcurlyvee", "bigcurlywedge", "bigsqcap", "bigbox", \
738                     "bigparallel", "biginterleave", "bignplus", \
739                     "varcopyright", "longarrownot", "Longarrownot", \
740                     "Mapsto", "mapsfrom", "Mapsfrom" "Longmapsto", \
741                     "longmapsfrom", "Longmapsfrom"]
742 def convert_use_stmaryrd(document):
743     "insert use_package stmaryrd"
744     convert_use_package(document, "stmaryrd", stmaryrd_commands, False)
745
746
747 def revert_use_stmaryrd(document):
748     "remove use_package stmaryrd"
749     revert_use_package(document, "stmaryrd", stmaryrd_commands, False)
750
751
752 stackrel_commands = ["stackrel"]
753 def convert_use_stackrel(document):
754     "insert use_package stackrel"
755     convert_use_package(document, "stackrel", stackrel_commands, False)
756
757
758 def revert_use_stackrel(document):
759     "remove use_package stackrel"
760     revert_use_package(document, "stackrel", stackrel_commands, False)
761
762
763 def convert_cite_engine_type(document):
764     "Determine the \\cite_engine_type from the citation engine."
765     i = find_token(document.header, "\\cite_engine", 0)
766     if i == -1:
767         return
768     engine = get_value(document.header, "\\cite_engine", i)
769     if "_" in engine:
770         engine, type = engine.split("_")
771     else:
772         type = {"basic": "numerical", "jurabib": "authoryear"}[engine]
773     document.header[i] = "\\cite_engine " + engine
774     document.header.insert(i + 1, "\\cite_engine_type " + type)
775
776
777 def revert_cite_engine_type(document):
778     "Natbib had the type appended with an underscore."
779     engine_type = "numerical"
780     i = find_token(document.header, "\\cite_engine_type" , 0)
781     if i == -1:
782         document.warning("No \\cite_engine_type line. Assuming numerical.")
783     else:
784         engine_type = get_value(document.header, "\\cite_engine_type", i)
785         del document.header[i]
786
787     # We are looking for the natbib citation engine
788     i = find_token(document.header, "\\cite_engine natbib", 0)
789     if i == -1:
790         return
791     document.header[i] = "\\cite_engine natbib_" + engine_type
792
793
794 def convert_cite_engine_type_default(document):
795     "Convert \\cite_engine_type to default for the basic citation engine."
796     i = find_token(document.header, "\\cite_engine basic", 0)
797     if i == -1:
798         return
799     i = find_token(document.header, "\\cite_engine_type" , 0)
800     if i == -1:
801         return
802     document.header[i] = "\\cite_engine_type default"
803
804
805 def revert_cite_engine_type_default(document):
806     """Revert \\cite_engine_type default.
807
808     Revert to numerical for the basic cite engine, otherwise to authoryear."""
809     engine_type = "authoryear"
810     i = find_token(document.header, "\\cite_engine_type default" , 0)
811     if i == -1:
812         return
813     j = find_token(document.header, "\\cite_engine basic", 0)
814     if j != -1:
815         engine_type = "numerical"
816     document.header[i] = "\\cite_engine_type " + engine_type
817
818
819 cancel_commands = ["cancel", "bcancel", "xcancel", "cancelto"]
820 # this is the same, as revert_use_cancel() except for the default
821 def revert_cancel(document):
822     "add cancel to the preamble if necessary"
823     revert_use_package(document, "cancel", cancel_commands, False)
824
825
826 def revert_verbatim(document, starred = False):
827     " Revert verbatim environments completely to TeX-code. "
828     i = 0
829     consecutive = False
830
831     layout_name = "Verbatim"
832     latex_name  = "verbatim"
833     if starred:
834         layout_name = "Verbatim*"
835         latex_name  = "verbatim*"
836
837     subst_end = ['\\end_layout', '', '\\begin_layout Plain Layout',
838                  '\\end_layout', '',
839                  '\\begin_layout Plain Layout', '', '',
840                  '\\backslash', '',
841                  'end{%s}' % (latex_name),
842                  '\\end_layout', '', '\\end_inset',
843                  '', '', '\\end_layout']
844     subst_begin = ['\\begin_layout Standard', '\\noindent',
845                    '\\begin_inset ERT', 'status open', '',
846                    '\\begin_layout Plain Layout', '', '', '\\backslash',
847                    'begin{%s}' % (latex_name),
848                    '\\end_layout', '', '\\begin_layout Plain Layout', '']
849
850     while True:
851         i = find_token(document.body, "\\begin_layout %s" % (layout_name), i)
852         if i == -1:
853             return
854         j = find_end_of_layout(document.body, i)
855         if j == -1:
856             document.warning("Malformed LyX document: Can't find end of %s layout" \
857               % (layout_name))
858             i += 1
859             continue
860         # delete all line breaks insets (there are no other insets)
861         l = i
862         while True:
863             n = find_token(document.body, "\\begin_inset Newline newline", l, j)
864             if n == -1:
865                 n = find_token(document.body, "\\begin_inset Newline linebreak", l, j)
866                 if n == -1:
867                     break
868             m = find_end_of_inset(document.body, n)
869             del(document.body[m:m+1])
870             document.body[n:n+1] = ['\\end_layout', '', '\\begin_layout Plain Layout']
871             l += 1
872             # we deleted a line, so the end of the inset moved forward.
873             # FIXME But we also added some lines, didn't we? I think this
874             # should be j += 1.
875             j -= 1
876         # consecutive verbatim environments need to be connected
877         k = find_token(document.body, "\\begin_layout %s" % (layout_name), j)
878         if k == j + 2 and consecutive == False:
879             consecutive = True
880             document.body[j:j+1] = ['\\end_layout', '', '\\begin_layout Plain Layout']
881             document.body[i:i+1] = subst_begin
882             continue
883         if k == j + 2 and consecutive == True:
884             document.body[j:j+1] = ['\\end_layout', '', '\\begin_layout Plain Layout']
885             del(document.body[i:i+1])
886             continue
887         if k != j + 2 and consecutive == True:
888             document.body[j:j+1] = subst_end
889             # the next paragraph must not be indented
890             # FIXME This seems to be causing problems, because of the
891             # hardcoded use of 19. We should figure out exactly where
892             # this needs to go by searching for the right tag.
893             document.body[j+19:j+19] = ['\\noindent']
894             del(document.body[i:i+1])
895             consecutive = False
896             continue
897         else:
898             document.body[j:j+1] = subst_end
899             # the next paragraph must not be indented
900             # FIXME This seems to be causing problems, because of the
901             # hardcoded use of 19. We should figure out exactly where
902             # this needs to go by searching for the right tag.
903             document.body[j+19:j+19] = ['\\noindent']
904             document.body[i:i+1] = subst_begin
905
906
907 def revert_tipa(document):
908     " Revert native TIPA insets to mathed or ERT. "
909     i = 0
910     while True:
911         i = find_token(document.body, "\\begin_inset IPA", i)
912         if i == -1:
913             return
914         j = find_end_of_inset(document.body, i)
915         if j == -1:
916             document.warning("Malformed LyX document: Can't find end of IPA inset")
917             i += 1
918             continue
919         Multipar = False
920         n = find_token(document.body, "\\begin_layout", i, j)
921         if n == -1:
922             document.warning("Malformed LyX document: IPA inset has no embedded layout")
923             i += 1
924             continue
925         m = find_end_of_layout(document.body, n)
926         if m == -1:
927             document.warning("Malformed LyX document: Can't find end of embedded layout")
928             i += 1
929             continue
930         content = document.body[n+1:m]
931         p = find_token(document.body, "\\begin_layout", m, j)
932         if p != -1 or len(content) > 1:
933             Multipar = True
934             content = document.body[i+1:j]
935         if Multipar:
936             # IPA insets with multiple pars need to be wrapped by \begin{IPA}...\end{IPA}
937             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}")
938             add_to_preamble(document, ["\\usepackage{tipa,tipx}"])
939         else:
940             # single-par IPA insets can be reverted to mathed
941             document.body[i:j+1] = ["\\begin_inset Formula $\\text{\\textipa{" + content[0] + "}}$", "\\end_inset"]
942         i = j
943
944
945 def revert_cell_rotation(document):
946   "Revert cell rotations to TeX-code"
947
948   load_rotating = False
949   i = 0
950   try:
951     while True:
952       # first, let's find out if we need to do anything
953       i = find_token(document.body, '<cell ', i)
954       if i == -1:
955         return
956       j = document.body[i].find('rotate="')
957       if j != -1:
958         k = document.body[i].find('"', j + 8)
959         value = document.body[i][j + 8 : k]
960         if value == "0":
961           rgx = re.compile(r' rotate="[^"]+?"')
962           # remove rotate option
963           document.body[i] = rgx.sub('', document.body[i])
964         elif value == "90":
965           rgx = re.compile(r' rotate="[^"]+?"')
966           document.body[i] = rgx.sub(' rotate="true"', document.body[i])
967         else:
968           rgx = re.compile(r' rotate="[^"]+?"')
969           load_rotating = True
970           # remove rotate option
971           document.body[i] = rgx.sub('', document.body[i])
972           # write ERT
973           document.body[i + 5 : i + 5] = \
974             put_cmd_in_ert("\\end{turn}")
975           document.body[i + 4 : i + 4] = \
976             put_cmd_in_ert("\\begin{turn}{" + value + "}")
977
978       i += 1
979
980   finally:
981     if load_rotating:
982       add_to_preamble(document, ["\\@ifundefined{turnbox}{\\usepackage{rotating}}{}"])
983
984
985 def convert_cell_rotation(document):
986     'Convert cell rotation statements from "true" to "90"'
987
988     i = 0
989     while True:
990       # first, let's find out if we need to do anything
991       i = find_token(document.body, '<cell ', i)
992       if i == -1:
993         return
994       j = document.body[i].find('rotate="true"')
995       if j != -1:
996         rgx = re.compile(r'rotate="[^"]+?"')
997         # convert "true" to "90"
998         document.body[i] = rgx.sub('rotate="90"', document.body[i])
999
1000       i += 1
1001
1002
1003 def revert_table_rotation(document):
1004   "Revert table rotations to TeX-code"
1005
1006   load_rotating = False
1007   i = 0
1008   try:
1009     while True:
1010       # first, let's find out if we need to do anything
1011       i = find_token(document.body, '<features ', i)
1012       if i == -1:
1013         return
1014       j = document.body[i].find('rotate="')
1015       if j != -1:
1016         end_table = find_token(document.body, '</lyxtabular>', j)
1017         k = document.body[i].find('"', j + 8)
1018         value = document.body[i][j + 8 : k]
1019         if value == "0":
1020           rgx = re.compile(r' rotate="[^"]+?"')
1021           # remove rotate option
1022           document.body[i] = rgx.sub('', document.body[i])
1023         elif value == "90":
1024           rgx = re.compile(r'rotate="[^"]+?"')
1025           document.body[i] = rgx.sub('rotate="true"', document.body[i])
1026         else:
1027           rgx = re.compile(r' rotate="[^"]+?"')
1028           load_rotating = True
1029           # remove rotate option
1030           document.body[i] = rgx.sub('', document.body[i])
1031           # write ERT
1032           document.body[end_table + 3 : end_table + 3] = \
1033             put_cmd_in_ert("\\end{turn}")
1034           document.body[i - 2 : i - 2] = \
1035             put_cmd_in_ert("\\begin{turn}{" + value + "}")
1036
1037       i += 1
1038
1039   finally:
1040     if load_rotating:
1041       add_to_preamble(document, ["\\@ifundefined{turnbox}{\\usepackage{rotating}}{}"])
1042
1043
1044 def convert_table_rotation(document):
1045     'Convert table rotation statements from "true" to "90"'
1046
1047     i = 0
1048     while True:
1049       # first, let's find out if we need to do anything
1050       i = find_token(document.body, '<features ', i)
1051       if i == -1:
1052         return
1053       j = document.body[i].find('rotate="true"')
1054       if j != -1:
1055         rgx = re.compile(r'rotate="[^"]+?"')
1056         # convert "true" to "90"
1057         document.body[i] = rgx.sub('rotate="90"', document.body[i])
1058
1059       i += 1
1060
1061
1062 def convert_listoflistings(document):
1063     'Convert ERT \lstlistoflistings to TOC lstlistoflistings inset'
1064     # We can support roundtrip because the command is so simple
1065     i = 0
1066     while True:
1067         i = find_token(document.body, "\\begin_inset ERT", i)
1068         if i == -1:
1069             return
1070         j = find_end_of_inset(document.body, i)
1071         if j == -1:
1072             document.warning("Malformed LyX document: Can't find end of ERT inset")
1073             i += 1
1074             continue
1075         ert = get_ert(document.body, i)
1076         if ert == "\\lstlistoflistings{}":
1077             document.body[i:j] = ["\\begin_inset CommandInset toc", "LatexCommand lstlistoflistings", ""]
1078             i = i + 4
1079         else:
1080             i = j + 1
1081
1082
1083 def revert_listoflistings(document):
1084     'Convert TOC lstlistoflistings inset to ERT lstlistoflistings'
1085     i = 0
1086     while True:
1087         i = find_token(document.body, "\\begin_inset CommandInset toc", i)
1088         if i == -1:
1089             return
1090         if document.body[i+1] == "LatexCommand lstlistoflistings":
1091             j = find_end_of_inset(document.body, i)
1092             if j == -1:
1093                 document.warning("Malformed LyX document: Can't find end of TOC inset")
1094                 i += 1
1095                 continue
1096             subst = put_cmd_in_ert("\\lstlistoflistings{}")
1097             document.body[i:j+1] = subst
1098             add_to_preamble(document, ["\\usepackage{listings}"])
1099         i += 1
1100
1101
1102 def convert_use_amssymb(document):
1103     "insert use_package amssymb"
1104     regexp = re.compile(r'(\\use_package\s+amsmath)')
1105     i = find_re(document.header, regexp, 0)
1106     if i == -1:
1107         document.warning("Malformed LyX document: Can't find \\use_package amsmath.")
1108         return;
1109     value = get_value(document.header, "\\use_package" , i).split()[1]
1110     useamsmath = 0
1111     try:
1112         useamsmath = int(value)
1113     except:
1114         document.warning("Invalid \\use_package amsmath: " + value + ". Assuming auto.")
1115         useamsmath = 1
1116     j = find_token(document.preamble, "\\usepackage{amssymb}", 0)
1117     if j == -1:
1118         document.header.insert(i + 1, "\\use_package amssymb %d" % useamsmath)
1119     else:
1120         document.header.insert(i + 1, "\\use_package amssymb 2")
1121         del document.preamble[j]
1122
1123
1124 def revert_use_amssymb(document):
1125     "remove use_package amssymb"
1126     regexp1 = re.compile(r'(\\use_package\s+amsmath)')
1127     regexp2 = re.compile(r'(\\use_package\s+amssymb)')
1128     i = find_re(document.header, regexp1, 0)
1129     j = find_re(document.header, regexp2, 0)
1130     value1 = "1" # default is auto
1131     value2 = "1" # default is auto
1132     if i != -1:
1133         value1 = get_value(document.header, "\\use_package" , i).split()[1]
1134     if j != -1:
1135         value2 = get_value(document.header, "\\use_package" , j).split()[1]
1136         del document.header[j]
1137     if value1 != value2 and value2 == "2": # on
1138         add_to_preamble(document, ["\\usepackage{amssymb}"])
1139
1140
1141 def convert_use_cancel(document):
1142     "insert use_package cancel"
1143     convert_use_package(document, "cancel", cancel_commands, True)
1144
1145
1146 def revert_use_cancel(document):
1147     "remove use_package cancel"
1148     revert_use_package(document, "cancel", cancel_commands, True)
1149
1150
1151 def revert_ancientgreek(document):
1152     "Set the document language for ancientgreek to greek"
1153
1154     if document.language == "ancientgreek":
1155         document.language = "greek"
1156         i = find_token(document.header, "\\language", 0)
1157         if i != -1:
1158             document.header[i] = "\\language greek"
1159     j = 0
1160     while True:
1161         j = find_token(document.body, "\\lang ancientgreek", j)
1162         if j == -1:
1163             return
1164         else:
1165             document.body[j] = document.body[j].replace("\\lang ancientgreek", "\\lang greek")
1166         j += 1
1167
1168
1169 def revert_languages(document):
1170     "Set the document language for new supported languages to English"
1171
1172     languages = [
1173                  "coptic", "divehi", "hindi", "kurmanji", "lao", "marathi", "occitan", "sanskrit",
1174                  "syriac", "tamil", "telugu", "urdu"
1175                 ]
1176     for n in range(len(languages)):
1177         if document.language == languages[n]:
1178             document.language = "english"
1179             i = find_token(document.header, "\\language", 0)
1180             if i != -1:
1181                 document.header[i] = "\\language english"
1182         j = 0
1183         while j < len(document.body):
1184             j = find_token(document.body, "\\lang " + languages[n], j)
1185             if j != -1:
1186                 document.body[j] = document.body[j].replace("\\lang " + languages[n], "\\lang english")
1187                 j += 1
1188             else:
1189                 j = len(document.body)
1190
1191
1192 def convert_armenian(document):
1193     "Use polyglossia and thus non-TeX fonts for Armenian"
1194
1195     if document.language == "armenian":
1196         i = find_token(document.header, "\\use_non_tex_fonts", 0)
1197         if i != -1:
1198             document.header[i] = "\\use_non_tex_fonts true"
1199
1200
1201 def revert_armenian(document):
1202     "Use ArmTeX and thus TeX fonts for Armenian"
1203
1204     if document.language == "armenian":
1205         i = find_token(document.header, "\\use_non_tex_fonts", 0)
1206         if i != -1:
1207             document.header[i] = "\\use_non_tex_fonts false"
1208
1209
1210 def revert_libertine(document):
1211     " Revert native libertine font definition to LaTeX "
1212
1213     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1214         i = find_token(document.header, "\\font_roman libertine", 0)
1215         if i != -1:
1216             osf = False
1217             j = find_token(document.header, "\\font_osf true", 0)
1218             if j != -1:
1219                 osf = True
1220             preamble = "\\usepackage"
1221             if osf:
1222                 document.header[j] = "\\font_osf false"
1223                 preamble += "[osf]"
1224             else:
1225                 preamble += "[lining]"
1226             preamble += "{libertine-type1}"
1227             add_to_preamble(document, [preamble])
1228             document.header[i] = "\\font_roman default"
1229
1230
1231 def revert_txtt(document):
1232     " Revert native txtt font definition to LaTeX "
1233
1234     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1235         i = find_token(document.header, "\\font_typewriter txtt", 0)
1236         if i != -1:
1237             preamble = "\\renewcommand{\\ttdefault}{txtt}"
1238             add_to_preamble(document, [preamble])
1239             document.header[i] = "\\font_typewriter default"
1240
1241
1242 def revert_mathdesign(document):
1243     " Revert native mathdesign font definition to LaTeX "
1244
1245     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1246         mathdesign_dict = {
1247         "mdbch":  "charter",
1248         "mdput":  "utopia",
1249         "mdugm":  "garamond"
1250         }
1251         i = find_token(document.header, "\\font_roman", 0)
1252         if i == -1:
1253             return
1254         val = get_value(document.header, "\\font_roman", i)
1255         if val in list(mathdesign_dict.keys()):
1256             preamble = "\\usepackage[%s" % mathdesign_dict[val]
1257             expert = False
1258             j = find_token(document.header, "\\font_osf true", 0)
1259             if j != -1:
1260                 expert = True
1261                 document.header[j] = "\\font_osf false"
1262             l = find_token(document.header, "\\font_sc true", 0)
1263             if l != -1:
1264                 expert = True
1265                 document.header[l] = "\\font_sc false"
1266             if expert:
1267                 preamble += ",expert"
1268             preamble += "]{mathdesign}"
1269             add_to_preamble(document, [preamble])
1270             document.header[i] = "\\font_roman default"
1271
1272
1273 def revert_texgyre(document):
1274     " Revert native TeXGyre font definition to LaTeX "
1275
1276     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1277         texgyre_fonts = ["tgadventor", "tgbonum", "tgchorus", "tgcursor", \
1278                          "tgheros", "tgpagella", "tgschola", "tgtermes"]
1279         i = find_token(document.header, "\\font_roman", 0)
1280         if i != -1:
1281             val = get_value(document.header, "\\font_roman", i)
1282             if val in texgyre_fonts:
1283                 preamble = "\\usepackage{%s}" % val
1284                 add_to_preamble(document, [preamble])
1285                 document.header[i] = "\\font_roman default"
1286         i = find_token(document.header, "\\font_sans", 0)
1287         if i != -1:
1288             val = get_value(document.header, "\\font_sans", i)
1289             if val in texgyre_fonts:
1290                 preamble = "\\usepackage{%s}" % val
1291                 add_to_preamble(document, [preamble])
1292                 document.header[i] = "\\font_sans default"
1293         i = find_token(document.header, "\\font_typewriter", 0)
1294         if i != -1:
1295             val = get_value(document.header, "\\font_typewriter", i)
1296             if val in texgyre_fonts:
1297                 preamble = "\\usepackage{%s}" % val
1298                 add_to_preamble(document, [preamble])
1299                 document.header[i] = "\\font_typewriter default"
1300
1301
1302 def revert_ipadeco(document):
1303     " Revert IPA decorations to ERT "
1304     i = 0
1305     while True:
1306       i = find_token(document.body, "\\begin_inset IPADeco", i)
1307       if i == -1:
1308           return
1309       end = find_end_of_inset(document.body, i)
1310       if end == -1:
1311           document.warning("Can't find end of inset at line " + str(i))
1312           i += 1
1313           continue
1314       line = document.body[i]
1315       rx = re.compile(r'\\begin_inset IPADeco (.*)$')
1316       m = rx.match(line)
1317       decotype = m.group(1)
1318       if decotype != "toptiebar" and decotype != "bottomtiebar":
1319           document.warning("Invalid IPADeco type: " + decotype)
1320           i = end
1321           continue
1322       blay = find_token(document.body, "\\begin_layout Plain Layout", i, end)
1323       if blay == -1:
1324           document.warning("Can't find layout for inset at line " + str(i))
1325           i = end
1326           continue
1327       bend = find_end_of_layout(document.body, blay)
1328       if bend == -1:
1329           document.warning("Malformed LyX document: Could not find end of IPADeco inset's layout.")
1330           i = end
1331           continue
1332       substi = ["\\begin_inset ERT", "status collapsed", "",
1333                 "\\begin_layout Plain Layout", "", "", "\\backslash",
1334                 decotype + "{", "\\end_layout", "", "\\end_inset"]
1335       substj = ["\\size default", "", "\\begin_inset ERT", "status collapsed", "",
1336                 "\\begin_layout Plain Layout", "", "}", "\\end_layout", "", "\\end_inset"]
1337       # do the later one first so as not to mess up the numbering
1338       document.body[bend:end + 1] = substj
1339       document.body[i:blay + 1] = substi
1340       i = end + len(substi) + len(substj) - (end - bend) - (blay - i) - 2
1341       add_to_preamble(document, "\\usepackage{tipa}")
1342
1343
1344 def revert_ipachar(document):
1345     ' Revert \\IPAChar to ERT '
1346     i = 0
1347     found = False
1348     while i < len(document.body):
1349         m = re.match(r'(.*)\\IPAChar \\(\w+\{\w+\})(.*)', document.body[i])
1350         if m:
1351             found = True
1352             before = m.group(1)
1353             ipachar = m.group(2)
1354             after = m.group(3)
1355             subst = [before,
1356                      '\\begin_inset ERT',
1357                      'status collapsed', '',
1358                      '\\begin_layout Standard',
1359                      '', '', '\\backslash',
1360                      ipachar,
1361                      '\\end_layout', '',
1362                      '\\end_inset', '',
1363                      after]
1364             document.body[i: i+1] = subst
1365             i = i + len(subst)
1366         else:
1367             i += 1
1368     if found:
1369         add_to_preamble(document, "\\usepackage{tone}")
1370
1371
1372 def revert_minionpro(document):
1373     " Revert native MinionPro font definition to LaTeX "
1374
1375     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1376         i = find_token(document.header, "\\font_roman minionpro", 0)
1377         if i != -1:
1378             osf = False
1379             j = find_token(document.header, "\\font_osf true", 0)
1380             if j != -1:
1381                 osf = True
1382             preamble = "\\usepackage"
1383             if osf:
1384                 document.header[j] = "\\font_osf false"
1385             else:
1386                 preamble += "[lf]"
1387             preamble += "{MinionPro}"
1388             add_to_preamble(document, [preamble])
1389             document.header[i] = "\\font_roman default"
1390
1391
1392 def revert_mathfonts(document):
1393     " Revert native math font definitions to LaTeX "
1394
1395     i = find_token(document.header, "\\font_math", 0)
1396     if i == -1:
1397        return
1398     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1399         val = get_value(document.header, "\\font_math", i)
1400         if val == "eulervm":
1401             add_to_preamble(document, "\\usepackage{eulervm}")
1402         elif val == "default":
1403             mathfont_dict = {
1404             "lmodern":  "\\renewcommand{\\rmdefault}{lmr}",
1405             "minionpro":  "\\usepackage[onlytext,lf]{MinionPro}",
1406             "minionpro-osf":  "\\usepackage[onlytext]{MinionPro}",
1407             "palatino":  "\\renewcommand{\\rmdefault}{ppl}",
1408             "palatino-osf":  "\\renewcommand{\\rmdefault}{pplj}",
1409             "times":  "\\renewcommand{\\rmdefault}{ptm}",
1410             "utopia":  "\\renewcommand{\\rmdefault}{futs}",
1411             "utopia-osf":  "\\renewcommand{\\rmdefault}{futj}",
1412             }
1413             j = find_token(document.header, "\\font_roman", 0)
1414             if j != -1:
1415                 rm = get_value(document.header, "\\font_roman", j)
1416                 k = find_token(document.header, "\\font_osf true", 0)
1417                 if k != -1:
1418                     rm += "-osf"
1419                 if rm in list(mathfont_dict.keys()):
1420                     add_to_preamble(document, mathfont_dict[rm])
1421                     document.header[j] = "\\font_roman default"
1422                     if k != -1:
1423                         document.header[k] = "\\font_osf false"
1424     del document.header[i]
1425
1426
1427 def revert_mdnomath(document):
1428     " Revert mathdesign and fourier without math "
1429
1430     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1431         mathdesign_dict = {
1432         "md-charter": "mdbch",
1433         "md-utopia": "mdput",
1434         "md-garamond": "mdugm"
1435         }
1436         i = find_token(document.header, "\\font_roman", 0)
1437         if i == -1:
1438             return
1439         val = get_value(document.header, "\\font_roman", i)
1440         if val in list(mathdesign_dict.keys()):
1441             j = find_token(document.header, "\\font_math", 0)
1442             if j == -1:
1443                 document.header[i] = "\\font_roman %s" % mathdesign_dict[val]
1444             mval = get_value(document.header, "\\font_math", j)
1445             if mval == "default":
1446                 document.header[i] = "\\font_roman default"
1447                 add_to_preamble(document, "\\renewcommand{\\rmdefault}{%s}" % mathdesign_dict[val])
1448             else:
1449                 document.header[i] = "\\font_roman %s" % mathdesign_dict[val]
1450
1451
1452 def convert_mathfonts(document):
1453     document.header.insert(-1, "\\font_math auto")
1454
1455
1456 def convert_mdnomath(document):
1457     " Change mathdesign font name "
1458
1459     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1460         mathdesign_dict = {
1461         "mdbch":  "md-charter",
1462         "mdput":  "md-utopia",
1463         "mdugm":  "md-garamond"
1464         }
1465         i = find_token(document.header, "\\font_roman", 0)
1466         if i == -1:
1467             return
1468         val = get_value(document.header, "\\font_roman", i)
1469         if val in list(mathdesign_dict.keys()):
1470              document.header[i] = "\\font_roman %s" % mathdesign_dict[val]
1471
1472
1473 def revert_newtxmath(document):
1474     " Revert native newtxmath definitions to LaTeX "
1475
1476     i = find_token(document.header, "\\font_math", 0)
1477     if i == -1:
1478        return
1479     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1480         val = get_value(document.header, "\\font_math", i)
1481         mathfont_dict = {
1482         "libertine-ntxm":  "\\usepackage[libertine]{newtxmath}",
1483         "minion-ntxm":  "\\usepackage[minion]{newtxmath}",
1484         "newtxmath":  "\\usepackage{newtxmath}",
1485         }
1486         if val in list(mathfont_dict.keys()):
1487             add_to_preamble(document, mathfont_dict[val])
1488             document.header[i] = "\\font_math auto"
1489
1490
1491 def revert_biolinum(document):
1492     " Revert native biolinum font definition to LaTeX "
1493
1494     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1495         i = find_token(document.header, "\\font_sans biolinum", 0)
1496         if i != -1:
1497             osf = False
1498             j = find_token(document.header, "\\font_osf true", 0)
1499             if j != -1:
1500                 osf = True
1501             preamble = "\\usepackage"
1502             if not osf:
1503                 preamble += "[lf]"
1504             preamble += "{biolinum-type1}"
1505             add_to_preamble(document, [preamble])
1506             document.header[i] = "\\font_sans default"
1507
1508
1509 def revert_uop(document):
1510     " Revert native URW Classico (Optima) font definition to LaTeX "
1511
1512     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1513         i = find_token(document.header, "\\font_sans uop", 0)
1514         if i != -1:
1515                 preamble = "\\renewcommand{\\sfdefault}{uop}"
1516                 add_to_preamble(document, [preamble])
1517                 document.header[i] = "\\font_sans default"
1518
1519
1520 def convert_latexargs(document):
1521     " Convert InsetArgument to new syntax "
1522
1523     if find_token(document.body, "\\begin_inset Argument", 0) == -1:
1524         # nothing to do.
1525         return
1526
1527     # A list of layouts (document classes) with only optional or no arguments.
1528     # These can be safely converted to the new syntax
1529     # (I took the liberty to add some of my personal layouts/modules here; JSP)
1530     safe_layouts = ["aa", "aapaper", "aastex", "achemso", "acmsiggraph", "AEA",
1531                     "agu-dtd", "agums", "agutex", "amsart", "amsbook", "apa",
1532                     "arab-article", "armenian-article", "article-beamer", "article",
1533                     "beamer", "book", "broadway", "chess", "cl2emult", "ctex-article",
1534                     "ctex-book", "ctex-report", "dinbrief", "docbook-book", "docbook-chapter",
1535                     "docbook", "docbook-section", "doublecol-new", "dtk", "ectaart", "egs",
1536                     "elsarticle", "elsart", "entcs", "europecv", "extarticle", "extbook",
1537                     "extletter", "extreport", "foils", "frletter", "g-brief2", "g-brief",
1538                     "heb-article", "heb-letter", "hollywood", "IEEEtran", "ijmpc", "ijmpd",
1539                     "iopart", "isprs", "jarticle", "jasatex", "jbook", "jgrga", "jreport",
1540                     "jsarticle", "jsbeamer", "jsbook", "jss", "kluwer", "latex8", "letter", "lettre",
1541                     "literate-article", "literate-book", "literate-report", "llncs", "ltugboat",
1542                     "memoir", "moderncv", "mwart", "mwbk", "mwrep", "paper", "powerdot",
1543                     "recipebook", "report", "revtex4", "revtex", "scrartcl", "scrarticle-beamer",
1544                     "scrbook", "scrlettr", "scrlttr2", "scrreprt", "seminar", "siamltex",
1545                     "sigplanconf", "simplecv", "singlecol", "singlecol-new", "slides", "spie",
1546                     "svglobal3", "svglobal", "svjog", "svmono", "svmult", "svprobth", "tarticle",
1547                     "tbook", "treport", "tufte-book", "tufte-handout"]
1548     # A list of "safe" modules, same as above
1549     safe_modules = ["biblatex", "beameraddons", "beamer-resenumerate", "beamersession", "braille",
1550                     "customHeadersFooters", "endnotes", "enumitem", "eqs-within-sections", "figs-within-sections",
1551                     "fix-cm", "fixltx2e", "foottoend", "hanging", "jscharstyles", "knitr", "lilypond",
1552                     "linguistics", "linguisticx", "logicalmkup", "minimalistic", "nomindex", "noweb",
1553                     "pdfcomment", "sweave", "tabs-within-sections", "theorems-ams-bytype",
1554                     "theorems-ams-extended-bytype", "theorems-ams-extended", "theorems-ams", "theorems-bytype",
1555                     "theorems-chap-bytype", "theorems-chap", "theorems-named", "theorems-sec-bytype",
1556                     "theorems-sec", "theorems-starred", "theorems-std", "todonotes"]
1557     # Modules we need to take care of
1558     caveat_modules = ["initials"] # TODO: , "graphicboxes", "bicaption"]
1559     # information about the relevant styles in caveat_modules (number of opt and req args)
1560     # use this if we get more caveat_modules. For now, use hard coding (see below).
1561     # initials = [{'Layout' : 'Initial', 'opt' : 1, 'req' : 1}]
1562     # graphicboxes = { ... }
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     for captype in caption_dict.keys():
3661         i = 0
3662         while True:
3663             i = find_token(document.body, "\\begin_layout " + captype, i)
3664             if i == -1:
3665                 break
3666             j = find_end_of_layout(document.body, i)
3667             if j == -1:
3668                 document.warning("Malformed LyX document: Missing `\\end_layout'.")
3669                 break
3670
3671             document.body[j:j] = ["\\end_layout", "", "\\end_inset", "", ""]
3672             document.body[i:i+1] = ["\\begin_layout %s" % document.default_layout,
3673                                     "\\begin_inset Caption %s" % caption_dict[captype], "",
3674                                     "\\begin_layout %s" % document.default_layout]
3675             i = j + 1
3676
3677
3678 def revert_captionlayouts(document):
3679     " Revert caption insets to caption layouts. "
3680
3681     caption_dict = {
3682         "Above" : "Captionabove",
3683         "Below" : "Captionbelow",
3684         "FigCaption"  :  "FigCaption",
3685         "Table" : "Table_Caption",
3686         "Centered" : "CenteredCaption",
3687         "Bicaption" : "Bicaption",
3688         }
3689
3690     i = 0
3691     rx = re.compile(r'^\\begin_inset Caption (\S+)$')
3692     while True:
3693         i = find_token(document.body, "\\begin_inset Caption", i)
3694         if i == -1:
3695             return
3696
3697         m = rx.match(document.body[i])
3698         val = ""
3699         if m:
3700             val = m.group(1)
3701         if val not in list(caption_dict.keys()):
3702             i += 1
3703             continue
3704
3705         # We either need to delete the previous \begin_layout line, or we
3706         # need to end the previous layout if this inset is not in the first
3707         # position of the paragraph.
3708         layout_before = find_token_backwards(document.body, "\\begin_layout", i)
3709         if layout_before == -1:
3710             document.warning("Malformed LyX document: Missing `\\begin_layout'.")
3711             return
3712         layout_line = document.body[layout_before]
3713         del_layout_before = True
3714         l = layout_before + 1
3715         while l < i:
3716             if document.body[l] != "":
3717                 del_layout_before = False
3718                 break
3719             l = l + 1
3720         if del_layout_before:
3721             del document.body[layout_before:i]
3722             i = layout_before
3723         else:
3724             document.body[i:i] = ["\\end_layout", ""]
3725             i = i + 2
3726
3727         # Find start of layout in the inset and end of inset
3728         j = find_token(document.body, "\\begin_layout", i)
3729         if j == -1:
3730             document.warning("Malformed LyX document: Missing `\\begin_layout'.")
3731             return
3732         k = find_end_of_inset(document.body, i)
3733         if k == -1:
3734             document.warning("Malformed LyX document: Missing `\\end_inset'.")
3735             return
3736
3737         # We either need to delete the following \end_layout line, or we need
3738         # to restart the old layout if this inset is not at the paragraph end.
3739         layout_after = find_token(document.body, "\\end_layout", k)
3740         if layout_after == -1:
3741             document.warning("Malformed LyX document: Missing `\\end_layout'.")
3742             return
3743         del_layout_after = True
3744         l = k + 1
3745         while l < layout_after:
3746             if document.body[l] != "":
3747                 del_layout_after = False
3748                 break
3749             l = l + 1
3750         if del_layout_after:
3751             del document.body[k+1:layout_after+1]
3752         else:
3753             document.body[k+1:k+1] = [layout_line, ""]
3754
3755         # delete \begin_layout and \end_inset and replace \begin_inset with
3756         # "\begin_layout XXX". This works because we can only have one
3757         # paragraph in the caption inset: The old \end_layout will be recycled.
3758         del document.body[k]
3759         if document.body[k] == "":
3760             del document.body[k]
3761         del document.body[j]
3762         if document.body[j] == "":
3763             del document.body[j]
3764         document.body[i] = "\\begin_layout %s" % caption_dict[val]
3765         if document.body[i+1] == "":
3766             del document.body[i+1]
3767         i += 1
3768
3769
3770 def revert_fragileframe(document):
3771     " Reverts beamer FragileFrame layout to ERT "
3772
3773     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3774     if document.textclass not in beamer_classes:
3775         return
3776
3777     i = 0
3778     while True:
3779         i = find_token(document.body, "\\begin_layout FragileFrame", i)
3780         if i == -1:
3781             return
3782         # Find end of sequence
3783         j = find_end_of_sequence(document.body, i)
3784         if j == -1:
3785             document.warning("Malformed LyX document. Cannot find end of FragileFrame sequence!")
3786             i += 1
3787             continue
3788         endseq = j
3789         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\begin{frame}")
3790         esubst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\end{frame}")
3791         endseq = endseq + len(esubst) - len(document.body[j : j])
3792         if document.body[j] == "\\end_deeper":
3793             document.body[j : j] = [""] + esubst + ["", "\\end_layout"]
3794         else:
3795             document.body[j : j] = esubst
3796         for q in range(i, j):
3797             if document.body[q] == "\\begin_layout FragileFrame":
3798                 document.body[q] = "\\begin_layout %s" % document.default_layout
3799         r = i
3800         while r < j:
3801             if document.body[r] == "\\begin_deeper":
3802                 s = find_end_of(document.body, r, "\\begin_deeper", "\\end_deeper")
3803                 if s != -1:
3804                     document.body[r] = ""
3805                     document.body[s] = ""
3806                     r = s
3807                     continue
3808             r = r + 1
3809         for p in range(1, 5):
3810             arg = find_token(document.body, "\\begin_inset Argument %d" % p, i, j)
3811             if arg != -1:
3812                 if p == 1:
3813                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3814                     endPlain = find_end_of_layout(document.body, beginPlain)
3815                     endInset = find_end_of_inset(document.body, arg)
3816                     content = document.body[beginPlain + 1 : endPlain]
3817                     # Adjust range end
3818                     j = j - len(document.body[arg : endInset + 1])
3819                     # Remove arg inset
3820                     del document.body[arg : endInset + 1]
3821                     subst += put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
3822                 elif p == 2:
3823                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3824                     endPlain = find_end_of_layout(document.body, beginPlain)
3825                     endInset = find_end_of_inset(document.body, arg)
3826                     content = document.body[beginPlain + 1 : endPlain]
3827                     # Adjust range end
3828                     j = j - len(document.body[arg : endInset + 1])
3829                     # Remove arg inset
3830                     del document.body[arg : endInset + 1]
3831                     subst += put_cmd_in_ert("[<") + content + put_cmd_in_ert(">]")
3832                 elif p == 3:
3833                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3834                     endPlain = find_end_of_layout(document.body, beginPlain)
3835                     endInset = find_end_of_inset(document.body, arg)
3836                     content = document.body[beginPlain + 1 : endPlain]
3837                     # Adjust range end
3838                     j = j - len(document.body[arg : endInset + 1])
3839                     # Remove arg inset
3840                     del document.body[arg : endInset + 1]
3841                     subst += put_cmd_in_ert("[fragile,") + content + put_cmd_in_ert("]")
3842                 elif p == 4:
3843                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3844                     endPlain = find_end_of_layout(document.body, beginPlain)
3845                     endInset = find_end_of_inset(document.body, arg)
3846                     content = document.body[beginPlain + 1 : endPlain]
3847                     # Adjust range end
3848                     j = j - len(document.body[arg : endInset + 1])
3849                     # Remove arg inset
3850                     del document.body[arg : endInset + 1]
3851                     subst += put_cmd_in_ert("{") + content + put_cmd_in_ert("}")
3852             elif p == 3:
3853                 subst += put_cmd_in_ert("[fragile]")
3854
3855         document.body[i : i + 1] = subst
3856         i = j
3857
3858
3859 def revert_newframes(document):
3860     " Reverts beamer Frame and PlainFrame layouts to old forms "
3861
3862     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3863     if document.textclass not in beamer_classes:
3864         return
3865
3866     frame_dict = {
3867         "Frame" : "BeginFrame",
3868         "PlainFrame" : "BeginPlainFrame",
3869         }
3870
3871     rx = re.compile(r'^\\begin_layout (\S+)$')
3872     i = 0
3873     while True:
3874         i = find_token(document.body, "\\begin_layout", i)
3875         if i == -1:
3876             return
3877
3878         m = rx.match(document.body[i])
3879         val = ""
3880         if m:
3881             val = m.group(1)
3882         if val not in list(frame_dict.keys()):
3883             i += 1
3884             continue
3885         # Find end of sequence
3886         j = find_end_of_sequence(document.body, i)
3887         if j == -1:
3888             document.warning("Malformed LyX document. Cannot find end of Frame sequence!")
3889             i += 1
3890             continue
3891         endseq = j
3892         subst = ["\\begin_layout %s" % frame_dict[val]]
3893         esubst = ["", "\\begin_layout EndFrame", "", "\\end_layout"]
3894         endseq = endseq + len(esubst) - len(document.body[j : j])
3895         if document.body[j] == "\\end_deeper":
3896             document.body[j : j] = esubst
3897         else:
3898             document.body[j+1 : j+1] = esubst
3899         for q in range(i, j):
3900             if document.body[q] == "\\begin_layout %s" % val:
3901                 document.body[q] = "\\begin_layout %s" % document.default_layout
3902         r = i
3903         while r < j:
3904             if document.body[r] == "\\begin_deeper":
3905                 s = find_end_of(document.body, r, "\\begin_deeper", "\\end_deeper")
3906                 if s != -1:
3907                     document.body[r] = ""
3908                     document.body[s] = ""
3909                     r = s
3910                     continue
3911             r = r + 1
3912         l = find_end_of_layout(document.body, i)
3913         for p in range(1, 5):
3914             arg = find_token(document.body, "\\begin_inset Argument %d" % p, i, l)
3915             if arg != -1:
3916                 if p == 1:
3917                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3918                     endPlain = find_end_of_layout(document.body, beginPlain)
3919                     endInset = find_end_of_inset(document.body, arg)
3920                     content = document.body[beginPlain + 1 : endPlain]
3921                     # Adjust range end
3922                     l = l - len(document.body[arg : endInset + 1])
3923                     # Remove arg inset
3924                     del document.body[arg : endInset + 1]
3925                     subst += put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
3926                 elif p == 2:
3927                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3928                     endPlain = find_end_of_layout(document.body, beginPlain)
3929                     endInset = find_end_of_inset(document.body, arg)
3930                     content = document.body[beginPlain + 1 : endPlain]
3931                     # Adjust range end
3932                     l = l - len(document.body[arg : endInset + 1])
3933                     # Remove arg inset
3934                     del document.body[arg : endInset + 1]
3935                     subst += put_cmd_in_ert("[<") + content + put_cmd_in_ert(">]")
3936                 elif p == 3:
3937                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3938                     endPlain = find_end_of_layout(document.body, beginPlain)
3939                     endInset = find_end_of_inset(document.body, arg)
3940                     content = document.body[beginPlain + 1 : endPlain]
3941                     # Adjust range end
3942                     l = l - len(document.body[arg : endInset + 1])
3943                     # Remove arg inset
3944                     del document.body[arg : endInset + 1]
3945                     subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
3946                 elif p == 4:
3947                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3948                     endPlain = find_end_of_layout(document.body, beginPlain)
3949                     endInset = find_end_of_inset(document.body, arg)
3950                     content = document.body[beginPlain + 1 : endPlain]
3951                     # Adjust range end
3952                     l = l - len(document.body[arg : endInset + 1])
3953                     # Remove arg inset
3954                     del document.body[arg : endInset + 1]
3955                     subst += content
3956
3957         document.body[i : i + 1] = subst
3958         i = j
3959
3960 # known encodings that do not change their names (same LyX and LaTeX names)
3961 known_enc_tuple = ("auto", "default", "ansinew", "applemac", "armscii8", "ascii",
3962     "cp437", "cp437de", "cp850", "cp852", "cp855", "cp858", "cp862", "cp865", "cp866",
3963     "cp1250", "cp1251", "cp1252", "cp1255", "cp1256", "cp1257", "koi8-r", "koi8-u",
3964     "pt154", "pt254", "tis620-0", "utf8", "utf8x", "utf8-plain")
3965
3966 def convert_encodings(document):
3967     "Use the LyX names of the encodings instead of the LaTeX names."
3968     LaTeX2LyX_enc_dict = {
3969         "8859-6":     "iso8859-6",
3970         "8859-8":     "iso8859-8",
3971         "Bg5":        "big5",
3972         "euc":        "euc-jp-platex",
3973         "EUC-JP":     "euc-jp",
3974         "EUC-TW":     "euc-tw",
3975         "GB":         "euc-cn",
3976         "GBK":        "gbk",
3977         "iso88595":   "iso8859-5",
3978         "iso-8859-7": "iso8859-7",
3979         "JIS":        "jis",
3980         "jis":        "jis-platex",
3981         "KS":         "euc-kr",
3982         "l7xenc":     "iso8859-13",
3983         "latin1":     "iso8859-1",
3984         "latin2":     "iso8859-2",
3985         "latin3":     "iso8859-3",
3986         "latin4":     "iso8859-4",
3987         "latin5":     "iso8859-9",
3988         "latin9":     "iso8859-15",
3989         "latin10":    "iso8859-16",
3990         "SJIS":       "shift-jis",
3991         "sjis":       "shift-jis-platex",
3992         "UTF8":       "utf8-cjk"
3993     }
3994     i = find_token(document.header, "\\inputencoding" , 0)
3995     if i == -1:
3996         return
3997     val = get_value(document.header, "\\inputencoding", i)
3998     if val in list(LaTeX2LyX_enc_dict.keys()):
3999         document.header[i] = "\\inputencoding %s" % LaTeX2LyX_enc_dict[val]
4000     elif val not in known_enc_tuple:
4001         document.warning("Ignoring unknown input encoding: `%s'" % val)
4002
4003
4004 def revert_encodings(document):
4005     """Revert to using the LaTeX names of the encodings instead of the LyX names.
4006     Also revert utf8-platex to sjis, the language default when using Japanese.
4007     """
4008     LyX2LaTeX_enc_dict = {
4009         "big5":             "Bg5",
4010         "euc-cn":           "GB",
4011         "euc-kr":           "KS",
4012         "euc-jp":           "EUC-JP",
4013         "euc-jp-platex":    "euc",
4014         "euc-tw":           "EUC-TW",
4015         "gbk":              "GBK",
4016         "iso8859-1":        "latin1",
4017         "iso8859-2":        "latin2",
4018         "iso8859-3":        "latin3",
4019         "iso8859-4":        "latin4",
4020         "iso8859-5":        "iso88595",
4021         "iso8859-6":        "8859-6",
4022         "iso8859-7":        "iso-8859-7",
4023         "iso8859-8":        "8859-8",
4024         "iso8859-9":        "latin5",
4025         "iso8859-13":       "l7xenc",
4026         "iso8859-15":       "latin9",
4027         "iso8859-16":       "latin10",
4028         "jis":              "JIS",
4029         "jis-platex":       "jis",
4030         "shift-jis":        "SJIS",
4031         "shift-jis-platex": "sjis",
4032         "utf8-cjk":         "UTF8",
4033         "utf8-platex":      "sjis"
4034     }
4035     i = find_token(document.header, "\\inputencoding" , 0)
4036     if i == -1:
4037         return
4038     val = get_value(document.header, "\\inputencoding", i)
4039     if val in list(LyX2LaTeX_enc_dict.keys()):
4040         document.header[i] = "\\inputencoding %s" % LyX2LaTeX_enc_dict[val]
4041     elif val not in known_enc_tuple:
4042         document.warning("Ignoring unknown input encoding: `%s'" % val)
4043
4044
4045 def revert_IEEEtran_3(document):
4046   '''
4047   Reverts Flex Insets to TeX-code
4048   '''
4049   if document.textclass == "IEEEtran":
4050     h = 0
4051     i = 0
4052     j = 0
4053     while True:
4054       if h != -1:
4055         h = find_token(document.body, "\\begin_inset Flex Author Mark", h)
4056       if h != -1:
4057         endh = find_end_of_inset(document.body, h)
4058         document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
4059         document.body[h : h + 4] = put_cmd_in_ert("\\IEEEauthorrefmark{")
4060         h = h + 5
4061       if i != -1:
4062         i = find_token(document.body, "\\begin_inset Flex Author Name", i)
4063       if i != -1:
4064         endi = find_end_of_inset(document.body, i)
4065         document.body[endi - 2 : endi + 1] = put_cmd_in_ert("}")
4066         document.body[i : i + 4] = put_cmd_in_ert("\\IEEEauthorblockN{")
4067         i = i + 5
4068       if j != -1:
4069         j = find_token(document.body, "\\begin_inset Flex Author Affiliation", j)
4070       if j != -1:
4071         endj = find_end_of_inset(document.body, j)
4072         document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
4073         document.body[j : j + 4] = put_cmd_in_ert("\\IEEEauthorblockA{")
4074         j = j + 5
4075       if i == -1 and j == -1 and h == -1:
4076         return
4077
4078
4079 def revert_kurier_fonts(document):
4080   " Revert kurier font definition to LaTeX "
4081
4082   i = find_token(document.header, "\\font_math", 0)
4083   if i != -1:
4084     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
4085       val = get_value(document.header, "\\font_math", i)
4086       if val == "kurier-math":
4087         add_to_preamble(document, "\\let\\Myrmdefault\\rmdefault\n" \
4088           "\\usepackage[math]{kurier}\n" \
4089           "\\renewcommand{\\rmdefault}{\\Myrmdefault}")
4090         document.header[i] = "\\font_math auto"
4091
4092   if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
4093     kurier_fonts = ["kurier", "kurierc", "kurierl", "kurierlc"]
4094     k = find_token(document.header, "\\font_sans kurier", 0)
4095     if k != -1:
4096       sf = get_value(document.header, "\\font_sans", k)
4097       if sf in kurier_fonts:
4098         add_to_preamble(document, "\\renewcommand{\\sfdefault}{%s}" % sf)
4099         document.header[k] = "\\font_sans default"
4100
4101 def revert_iwona_fonts(document):
4102   " Revert iwona font definition to LaTeX "
4103
4104   i = find_token(document.header, "\\font_math", 0)
4105   if i != -1:
4106     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
4107       val = get_value(document.header, "\\font_math", i)
4108       if val == "iwona-math":
4109         add_to_preamble(document, "\\let\\Myrmdefault\\rmdefault\n" \
4110           "\\usepackage[math]{iwona}\n" \
4111           "\\renewcommand{\\rmdefault}{\\Myrmdefault}")
4112         document.header[i] = "\\font_math auto"
4113
4114   if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
4115     iwona_fonts = ["iwona", "iwonac", "iwonal", "iwonalc"]
4116     k = find_token(document.header, "\\font_sans iwona", 0)
4117     if k != -1:
4118       sf = get_value(document.header, "\\font_sans", k)
4119       if sf in iwona_fonts:
4120         add_to_preamble(document, "\\renewcommand{\\sfdefault}{%s}" % sf)
4121         document.header[k] = "\\font_sans default"
4122
4123
4124 def revert_new_libertines(document):
4125     " Revert new libertine font definition to LaTeX "
4126
4127     if find_token(document.header, "\\use_non_tex_fonts true", 0) != -1:
4128         return
4129
4130     i = find_token(document.header, "\\font_typewriter libertine-mono", 0)
4131     if i != -1:
4132         preamble = "\\usepackage"
4133         sc = find_token(document.header, "\\font_tt_scale", 0)
4134         if sc != -1:
4135             scval = get_value(document.header, "\\font_tt_scale", sc)
4136             if scval != "100":
4137                 preamble += "[scale=%f]" % (float(scval) / 100)
4138                 document.header[sc] = "\\font_tt_scale 100"
4139         preamble += "{libertineMono-type1}"
4140         add_to_preamble(document, [preamble])
4141         document.header[i] = "\\font_typewriter default"
4142
4143     k = find_token(document.header, "\\font_sans biolinum", 0)
4144     if k != -1:
4145         preamble = "\\usepackage"
4146         options = ""
4147         j = find_token(document.header, "\\font_osf true", 0)
4148         if j != -1:
4149             options += "osf"
4150         else:
4151             options += "lining"
4152         sc = find_token(document.header, "\\font_sf_scale", 0)
4153         if sc != -1:
4154             scval = get_value(document.header, "\\font_sf_scale", sc)
4155             if scval != "100":
4156                 options += ",scale=%f" % (float(scval) / 100)
4157                 document.header[sc] = "\\font_sf_scale 100"
4158         if options != "":
4159             preamble += "[" + options +"]"
4160         preamble += "{biolinum-type1}"
4161         add_to_preamble(document, [preamble])
4162         document.header[k] = "\\font_sans default"
4163
4164
4165 def convert_lyxframes(document):
4166     " Converts old beamer frames to new style "
4167
4168     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
4169     if document.textclass not in beamer_classes:
4170         return
4171
4172     framebeg = ["BeginFrame", "BeginPlainFrame"]
4173     frameend = ["Frame", "PlainFrame", "EndFrame", "BeginFrame", "BeginPlainFrame", "AgainFrame",
4174                 "Section", "Section*", "Subsection", "Subsection*", "Subsubsection", "Subsubsection*"]
4175     for lay in framebeg:
4176         i = 0
4177         while True:
4178             i = find_token_exact(document.body, "\\begin_layout " + lay, i)
4179             if i == -1:
4180                 break
4181             parent = get_containing_layout(document.body, i)
4182             if parent == False or parent[1] != i:
4183                 document.warning("Wrong parent layout!")
4184                 i += 1
4185                 continue
4186             frametype = parent[0]
4187             j = parent[2]
4188             parbeg = parent[3]
4189             if i != -1:
4190                 # Step I: Convert ERT arguments
4191                 # FIXME: See restrictions in convert_beamerframeargs method
4192                 ertend = convert_beamerframeargs(document, i, parbeg)
4193                 if ertend == -1:
4194                     break
4195                 # Step II: Now rename the layout and convert the title to an argument
4196                 j = find_end_of_layout(document.body, i)
4197                 document.body[j : j + 1] = ['\\end_layout', '', '\\end_inset', '', '\\end_layout']
4198                 if lay == "BeginFrame":
4199                     document.body[i] = "\\begin_layout Frame"
4200                 else:
4201                     document.body[i] = "\\begin_layout PlainFrame"
4202                 document.body[ertend + 1 : ertend + 1] = ['\\begin_inset Argument 4',
4203                                                 'status open', '', '\\begin_layout Plain Layout']
4204                 # Step III: find real frame end
4205                 j = j + 8
4206                 jj = j
4207                 inInset = get_containing_inset(document.body, i)
4208                 while True:
4209                     fend = find_token(document.body, "\\begin_layout", jj)
4210                     if fend == -1:
4211                         document.warning("Malformed LyX document: No real frame end!")
4212                         return
4213                     val = get_value(document.body, "\\begin_layout", fend)
4214                     if val not in frameend:
4215                         jj = fend + 1
4216                         continue
4217                     # is this frame nested in an inset (e.g., Note)?
4218                     if inInset != False:
4219                         # if so, end the frame inside the inset
4220                         if inInset[2] < fend:
4221                             fend = inInset[2]
4222                     if val == frametype:
4223                         document.body[fend : fend] = ['\\end_deeper', '', '\\begin_layout Separator', '', '\\end_layout']
4224                     # consider explicit EndFrames between two identical frame types
4225                     elif val == "EndFrame":
4226                         nextlayout = find_token(document.body, "\\begin_layout", fend + 1)
4227                         if nextlayout != -1 and get_value(document.body, "\\begin_layout", nextlayout) == frametype:
4228                             document.body[fend : fend] = ['\\end_deeper', '', '\\begin_layout Separator', '', '\\end_layout']
4229                         else:
4230                             document.body[fend : fend] = ['\\end_deeper']
4231                     else:
4232                         document.body[fend : fend] = ['\\end_deeper']
4233                     document.body[j + 1 : j + 1] = ['', '\\begin_deeper']
4234                     break
4235             i = j
4236
4237
4238 def remove_endframes(document):
4239     " Remove deprecated beamer endframes "
4240
4241     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
4242     if document.textclass not in beamer_classes:
4243         return
4244
4245     i = 0
4246     while True:
4247         i = find_token_exact(document.body, "\\begin_layout EndFrame", i)
4248         if i == -1:
4249             break
4250         j = find_end_of_layout(document.body, i)
4251         if j == -1:
4252             document.warning("Malformed LyX document: Missing \\end_layout to EndFrame")
4253             i += 1
4254             continue
4255         del document.body[i : j + 1]
4256
4257
4258 def revert_powerdot_flexes(document):
4259     " Reverts powerdot flex insets "
4260
4261     if document.textclass != "powerdot":
4262         return
4263
4264     flexes = {"Onslide" : "\\onslide",
4265               "Onslide*" : "\\onslide*",
4266               "Onslide+" : "\\onslide+"}
4267     rx = re.compile(r'^\\begin_inset Flex (.+)$')
4268
4269     i = 0
4270     while True:
4271         i = find_token(document.body, "\\begin_inset Flex", i)
4272         if i == -1:
4273             return
4274         m = rx.match(document.body[i])
4275         if m:
4276             flextype = m.group(1)
4277             z = find_end_of_inset(document.body, i)
4278             if z == -1:
4279                 document.warning("Can't find end of Flex " + flextype + " inset.")
4280                 i += 1
4281                 continue
4282             if flextype in flexes:
4283                 pre = put_cmd_in_ert(flexes[flextype])
4284                 arg = find_token(document.body, "\\begin_inset Argument 1", i, z)
4285                 if arg != -1:
4286                     argend = find_end_of_inset(document.body, arg)
4287                     if argend == -1:
4288                         document.warning("Can't find end of Argument!")
4289                         i += 1
4290                         continue
4291                     # Find containing paragraph layout
4292                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
4293                     endPlain = find_end_of_layout(document.body, beginPlain)
4294                     argcontent = document.body[beginPlain + 1 : endPlain]
4295                     # Adjust range end
4296                     z = z - len(document.body[arg : argend + 1])
4297                     # Remove arg inset
4298                     del document.body[arg : argend + 1]
4299                     pre += put_cmd_in_ert("{") + argcontent + put_cmd_in_ert("}")
4300                 pre += put_cmd_in_ert("{")
4301                 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
4302                 endPlain = find_end_of_layout(document.body, beginPlain)
4303                 # Adjust range end
4304                 z = z - len(document.body[i : beginPlain + 1])
4305                 z += len(pre)
4306                 document.body[i : beginPlain + 1] = pre
4307                 post = put_cmd_in_ert("}")
4308                 document.body[z - 2 : z + 1] = post
4309         i += 1
4310
4311
4312 def revert_powerdot_pause(document):
4313     " Reverts powerdot pause layout to ERT "
4314
4315     if document.textclass != "powerdot":
4316         return
4317
4318     i = 0
4319     while True:
4320         i = find_token(document.body, "\\begin_layout Pause", i)
4321         if i == -1:
4322             return
4323         j = find_end_of_layout(document.body, i)
4324         if j == -1:
4325             document.warning("Malformed LyX document: Can't find end of Pause layout")
4326             i += 1
4327             continue
4328         endlay = j
4329         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\pause")
4330         for p in range(i, j):
4331             if p >= endlay:
4332                 break
4333             arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
4334             if arg != -1:
4335                 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
4336                 endPlain = find_end_of_layout(document.body, beginPlain)
4337                 endInset = find_end_of_inset(document.body, p)
4338                 content = document.body[beginPlain + 1 : endPlain]
4339                 # Adjust range end
4340                 endlay = endlay - len(document.body[p : endInset + 1])
4341                 # Remove arg inset
4342                 del document.body[p : endInset + 1]
4343                 subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
4344
4345         document.body[i : i + 1] = subst
4346         i = endlay
4347
4348
4349 def revert_powerdot_itemargs(document):
4350     " Reverts powerdot item arguments to ERT "
4351
4352     if document.textclass != "powerdot":
4353         return
4354
4355     i = 0
4356     list_layouts = ["Itemize", "ItemizeType1", "Enumerate", "EnumerateType1"]
4357     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
4358
4359     while True:
4360         i = find_token(document.body, "\\begin_inset Argument", i)
4361         if i == -1:
4362             return
4363         # Find containing paragraph layout
4364         parent = get_containing_layout(document.body, i)
4365         if parent == False:
4366             document.warning("Malformed LyX document: Can't find parent paragraph layout")
4367             i += 1
4368             continue
4369         parbeg = parent[1]
4370         parend = parent[2]
4371         realparbeg = parent[3]
4372         layoutname = parent[0]
4373         realparend = parend
4374         for p in range(parbeg, parend):
4375             if p >= realparend:
4376                 i = realparend
4377                 break
4378             if layoutname in list_layouts:
4379                 m = rx.match(document.body[p])
4380                 if m:
4381                     argnr = m.group(1)
4382                     if argnr == "item:1":
4383                         j = find_end_of_inset(document.body, i)
4384                         # Find containing paragraph layout
4385                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
4386                         endPlain = find_end_of_layout(document.body, beginPlain)
4387                         content = document.body[beginPlain + 1 : endPlain]
4388                         del document.body[i:j+1]
4389                         subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
4390                         document.body[realparbeg : realparbeg] = subst
4391                     elif argnr == "item:2":
4392                         j = find_end_of_inset(document.body, i)
4393                         # Find containing paragraph layout
4394                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
4395                         endPlain = find_end_of_layout(document.body, beginPlain)
4396                         content = document.body[beginPlain + 1 : endPlain]
4397                         del document.body[i:j+1]
4398                         subst = put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
4399                         document.body[realparbeg : realparbeg] = subst
4400
4401         i = realparend
4402
4403
4404 def revert_powerdot_columns(document):
4405     " Reverts powerdot twocolumn to TeX-code "
4406     if document.textclass != "powerdot":
4407         return
4408
4409     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
4410     i = 0
4411     while True:
4412         i = find_token(document.body, "\\begin_layout Twocolumn", i)
4413         if i == -1:
4414             return
4415         j = find_end_of_layout(document.body, i)
4416         if j == -1:
4417             document.warning("Malformed LyX document: Can't find end of Twocolumn layout")
4418             i += 1
4419             continue
4420         endlay = j
4421         document.body[j : j] = put_cmd_in_ert("}") + document.body[j : j]
4422         endlay += len(put_cmd_in_ert("}"))
4423         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\twocolumn")
4424         for p in range(i, j):
4425             if p >= endlay:
4426                 break
4427             m = rx.match(document.body[p])
4428             if m:
4429                 argnr = m.group(1)
4430                 if argnr == "1":
4431                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
4432                     endPlain = find_end_of_layout(document.body, beginPlain)
4433                     endInset = find_end_of_inset(document.body, p)
4434                     content = document.body[beginPlain + 1 : endPlain]
4435                     # Adjust range end
4436                     endlay = endlay - len(document.body[p : endInset + 1])
4437                     # Remove arg inset
4438                     del document.body[p : endInset + 1]
4439                     subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
4440                 elif argnr == "2":
4441                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
4442                     endPlain = find_end_of_layout(document.body, beginPlain)
4443                     endInset = find_end_of_inset(document.body, p)
4444                     content = document.body[beginPlain + 1 : endPlain]
4445                     # Adjust range end
4446                     endlay = endlay - len(document.body[p : endInset + 1])
4447                     # Remove arg inset
4448                     del document.body[p : endInset + 1]
4449                     subst += put_cmd_in_ert("{") + content + put_cmd_in_ert("}")
4450
4451         subst += put_cmd_in_ert("{")
4452         document.body[i : i + 1] = subst
4453         i = endlay
4454
4455
4456 def revert_mbox_fbox(document):
4457     'Convert revert mbox/fbox boxes to TeX-code'
4458     i = 0
4459     while True:
4460         i = find_token(document.body, "\\begin_inset Box", i)
4461         if i == -1:
4462             return
4463         j = find_token(document.body, "width", i)
4464         if j != i + 7:
4465             document.warning("Malformed LyX document: Can't find box width")
4466             return
4467         width = get_value(document.body, "width", j)
4468         k = find_end_of_inset(document.body, j)
4469         if k == -1:
4470             document.warning("Malformed LyX document: Can't find end of box inset")
4471             i += 1
4472             continue
4473         BeginLayout = find_token(document.body, "\\begin_layout Plain Layout", j)
4474         EndLayout = find_end_of_layout(document.body, BeginLayout)
4475         # replace if width is ""
4476         if (width == '""'):
4477             document.body[EndLayout:k + 1] = put_cmd_in_ert("}")
4478             if document.body[i] == "\\begin_inset Box Frameless":
4479                 document.body[i:BeginLayout + 1] = put_cmd_in_ert("\\mbox{")
4480             if document.body[i] == "\\begin_inset Box Boxed":
4481                 document.body[i:BeginLayout + 1] = put_cmd_in_ert("\\fbox{")
4482         i += 1
4483
4484
4485 def revert_starred_caption(document):
4486     " Reverts unnumbered longtable caption insets "
4487
4488     i = 0
4489     while True:
4490       i = find_token(document.body, "\\begin_inset Caption LongTableNoNumber", i)
4491       if i == -1:
4492           return
4493       # This is not equivalent, but since the caption inset is a full blown
4494       # text inset a true conversion to ERT is too difficult.
4495       document.body[i] = "\\begin_inset Caption Standard"
4496       i += 1
4497
4498
4499 def revert_forced_local_layout(document):
4500     i = 0
4501     while True:
4502         i = find_token(document.header, "\\begin_forced_local_layout", i)
4503         if i == -1:
4504             return
4505         j = find_end_of(document.header, i, "\\begin_forced_local_layout", "\\end_forced_local_layout")
4506         if j == -1:
4507             # this should not happen
4508             break
4509         regexp = re.compile(r'\s*forcelocal', re.IGNORECASE)
4510         k = find_re(document.header, regexp, i, j)
4511         while k != -1:
4512             del document.header[k]
4513             j = j - 1
4514             k = find_re(document.header, regexp, i, j)
4515         k = find_token(document.header, "\\begin_local_layout", 0)
4516         if k == -1:
4517             document.header[i] = "\\begin_local_layout"
4518             document.header[j] = "\\end_local_layout"
4519         else:
4520             l = find_end_of(document.header, k, "\\begin_local_layout", "\\end_local_layout")
4521             if j == -1:
4522                 # this should not happen
4523                 break
4524             lines = document.header[i+1 : j]
4525             if k > i:
4526                 document.header[k+1 : k+1] = lines
4527                 document.header[i   : j  ] = []
4528             else:
4529                 document.header[i   : j  ] = []
4530                 document.header[k+1 : k+1] = lines
4531
4532
4533 def revert_aa1(document):
4534   " Reverts InsetArguments of aa to TeX-code "
4535   if document.textclass == "aa":
4536     i = 0
4537     while True:
4538       if i != -1:
4539         i = find_token(document.body, "\\begin_layout Abstract (structured)", i)
4540       if i != -1:
4541         revert_Argument_to_TeX_brace(document, i, 0, 1, 4, False, False)
4542         i += 1
4543       if i == -1:
4544         return
4545
4546
4547 def revert_aa2(document):
4548   " Reverts InsetArguments of aa to TeX-code "
4549   if document.textclass == "aa":
4550     i = 0
4551     while True:
4552       if i != -1:
4553         i = find_token(document.body, "\\begin_layout Abstract (structured)", i)
4554       if i != -1:
4555         document.body[i] = "\\begin_layout Abstract"
4556         i += 1
4557       if i == -1:
4558         return
4559
4560
4561 def revert_tibetan(document):
4562     "Set the document language for Tibetan to English"
4563
4564     if document.language == "tibetan":
4565         document.language = "english"
4566         i = find_token(document.header, "\\language", 0)
4567         if i != -1:
4568             document.header[i] = "\\language english"
4569     j = 0
4570     while j < len(document.body):
4571         j = find_token(document.body, "\\lang tibetan", j)
4572         if j != -1:
4573             document.body[j] = document.body[j].replace("\\lang tibetan", "\\lang english")
4574             j += 1
4575         else:
4576             j = len(document.body)
4577
4578
4579 #############
4580 #
4581 # Chunk stuff
4582 #
4583 #############
4584
4585 # The idea here is that we will have a sequence of chunk paragraphs.
4586 # We want to convert them to paragraphs in one or several chunk insets.
4587 # Individual chunks are terminated by the character @ on the last line.
4588 # This line will be discarded, and following lines are treated as new
4589 # chunks, which go into their own insets.
4590 # The first line of a chunk should look like: <<CONTENT>>=
4591 # We will discard the delimiters, and put the CONTENT into the
4592 # optional argument of the inset, if the CONTENT is non-empty.
4593 def convert_chunks(document):
4594     first_re = re.compile(r'<<(.*)>>=(.*)')
4595     file_pos = 0
4596     while True:
4597         # find start of a block of chunks
4598         i = find_token(document.body, "\\begin_layout Chunk", file_pos)
4599         if i == -1:
4600             return
4601         start = i
4602         end = -1
4603         contents = []
4604         chunk_started = False
4605
4606         while True:
4607             # process the one we just found
4608             j = find_end_of_layout(document.body, i)
4609             if j == -1:
4610                 document.warning("Malformed LyX documents. Can't find end of Chunk layout!")
4611                 # there is no point continuing, as we will run into the same error again.
4612                 return
4613             this_chunk = "".join(document.body[i + 1:j])
4614
4615             # there may be empty lines between chunks
4616             # we just skip them.
4617             if not chunk_started:
4618                 if this_chunk != "":
4619                     # new chunk starts
4620                     chunk_started = True
4621
4622             if chunk_started:
4623                 contents.append(document.body[i + 1:j])
4624
4625             # look for potential chunk terminator
4626             # on the last line of the chunk paragraph
4627             if document.body[j - 1] == "@":
4628                 break
4629
4630             # look for subsequent chunk paragraph
4631             i = find_token(document.body, "\\begin_layout", j)
4632             if i == -1:
4633                 break
4634
4635             if get_value(document.body, "\\begin_layout", i) != "Chunk":
4636                 break
4637
4638         file_pos = end = j + 1
4639
4640         # The last chunk should simply have an "@" in it
4641         # or at least end with "@" (can happen if @ is
4642         # preceded by a newline)
4643         lastpar = ''
4644         if len(contents) > 0:
4645             lastpar = ''.join(contents[-1])
4646         if not lastpar.endswith("@"):
4647             document.warning("Unexpected chunk content: chunk not terminated by '@'!")
4648             if len(contents) == 0:
4649                 # convert empty chunk layouts to Standard
4650                 document.body[start] = "\\begin_layout Standard"
4651             continue
4652
4653         if lastpar == "@":
4654             # chunk par only contains "@". Just drop it.
4655             contents.pop()
4656         else:
4657             # chunk par contains more. Only drop the "@".
4658             contents[-1].pop()
4659
4660         # The first line should look like: <<CONTENT>>=
4661         # We want the CONTENT
4662         optarg = ' '.join(contents[0])
4663         optarg.strip()
4664         # We can already have real chunk content in
4665         # the first par (separated from the options by a newline).
4666         # We collect such stuff to re-insert it later.
4667         postoptstuff = []
4668
4669         match = first_re.search(optarg)
4670         if match:
4671             optarg = match.groups()[0]
4672             if match.groups()[1] != "":
4673                 postopt = False
4674                 for c in contents[0]:
4675                     if c.endswith(">>="):
4676                         postopt = True
4677                         continue
4678                     if postopt:
4679                         postoptstuff.append(c)
4680             # We have stripped everything. This can be deleted.
4681             contents.pop(0)
4682
4683         newstuff = ['\\begin_layout Standard']
4684
4685         # Maintain paragraph parameters
4686         par_params = ["\\noindent", "\\indent", "\\indent-toggle", "\\leftindent",
4687                       "\\start_of_appendix", "\\paragraph_spacing", "\\align",
4688                       "\\labelwidthstring"]
4689         parms = start + 1
4690         while True:
4691             if document.body[parms].split(' ', 1)[0] not in par_params:
4692                 break
4693             newstuff.extend([document.body[parms]])
4694             parms += 1
4695
4696         newstuff.extend(
4697             ['\\begin_inset Flex Chunk',
4698              'status open', '',
4699              '\\begin_layout Plain Layout', ''])
4700
4701         # If we have a non-empty optional argument, insert it.
4702         if match and optarg != "":
4703             newstuff.extend(
4704                 ['\\begin_inset Argument 1',
4705                  'status open', '',
4706                  '\\begin_layout Plain Layout',
4707                  optarg,
4708                  '\\end_layout', '',
4709                  '\\end_inset', ''])
4710
4711         # Since we already opened a Plain layout, the first paragraph
4712         # does not need to do that.
4713         did_one_par = False
4714         if postoptstuff:
4715             # we need to replace newlines with new layouts
4716             start_newline = -1
4717             started_text = False
4718             for lno in range(0,len(postoptstuff)):
4719                 if postoptstuff[lno].startswith("\\begin_inset Newline newline"):
4720                     start_newline = lno
4721                 elif start_newline != -1:
4722                     if postoptstuff[lno].startswith("\\end_inset"):
4723                         # replace that bit, but only if we already have some text
4724                         # and we're not at the end except for a blank line
4725                         if started_text and \
4726                           (lno != len(postoptstuff) - 2 or postoptstuff[-1] != ""):
4727                             newstuff.extend(['\\end_layout', '\n', '\\begin_layout Plain Layout', '\n'])
4728                         start_newline = -1
4729                         started_text = True
4730                 else:
4731                     newstuff.extend([postoptstuff[lno]])
4732             newstuff.append('\\end_layout')
4733             did_one_par = True
4734         for c in contents:
4735             if did_one_par:
4736                 newstuff.extend(['', '\\begin_layout Plain Layout', ''])
4737             else:
4738                 did_one_par = True
4739             newstuff.extend(c)
4740             newstuff.append('\\end_layout')
4741
4742         newstuff.extend(['', '\\end_inset', '', '\\end_layout', ''])
4743
4744         document.body[start:end] = newstuff
4745
4746         file_pos += len(newstuff) - (end - start)
4747
4748
4749 def revert_chunks(document):
4750     i = 0
4751     while True:
4752         i = find_token(document.body, "\\begin_inset Flex Chunk", i)
4753         if i == -1:
4754             return
4755
4756         iend = find_end_of_inset(document.body, i)
4757         if iend == -1:
4758             document.warning("Can't find end of Chunk!")
4759             i += 1
4760             continue
4761
4762         # Look for optional argument
4763         optarg = ""
4764         ostart = find_token(document.body, "\\begin_inset Argument 1", i, iend)
4765         if ostart != -1:
4766             oend = find_end_of_inset(document.body, ostart)
4767             k = find_token(document.body, "\\begin_layout Plain Layout", ostart, oend)
4768             if k == -1:
4769                 document.warning("Malformed LyX document: Can't find argument contents!")
4770             else:
4771                 m = find_end_of_layout(document.body, k)
4772                 optarg = "".join(document.body[k+1:m])
4773
4774             # We now remove the optional argument, so we have something
4775             # uniform on which to work
4776             document.body[ostart : oend + 1] = []
4777             # iend is now invalid
4778             iend = find_end_of_inset(document.body, i)
4779
4780         retval = get_containing_layout(document.body, i)
4781         if not retval:
4782             document.warning("Can't find containing layout for Chunk!")
4783             i = iend
4784             continue
4785         (lname, lstart, lend, pstart)  = retval
4786         # we now want to work through the various paragraphs, and collect their contents
4787         parlist = []
4788         k = i
4789         while True:
4790             k = find_token(document.body, "\\begin_layout Plain Layout", k, lend)
4791             if k == -1:
4792                 break
4793             j = find_end_of_layout(document.body, k)
4794             if j == -1:
4795                 document.warning("Can't find end of layout inside chunk!")
4796                 break
4797             parlist.append(document.body[k+1:j])
4798             k = j
4799         # we now need to wrap all of these paragraphs in chunks
4800         newlines = []
4801         newlines.extend(["\\begin_layout Chunk", "", "<<" + optarg + ">>=", "\\end_layout", ""])
4802         for stuff in parlist:
4803             newlines.extend(["\\begin_layout Chunk"] + stuff + ["\\end_layout", ""])
4804         newlines.extend(["\\begin_layout Chunk", "", "@", "\\end_layout", ""])
4805         # replace old content with new content
4806         document.body[lstart : lend + 1] = newlines
4807         i = lstart + len(newlines)
4808
4809
4810 ##
4811 # Conversion hub
4812 #
4813
4814 supported_versions = ["2.1.0","2.1"]
4815 convert = [
4816            [414, []],
4817            [415, [convert_undertilde]],
4818            [416, []],
4819            [417, [convert_japanese_encodings]],
4820            [418, [convert_justification]],
4821            [419, []],
4822            [420, [convert_biblio_style]],
4823            [421, [convert_longtable_captions]],
4824            [422, [convert_use_packages]],
4825            [423, [convert_use_mathtools]],
4826            [424, [convert_cite_engine_type]],
4827            # No convert_cancel, since cancel will be loaded automatically
4828            # in format 425 without any possibility to switch it off.
4829            # This has been fixed in format 464.
4830            [425, []],
4831            [426, []],
4832            [427, []],
4833            [428, [convert_cell_rotation]],
4834            [429, [convert_table_rotation]],
4835            [430, [convert_listoflistings]],
4836            [431, [convert_use_amssymb]],
4837            [432, []],
4838            [433, [convert_armenian]],
4839            [434, []],
4840            [435, []],
4841            [436, []],
4842            [437, []],
4843            [438, []],
4844            [439, []],
4845            [440, [convert_mathfonts]],
4846            [441, [convert_mdnomath]],
4847            [442, []],
4848            [443, []],
4849            [444, []],
4850            [445, []],
4851            [446, [convert_latexargs]],
4852            [447, [convert_IEEEtran, convert_AASTeX, convert_AGUTeX, convert_IJMP, convert_SIGPLAN, convert_SIGGRAPH, convert_EuropeCV, convert_Initials, convert_ModernCV]],
4853            [448, [convert_literate]],
4854            [449, []],
4855            [450, []],
4856            [451, [convert_beamerargs, convert_againframe_args, convert_corollary_args, convert_quote_args]],
4857            [452, [convert_beamerblocks]],
4858            [453, [convert_use_stmaryrd]],
4859            [454, [convert_overprint]],
4860            [455, []],
4861            [456, [convert_epigraph]],
4862            [457, [convert_use_stackrel]],
4863            [458, [convert_captioninsets, convert_captionlayouts]],
4864            [459, []],
4865            [460, []],
4866            [461, []],
4867            [462, []],
4868            [463, [convert_encodings]],
4869            [464, [convert_use_cancel]],
4870            [465, [convert_lyxframes, remove_endframes]],
4871            [466, []],
4872            [467, []],
4873            [468, []],
4874            [469, []],
4875            [470, []],
4876            [471, [convert_cite_engine_type_default]],
4877            [472, []],
4878            [473, []],
4879            [474, [convert_chunks, cleanup_beamerargs]],
4880           ]
4881
4882 revert =  [
4883            [473, [revert_chunks]],
4884            [472, [revert_tibetan]],
4885            [471, [revert_aa1,revert_aa2]],
4886            [470, [revert_cite_engine_type_default]],
4887            [469, [revert_forced_local_layout]],
4888            [468, [revert_starred_caption]],
4889            [467, [revert_mbox_fbox]],
4890            [466, [revert_iwona_fonts]],
4891            [465, [revert_powerdot_flexes, revert_powerdot_pause, revert_powerdot_itemargs, revert_powerdot_columns]],
4892            [464, []],
4893            [463, [revert_use_cancel]],
4894            [462, [revert_encodings]],
4895            [461, [revert_new_libertines]],
4896            [460, [revert_kurier_fonts]],
4897            [459, [revert_IEEEtran_3]],
4898            [458, [revert_fragileframe, revert_newframes]],
4899            [457, [revert_captioninsets, revert_captionlayouts]],
4900            [456, [revert_use_stackrel]],
4901            [455, [revert_epigraph]],
4902            [454, [revert_frametitle]],
4903            [453, [revert_overprint]],
4904            [452, [revert_use_stmaryrd]],
4905            [451, [revert_beamerblocks]],
4906            [450, [revert_beamerargs, revert_beamerargs2, revert_beamerargs3, revert_beamerflex]],
4907            [449, [revert_garamondx, revert_garamondx_newtxmath]],
4908            [448, [revert_itemargs]],
4909            [447, [revert_literate]],
4910            [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]],
4911            [445, [revert_latexargs]],
4912            [444, [revert_uop]],
4913            [443, [revert_biolinum]],
4914            [442, []],
4915            [441, [revert_newtxmath]],
4916            [440, [revert_mdnomath]],
4917            [439, [revert_mathfonts]],
4918            [438, [revert_minionpro]],
4919            [437, [revert_ipadeco, revert_ipachar]],
4920            [436, [revert_texgyre]],
4921            [435, [revert_mathdesign]],
4922            [434, [revert_txtt]],
4923            [433, [revert_libertine]],
4924            [432, [revert_armenian]],
4925            [431, [revert_languages, revert_ancientgreek]],
4926            [430, [revert_use_amssymb]],
4927            [429, [revert_listoflistings]],
4928            [428, [revert_table_rotation]],
4929            [427, [revert_cell_rotation]],
4930            [426, [revert_tipa]],
4931            [425, [revert_verbatim]],
4932            [424, [revert_cancel]],
4933            [423, [revert_cite_engine_type]],
4934            [422, [revert_use_mathtools]],
4935            [421, [revert_use_packages]],
4936            [420, [revert_longtable_captions]],
4937            [419, [revert_biblio_style]],
4938            [418, [revert_australian]],
4939            [417, [revert_justification]],
4940            [416, [revert_japanese_encodings]],
4941            [415, [revert_negative_space, revert_math_spaces]],
4942            [414, [revert_undertilde]],
4943            [413, [revert_visible_space]]
4944           ]
4945
4946
4947 if __name__ == "__main__":
4948     pass