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