]> git.lyx.org Git - lyx.git/blob - src/TextClass.cpp
a dummy file while we wait for andre
[lyx.git] / src / TextClass.cpp
1 /**
2  * \file TextClass.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author Jean-Marc Lasgouttes
8  * \author Angus Leeming
9  * \author John Levon
10  * \author André Pönitz
11  *
12  * Full author contact details are available in file CREDITS.
13  */
14
15 #include <config.h>
16
17 #include "TextClass.h"
18
19 #include "LayoutFile.h"
20 #include "Color.h"
21 #include "Counters.h"
22 #include "Floating.h"
23 #include "FloatList.h"
24 #include "Layout.h"
25 #include "Lexer.h"
26 #include "Font.h"
27
28 #include "frontends/alert.h"
29
30 #include "support/assert.h"
31 #include "support/debug.h"
32 #include "support/ExceptionMessage.h"
33 #include "support/FileName.h"
34 #include "support/filetools.h"
35 #include "support/gettext.h"
36 #include "support/lstrings.h"
37 #include "support/os.h"
38
39 #include <algorithm>
40 #include <fstream>
41 #include <sstream>
42
43 using namespace std;
44 using namespace lyx::support;
45
46 namespace lyx {
47
48 namespace {
49
50 class LayoutNamesEqual : public unary_function<Layout, bool> {
51 public:
52         LayoutNamesEqual(docstring const & name)
53                 : name_(name)
54         {}
55         bool operator()(Layout const & c) const
56         {
57                 return c.name() == name_;
58         }
59 private:
60         docstring name_;
61 };
62
63
64 int const FORMAT = 7;
65
66
67 bool layout2layout(FileName const & filename, FileName const & tempfile)
68 {
69         FileName const script = libFileSearch("scripts", "layout2layout.py");
70         if (script.empty()) {
71                 LYXERR0("Could not find layout conversion "
72                           "script layout2layout.py.");
73                 return false;
74         }
75
76         ostringstream command;
77         command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
78                 << ' ' << quoteName(filename.toFilesystemEncoding())
79                 << ' ' << quoteName(tempfile.toFilesystemEncoding());
80         string const command_str = command.str();
81
82         LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
83
84         cmd_ret const ret = runCommand(command_str);
85         if (ret.first != 0) {
86                 LYXERR0("Could not run layout conversion script layout2layout.py.");
87                 return false;
88         }
89         return true;
90 }
91
92
93 std::string translateRT(TextClass::ReadType rt) 
94 {
95         switch (rt) {
96         case TextClass::BASECLASS:
97                 return "textclass";
98         case TextClass::MERGE:
99                 return "input file";
100         case TextClass::MODULE:
101                 return "module file";
102         case TextClass::VALIDATION:
103                 return "validation";
104         }
105         // shutup warning
106         return string();
107 }
108
109 } // namespace anon
110
111
112 docstring const TextClass::emptylayout_ = from_ascii(N_("Plain Layout"));
113
114
115 InsetLayout DocumentClass::empty_insetlayout_;
116
117
118 TextClass::TextClass()
119 {
120         outputType_ = LATEX;
121         columns_ = 1;
122         sides_ = OneSide;
123         secnumdepth_ = 3;
124         tocdepth_ = 3;
125         pagestyle_ = "default";
126         defaultfont_ = sane_font;
127         opt_fontsize_ = "10|11|12";
128         opt_pagestyle_ = "empty|plain|headings|fancy";
129         titletype_ = TITLE_COMMAND_AFTER;
130         titlename_ = "maketitle";
131         loaded_ = false;
132 }
133
134
135 bool TextClass::readStyle(Lexer & lexrc, Layout & lay)
136 {
137         LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
138         if (!lay.read(lexrc, *this)) {
139                 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
140                 return false;
141         }
142         // Resolve fonts
143         lay.resfont = lay.font;
144         lay.resfont.realize(defaultfont_);
145         lay.reslabelfont = lay.labelfont;
146         lay.reslabelfont.realize(defaultfont_);
147         return true; // no errors
148 }
149
150
151 enum TextClassTags {
152         TC_OUTPUTTYPE = 1,
153         TC_INPUT,
154         TC_STYLE,
155         TC_DEFAULTSTYLE,
156         TC_INSETLAYOUT,
157         TC_ENVIRONMENT,
158         TC_NOSTYLE,
159         TC_COLUMNS,
160         TC_SIDES,
161         TC_PAGESTYLE,
162         TC_DEFAULTFONT,
163         TC_SECNUMDEPTH,
164         TC_TOCDEPTH,
165         TC_CLASSOPTIONS,
166         TC_PREAMBLE,
167         TC_PROVIDES,
168         TC_REQUIRES,
169         TC_LEFTMARGIN,
170         TC_RIGHTMARGIN,
171         TC_FLOAT,
172         TC_COUNTER,
173         TC_NOFLOAT,
174         TC_TITLELATEXNAME,
175         TC_TITLELATEXTYPE,
176         TC_FORMAT,
177         TC_ADDTOPREAMBLE
178 };
179
180
181 namespace {
182
183         LexerKeyword textClassTags[] = {
184                 { "addtopreamble",   TC_ADDTOPREAMBLE },
185                 { "classoptions",    TC_CLASSOPTIONS },
186                 { "columns",         TC_COLUMNS },
187                 { "counter",         TC_COUNTER },
188                 { "defaultfont",     TC_DEFAULTFONT },
189                 { "defaultstyle",    TC_DEFAULTSTYLE },
190                 { "environment",     TC_ENVIRONMENT },
191                 { "float",           TC_FLOAT },
192                 { "format",          TC_FORMAT },
193                 { "input",           TC_INPUT },
194                 { "insetlayout",     TC_INSETLAYOUT },
195                 { "leftmargin",      TC_LEFTMARGIN },
196                 { "nofloat",         TC_NOFLOAT },
197                 { "nostyle",         TC_NOSTYLE },
198                 { "outputtype",      TC_OUTPUTTYPE },
199                 { "pagestyle",       TC_PAGESTYLE },
200                 { "preamble",        TC_PREAMBLE },
201                 { "provides",        TC_PROVIDES },
202                 { "requires",        TC_REQUIRES },
203                 { "rightmargin",     TC_RIGHTMARGIN },
204                 { "secnumdepth",     TC_SECNUMDEPTH },
205                 { "sides",           TC_SIDES },
206                 { "style",           TC_STYLE },
207                 { "titlelatexname",  TC_TITLELATEXNAME },
208                 { "titlelatextype",  TC_TITLELATEXTYPE },
209                 { "tocdepth",        TC_TOCDEPTH }
210         };
211         
212 } //namespace anon
213
214
215 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
216 {
217         LYXERR(Debug::TCLASS, "Converting layout file to " << FORMAT);
218                 FileName const tempfile = FileName::tempName();
219                 bool success = layout2layout(filename, tempfile);
220                 if (success)
221                         success = read(tempfile, rt);
222                 tempfile.removeFile();
223                 return success;
224 }
225
226 bool TextClass::read(FileName const & filename, ReadType rt)
227 {
228         if (!filename.isReadableFile()) {
229                 lyxerr << "Cannot read layout file `" << filename << "'."
230                        << endl;
231                 return false;
232         }
233
234         LYXERR(Debug::TCLASS, "Reading " + translateRT(rt) + ": " +
235                 to_utf8(makeDisplayPath(filename.absFilename())));
236
237         // Define the `empty' layout used in table cells, ert, etc. Note that 
238         // we do this before loading any layout file, so that classes can 
239         // override features of this layout if they should choose to do so.
240         if (rt == BASECLASS && !hasLayout(emptylayout_)) {
241                 static char const * s = "Margin Static\n"
242                         "LatexType Paragraph\n"
243                         "LatexName dummy\n"
244                         "Align Block\n"
245                         "AlignPossible Left, Right, Center\n"
246                         "LabelType No_Label\n"
247                         "End";
248                 istringstream ss(s);
249                 Lexer lex(textClassTags);
250                 lex.setStream(ss);
251                 Layout lay;
252                 lay.setName(emptylayout_);
253                 if (!readStyle(lex, lay)) {
254                         // The only way this happens is because the hardcoded layout above
255                         // is wrong.
256                         LASSERT(false, /**/);
257                 };
258                 layoutlist_.push_back(lay);
259         }
260
261         Lexer lexrc(textClassTags);
262         lexrc.setFile(filename);
263         ReturnValues retval = read(lexrc, rt);
264         
265         LYXERR(Debug::TCLASS, "Finished reading " + translateRT(rt) + ": " +
266                         to_utf8(makeDisplayPath(filename.absFilename())));
267         
268         if (retval != FORMAT_MISMATCH) 
269                 return retval == OK;
270         
271         bool const worx = convertLayoutFormat(filename, rt);
272         if (!worx) {
273                 lyxerr << "Unable to convert " << filename << 
274                         " to format " << FORMAT << std::endl;
275                 return false;
276         }
277         return true;
278 }
279
280
281 bool TextClass::validate(std::string const & str)
282 {
283         TextClass tc;
284         return tc.read(str, VALIDATION);
285 }
286
287
288 bool TextClass::read(std::string const & str, ReadType rt) 
289 {
290         Lexer lexrc(textClassTags);
291         istringstream is(str);
292         lexrc.setStream(is);
293         ReturnValues retval = read(lexrc, rt);
294
295         if (retval != FORMAT_MISMATCH) 
296                 return retval == OK;
297
298         // write the layout string to a temporary file
299         FileName const tempfile = FileName::tempName();
300         ofstream os(tempfile.toFilesystemEncoding().c_str());
301         if (!os) {
302                 LYXERR0("Unable to create tempoary file");
303                 return false;
304         }
305         os << str;
306         os.close();
307
308         // now try to convert it
309         bool const worx = convertLayoutFormat(tempfile, rt);
310         if (!worx) {
311                 LYXERR0("Unable to convert internal layout information to format " 
312                         << FORMAT);
313         }
314         tempfile.removeFile();
315         return worx;
316 }
317
318
319 // Reads a textclass structure from file.
320 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt) 
321 {
322         bool error = !lexrc.isOK();
323
324         // Format of files before the 'Format' tag was introduced
325         int format = 1;
326
327         // parsing
328         while (lexrc.isOK() && !error) {
329                 int le = lexrc.lex();
330
331                 switch (le) {
332                 case Lexer::LEX_FEOF:
333                         continue;
334
335                 case Lexer::LEX_UNDEF:
336                         lexrc.printError("Unknown TextClass tag `$$Token'");
337                         error = true;
338                         continue;
339
340                 default:
341                         break;
342                 }
343
344                 switch (static_cast<TextClassTags>(le)) {
345
346                 case TC_FORMAT:
347                         if (lexrc.next())
348                                 format = lexrc.getInteger();
349                         break;
350
351                 case TC_OUTPUTTYPE:   // output type definition
352                         readOutputType(lexrc);
353                         break;
354
355                 case TC_INPUT: // Include file
356                         if (lexrc.next()) {
357                                 string const inc = lexrc.getString();
358                                 FileName tmp = libFileSearch("layouts", inc,
359                                                             "layout");
360
361                                 if (tmp.empty()) {
362                                         lexrc.printError("Could not find input file: " + inc);
363                                         error = true;
364                                 } else if (!read(tmp, MERGE)) {
365                                         lexrc.printError("Error reading input"
366                                                          "file: " + tmp.absFilename());
367                                         error = true;
368                                 }
369                         }
370                         break;
371
372                 case TC_DEFAULTSTYLE:
373                         if (lexrc.next()) {
374                                 docstring const name = from_utf8(subst(lexrc.getString(),
375                                                           '_', ' '));
376                                 defaultlayout_ = name;
377                         }
378                         break;
379
380                 case TC_ENVIRONMENT:
381                 case TC_STYLE:
382                         if (lexrc.next()) {
383                                 docstring const name = from_utf8(subst(lexrc.getString(),
384                                                     '_', ' '));
385                                 if (name.empty()) {
386                                         string s = "Could not read name for style: `$$Token' "
387                                                 + lexrc.getString() + " is probably not valid UTF-8!";
388                                         lexrc.printError(s.c_str());
389                                         Layout lay;
390                                         // Since we couldn't read the name, we just scan the rest
391                                         // of the style and discard it.
392                                         error = !readStyle(lexrc, lay);
393                                 } else if (hasLayout(name)) {
394                                         Layout & lay = operator[](name);
395                                         error = !readStyle(lexrc, lay);
396                                 } else {
397                                         Layout layout;
398                                         layout.setName(name);
399                                         if (le == TC_ENVIRONMENT)
400                                                 layout.is_environment = true;
401                                         error = !readStyle(lexrc, layout);
402                                         if (!error)
403                                                 layoutlist_.push_back(layout);
404
405                                         if (defaultlayout_.empty()) {
406                                                 // We do not have a default layout yet, so we choose
407                                                 // the first layout we encounter.
408                                                 defaultlayout_ = name;
409                                         }
410                                 }
411                         }
412                         else {
413                                 //FIXME Should we also eat the style here? viz:
414                                 //Layout layout;
415                                 //readStyle(lexrc, layout);
416                                 //as above...
417                                 lexrc.printError("No name given for style: `$$Token'.");
418                                 error = true;
419                         }
420                         break;
421
422                 case TC_NOSTYLE:
423                         if (lexrc.next()) {
424                                 docstring const style = from_utf8(subst(lexrc.getString(),
425                                                      '_', ' '));
426                                 if (!deleteLayout(style))
427                                         lyxerr << "Cannot delete style `"
428                                                << to_utf8(style) << '\'' << endl;
429                         }
430                         break;
431
432                 case TC_COLUMNS:
433                         if (lexrc.next())
434                                 columns_ = lexrc.getInteger();
435                         break;
436
437                 case TC_SIDES:
438                         if (lexrc.next()) {
439                                 switch (lexrc.getInteger()) {
440                                 case 1: sides_ = OneSide; break;
441                                 case 2: sides_ = TwoSides; break;
442                                 default:
443                                         lyxerr << "Impossible number of page"
444                                                 " sides, setting to one."
445                                                << endl;
446                                         sides_ = OneSide;
447                                         break;
448                                 }
449                         }
450                         break;
451
452                 case TC_PAGESTYLE:
453                         lexrc.next();
454                         pagestyle_ = rtrim(lexrc.getString());
455                         break;
456
457                 case TC_DEFAULTFONT:
458                         defaultfont_ = lyxRead(lexrc);
459                         if (!defaultfont_.resolved()) {
460                                 lexrc.printError("Warning: defaultfont should "
461                                                  "be fully instantiated!");
462                                 defaultfont_.realize(sane_font);
463                         }
464                         break;
465
466                 case TC_SECNUMDEPTH:
467                         lexrc.next();
468                         secnumdepth_ = lexrc.getInteger();
469                         break;
470
471                 case TC_TOCDEPTH:
472                         lexrc.next();
473                         tocdepth_ = lexrc.getInteger();
474                         break;
475
476                 // First step to support options
477                 case TC_CLASSOPTIONS:
478                         readClassOptions(lexrc);
479                         break;
480
481                 case TC_PREAMBLE:
482                         preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
483                         break;
484
485                 case TC_ADDTOPREAMBLE:
486                         preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
487                         break;
488
489                 case TC_PROVIDES: {
490                         lexrc.next();
491                         string const feature = lexrc.getString();
492                         lexrc.next();
493                         if (lexrc.getInteger())
494                                 provides_.insert(feature);
495                         else
496                                 provides_.erase(feature);
497                         break;
498                 }
499
500                 case TC_REQUIRES: {
501                         lexrc.eatLine();
502                         vector<string> const req 
503                                 = getVectorFromString(lexrc.getString());
504                         requires_.insert(req.begin(), req.end());
505                         break;
506                 }
507
508                 case TC_LEFTMARGIN:     // left margin type
509                         if (lexrc.next())
510                                 leftmargin_ = lexrc.getDocString();
511                         break;
512
513                 case TC_RIGHTMARGIN:    // right margin type
514                         if (lexrc.next())
515                                 rightmargin_ = lexrc.getDocString();
516                         break;
517
518                 case TC_INSETLAYOUT:
519                         if (lexrc.next()) {
520                                 InsetLayout il;
521                                 if (il.read(lexrc))
522                                         insetlayoutlist_[il.name()] = il;
523                                 // else there was an error, so forget it
524                         }
525                         break;
526
527                 case TC_FLOAT:
528                         readFloat(lexrc);
529                         break;
530
531                 case TC_COUNTER:
532                         readCounter(lexrc);
533                         break;
534
535                 case TC_TITLELATEXTYPE:
536                         readTitleType(lexrc);
537                         break;
538
539                 case TC_TITLELATEXNAME:
540                         if (lexrc.next())
541                                 titlename_ = lexrc.getString();
542                         break;
543
544                 case TC_NOFLOAT:
545                         if (lexrc.next()) {
546                                 string const nofloat = lexrc.getString();
547                                 floatlist_.erase(nofloat);
548                         }
549                         break;
550                 }
551
552                 //Note that this is triggered the first time through the loop unless
553                 //we hit a format tag.
554                 if (format != FORMAT)
555                         break;
556         }
557
558         if (format != FORMAT)
559                 return FORMAT_MISMATCH;
560
561         if (rt != BASECLASS) 
562                 return (error ? ERROR : OK);
563
564         if (defaultlayout_.empty()) {
565                 LYXERR0("Error: Textclass '" << name_
566                                                 << "' is missing a defaultstyle.");
567                 error = true;
568         }
569                 
570         // Try to erase "stdinsets" from the provides_ set. 
571         // The
572         //   Provides stdinsets 1
573         // declaration simply tells us that the standard insets have been
574         // defined. (It's found in stdinsets.inc but could also be used in
575         // user-defined files.) There isn't really any such package. So we
576         // might as well go ahead and erase it.
577         // If we do not succeed, then it was not there, which means that
578         // the textclass did not provide the definitions of the standard
579         // insets. So we need to try to load them.
580         int erased = provides_.erase("stdinsets");
581         if (!erased) {
582                 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
583
584                 if (tmp.empty()) {
585                         throw ExceptionMessage(WarningException, _("Missing File"),
586                                 _("Could not find stdinsets.inc! This may lead to data loss!"));
587                         error = true;
588                 } else if (!read(tmp, MERGE)) {
589                         throw ExceptionMessage(WarningException, _("Corrupt File"),
590                                 _("Could not read stdinsets.inc! This may lead to data loss!"));
591                         error = true;
592                 }
593         }
594
595         min_toclevel_ = Layout::NOT_IN_TOC;
596         max_toclevel_ = Layout::NOT_IN_TOC;
597         const_iterator lit = begin();
598         const_iterator len = end();
599         for (; lit != len; ++lit) {
600                 int const toclevel = lit->toclevel;
601                 if (toclevel != Layout::NOT_IN_TOC) {
602                         if (min_toclevel_ == Layout::NOT_IN_TOC)
603                                 min_toclevel_ = toclevel;
604                         else
605                                 min_toclevel_ = min(min_toclevel_, toclevel);
606                         max_toclevel_ = max(max_toclevel_, toclevel);
607                 }
608         }
609         LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
610                 << ", maximum is " << max_toclevel_);
611
612         return (error ? ERROR : OK);
613 }
614
615
616 void TextClass::readTitleType(Lexer & lexrc)
617 {
618         LexerKeyword titleTypeTags[] = {
619                 { "commandafter", TITLE_COMMAND_AFTER },
620                 { "environment",  TITLE_ENVIRONMENT }
621         };
622
623         PushPopHelper pph(lexrc, titleTypeTags);
624
625         int le = lexrc.lex();
626         switch (le) {
627         case Lexer::LEX_UNDEF:
628                 lexrc.printError("Unknown output type `$$Token'");
629                 break;
630         case TITLE_COMMAND_AFTER:
631         case TITLE_ENVIRONMENT:
632                 titletype_ = static_cast<TitleLatexType>(le);
633                 break;
634         default:
635                 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
636                 break;
637         }
638 }
639
640
641 void TextClass::readOutputType(Lexer & lexrc)
642 {
643         LexerKeyword outputTypeTags[] = {
644                 { "docbook",  DOCBOOK },
645                 { "latex",    LATEX },
646                 { "literate", LITERATE }
647         };
648
649         PushPopHelper pph(lexrc, outputTypeTags);
650
651         int le = lexrc.lex();
652         switch (le) {
653         case Lexer::LEX_UNDEF:
654                 lexrc.printError("Unknown output type `$$Token'");
655                 return;
656         case LATEX:
657         case DOCBOOK:
658         case LITERATE:
659                 outputType_ = static_cast<OutputType>(le);
660                 break;
661         default:
662                 LYXERR0("Unhandled value " << le);
663                 break;
664         }
665 }
666
667
668 void TextClass::readClassOptions(Lexer & lexrc)
669 {
670         enum {
671                 CO_FONTSIZE = 1,
672                 CO_PAGESTYLE,
673                 CO_OTHER,
674                 CO_HEADER,
675                 CO_END
676         };
677
678         LexerKeyword classOptionsTags[] = {
679                 {"end",       CO_END },
680                 {"fontsize",  CO_FONTSIZE },
681                 {"header",    CO_HEADER },
682                 {"other",     CO_OTHER },
683                 {"pagestyle", CO_PAGESTYLE }
684         };
685
686         lexrc.pushTable(classOptionsTags);
687         bool getout = false;
688         while (!getout && lexrc.isOK()) {
689                 int le = lexrc.lex();
690                 switch (le) {
691                 case Lexer::LEX_UNDEF:
692                         lexrc.printError("Unknown ClassOption tag `$$Token'");
693                         continue;
694                 default: break;
695                 }
696                 switch (le) {
697                 case CO_FONTSIZE:
698                         lexrc.next();
699                         opt_fontsize_ = rtrim(lexrc.getString());
700                         break;
701                 case CO_PAGESTYLE:
702                         lexrc.next();
703                         opt_pagestyle_ = rtrim(lexrc.getString());
704                         break;
705                 case CO_OTHER:
706                         lexrc.next();
707                         options_ = lexrc.getString();
708                         break;
709                 case CO_HEADER:
710                         lexrc.next();
711                         class_header_ = subst(lexrc.getString(), "&quot;", "\"");
712                         break;
713                 case CO_END:
714                         getout = true;
715                         break;
716                 }
717         }
718         lexrc.popTable();
719 }
720
721
722 void TextClass::readFloat(Lexer & lexrc)
723 {
724         enum {
725                 FT_TYPE = 1,
726                 FT_NAME,
727                 FT_PLACEMENT,
728                 FT_EXT,
729                 FT_WITHIN,
730                 FT_STYLE,
731                 FT_LISTNAME,
732                 FT_BUILTIN,
733                 FT_END
734         };
735
736         LexerKeyword floatTags[] = {
737                 { "end", FT_END },
738                 { "extension", FT_EXT },
739                 { "guiname", FT_NAME },
740                 { "latexbuiltin", FT_BUILTIN },
741                 { "listname", FT_LISTNAME },
742                 { "numberwithin", FT_WITHIN },
743                 { "placement", FT_PLACEMENT },
744                 { "style", FT_STYLE },
745                 { "type", FT_TYPE }
746         };
747
748         lexrc.pushTable(floatTags);
749
750         string type;
751         string placement;
752         string ext;
753         string within;
754         string style;
755         string name;
756         string listName;
757         bool builtin = false;
758
759         bool getout = false;
760         while (!getout && lexrc.isOK()) {
761                 int le = lexrc.lex();
762                 switch (le) {
763                 case Lexer::LEX_UNDEF:
764                         lexrc.printError("Unknown float tag `$$Token'");
765                         continue;
766                 default: break;
767                 }
768                 switch (le) {
769                 case FT_TYPE:
770                         lexrc.next();
771                         type = lexrc.getString();
772                         if (floatlist_.typeExist(type)) {
773                                 Floating const & fl = floatlist_.getType(type);
774                                 placement = fl.placement();
775                                 ext = fl.ext();
776                                 within = fl.within();
777                                 style = fl.style();
778                                 name = fl.name();
779                                 listName = fl.listName();
780                                 builtin = fl.builtin();
781                         } 
782                         break;
783                 case FT_NAME:
784                         lexrc.next();
785                         name = lexrc.getString();
786                         break;
787                 case FT_PLACEMENT:
788                         lexrc.next();
789                         placement = lexrc.getString();
790                         break;
791                 case FT_EXT:
792                         lexrc.next();
793                         ext = lexrc.getString();
794                         break;
795                 case FT_WITHIN:
796                         lexrc.next();
797                         within = lexrc.getString();
798                         if (within == "none")
799                                 within.erase();
800                         break;
801                 case FT_STYLE:
802                         lexrc.next();
803                         style = lexrc.getString();
804                         break;
805                 case FT_LISTNAME:
806                         lexrc.next();
807                         listName = lexrc.getString();
808                         break;
809                 case FT_BUILTIN:
810                         lexrc.next();
811                         builtin = lexrc.getBool();
812                         break;
813                 case FT_END:
814                         getout = true;
815                         break;
816                 }
817         }
818
819         // Here if have a full float if getout == true
820         if (getout) {
821                 Floating fl(type, placement, ext, within,
822                             style, name, listName, builtin);
823                 floatlist_.newFloat(fl);
824                 // each float has its own counter
825                 counters_.newCounter(from_ascii(type), from_ascii(within),
826                                       docstring(), docstring());
827                 // also define sub-float counters
828                 docstring const subtype = "sub-" + from_ascii(type);
829                 counters_.newCounter(subtype, from_ascii(type),
830                                       "\\alph{" + subtype + "}", docstring());
831         }
832
833         lexrc.popTable();
834 }
835
836
837 void TextClass::readCounter(Lexer & lexrc)
838 {
839         enum {
840                 CT_NAME = 1,
841                 CT_WITHIN,
842                 CT_LABELSTRING,
843                 CT_LABELSTRING_APPENDIX,
844                 CT_END
845         };
846
847         LexerKeyword counterTags[] = {
848                 { "end", CT_END },
849                 { "labelstring", CT_LABELSTRING },
850                 { "labelstringappendix", CT_LABELSTRING_APPENDIX },
851                 { "name", CT_NAME },
852                 { "within", CT_WITHIN }
853         };
854
855         lexrc.pushTable(counterTags);
856
857         docstring name;
858         docstring within;
859         docstring labelstring;
860         docstring labelstring_appendix;
861
862         bool getout = false;
863         while (!getout && lexrc.isOK()) {
864                 int le = lexrc.lex();
865                 switch (le) {
866                 case Lexer::LEX_UNDEF:
867                         lexrc.printError("Unknown counter tag `$$Token'");
868                         continue;
869                 default: break;
870                 }
871                 switch (le) {
872                 case CT_NAME:
873                         lexrc.next();
874                         name = lexrc.getDocString();
875                         if (counters_.hasCounter(name))
876                                 LYXERR(Debug::TCLASS, "Reading existing counter " << to_utf8(name));
877                         else
878                                 LYXERR(Debug::TCLASS, "Reading new counter " << to_utf8(name));
879                         break;
880                 case CT_WITHIN:
881                         lexrc.next();
882                         within = lexrc.getDocString();
883                         if (within == "none")
884                                 within.erase();
885                         break;
886                 case CT_LABELSTRING:
887                         lexrc.next();
888                         labelstring = lexrc.getDocString();
889                         labelstring_appendix = labelstring;
890                         break;
891                 case CT_LABELSTRING_APPENDIX:
892                         lexrc.next();
893                         labelstring_appendix = lexrc.getDocString();
894                         break;
895                 case CT_END:
896                         getout = true;
897                         break;
898                 }
899         }
900
901         // Here if have a full counter if getout == true
902         if (getout)
903                 counters_.newCounter(name, within, 
904                                       labelstring, labelstring_appendix);
905
906         lexrc.popTable();
907 }
908
909
910 bool TextClass::hasLayout(docstring const & n) const
911 {
912         docstring const name = n.empty() ? defaultLayoutName() : n;
913
914         return find_if(layoutlist_.begin(), layoutlist_.end(),
915                        LayoutNamesEqual(name))
916                 != layoutlist_.end();
917 }
918
919
920
921 Layout const & TextClass::operator[](docstring const & name) const
922 {
923         LASSERT(!name.empty(), /**/);
924
925         const_iterator it = 
926                 find_if(begin(), end(), LayoutNamesEqual(name));
927
928         if (it == end()) {
929                 lyxerr << "We failed to find the layout '" << to_utf8(name)
930                        << "' in the layout list. You MUST investigate!"
931                        << endl;
932                 for (const_iterator cit = begin(); cit != end(); ++cit)
933                         lyxerr  << " " << to_utf8(cit->name()) << endl;
934
935                 // we require the name to exist
936                 LASSERT(false, /**/);
937         }
938
939         return *it;
940 }
941
942
943 Layout & TextClass::operator[](docstring const & name)
944 {
945         LASSERT(!name.empty(), /**/);
946
947         iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
948
949         if (it == end()) {
950                 LYXERR0("We failed to find the layout '" << to_utf8(name)
951                        << "' in the layout list. You MUST investigate!");
952                 for (const_iterator cit = begin(); cit != end(); ++cit)
953                         LYXERR0(" " << to_utf8(cit->name()));
954
955                 // we require the name to exist
956                 LASSERT(false, /**/);
957         }
958
959         return *it;
960 }
961
962
963 bool TextClass::deleteLayout(docstring const & name)
964 {
965         if (name == defaultLayoutName() || name == emptyLayoutName())
966                 return false;
967
968         LayoutList::iterator it =
969                 remove_if(layoutlist_.begin(), layoutlist_.end(),
970                           LayoutNamesEqual(name));
971
972         LayoutList::iterator end = layoutlist_.end();
973         bool const ret = (it != end);
974         layoutlist_.erase(it, end);
975         return ret;
976 }
977
978
979 // Load textclass info if not loaded yet
980 bool TextClass::load(string const & path) const
981 {
982         if (loaded_)
983                 return true;
984
985         // Read style-file, provided path is searched before the system ones
986         FileName layout_file;
987         if (!path.empty())
988                 layout_file = FileName(addName(path, name_ + ".layout"));
989         if (layout_file.empty() || !layout_file.exists())
990                 layout_file = libFileSearch("layouts", name_, "layout");
991         loaded_ = const_cast<TextClass*>(this)->read(layout_file);
992
993         if (!loaded_) {
994                 lyxerr << "Error reading `"
995                        << to_utf8(makeDisplayPath(layout_file.absFilename()))
996                        << "'\n(Check `" << name_
997                        << "')\nCheck your installation and "
998                         "try Options/Reconfigure..." << endl;
999         }
1000
1001         return loaded_;
1002 }
1003
1004
1005 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const 
1006 {
1007         docstring n = name;
1008         InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1009         while (!n.empty()) {
1010                 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1011                 if (cit != cen && cit->first == n)
1012                         return cit->second;
1013                 size_t i = n.find(':');
1014                 if (i == string::npos)
1015                         break;
1016                 n = n.substr(0,i);
1017         }
1018         return empty_insetlayout_;
1019 }
1020
1021
1022 docstring const & TextClass::defaultLayoutName() const
1023 {
1024         // This really should come from the actual layout... (Lgb)
1025         return defaultlayout_;
1026 }
1027
1028
1029 Layout const & TextClass::defaultLayout() const
1030 {
1031         return operator[](defaultLayoutName());
1032 }
1033
1034
1035 bool TextClass::isDefaultLayout(Layout const & lay) const 
1036 {
1037         return lay.name() == defaultLayoutName();
1038 }
1039
1040
1041 bool TextClass::isEmptyLayout(Layout const & lay) const 
1042 {
1043         return lay.name() == emptyLayoutName();
1044 }
1045
1046
1047 DocumentClass & DocumentClassBundle::newClass(LayoutFile const & baseClass)
1048 {
1049         DocumentClass * dc = new DocumentClass(baseClass);
1050         tc_list_.push_back(dc);
1051         return *tc_list_.back();
1052 }
1053
1054
1055 DocumentClassBundle & DocumentClassBundle::get()
1056 {
1057         static DocumentClassBundle singleton; 
1058         return singleton; 
1059 }
1060
1061
1062 DocumentClass::DocumentClass(LayoutFile const & tc)
1063         : TextClass(tc)
1064 {}
1065
1066
1067 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1068 {
1069         LayoutList::const_iterator it  = layoutlist_.begin();
1070         LayoutList::const_iterator end = layoutlist_.end();
1071         for (; it != end; ++it)
1072                 if (it->latexname() == lay)
1073                         return true;
1074         return false;
1075 }
1076
1077
1078 bool DocumentClass::provides(string const & p) const
1079 {
1080         return provides_.find(p) != provides_.end();
1081 }
1082
1083
1084 bool DocumentClass::hasTocLevels() const
1085 {
1086         return min_toclevel_ != Layout::NOT_IN_TOC;
1087 }
1088
1089
1090 ostream & operator<<(ostream & os, PageSides p)
1091 {
1092         switch (p) {
1093         case OneSide:
1094                 os << '1';
1095                 break;
1096         case TwoSides:
1097                 os << '2';
1098                 break;
1099         }
1100         return os;
1101 }
1102
1103
1104 } // namespace lyx