]> git.lyx.org Git - lyx.git/blob - src/lyxtextclass.C
layout as string
[lyx.git] / src / lyxtextclass.C
1 /* This file is part of
2  * ====================================================== 
3  * 
4  *           LyX, The Document Processor
5  *       
6  *          Copyright 1995 Matthias Ettrich
7  *          Copyright 1995-2001 The LyX Team.
8  *
9  * ======================================================
10  */
11
12 #include <config.h>
13
14 #ifdef __GNUG__
15 #pragma implementation
16 #endif
17
18 #include "lyxtextclass.h"
19 #include "debug.h"
20 #include "lyxlex.h"
21
22 #include "support/lstrings.h"
23 #include "support/LAssert.h"
24 #include "support/lyxfunctional.h"
25 #include "support/filetools.h"
26
27 #include <algorithm>
28
29 using std::endl;
30 using std::find_if;
31 using std::remove_if;
32 using std::ostream;
33
34
35 /* ******************************************************************* */
36
37 LyXTextClass::LyXTextClass(string const & fn, string const & cln,
38                            string const & desc)
39         : name_(fn), latexname_(cln), description_(desc)
40 {
41         outputType_ = LATEX;
42         columns_ = 1;
43         sides_ = OneSide;
44         secnumdepth_ = 3;
45         tocdepth_ = 3;
46         pagestyle_ = "default";
47         maxcounter_ = LABEL_COUNTER_CHAPTER;
48         defaultfont_ = LyXFont(LyXFont::ALL_SANE);
49         opt_fontsize_ = "10|11|12";
50         opt_pagestyle_ = "empty|plain|headings|fancy";
51         provides_ = nothing;
52         loaded = false;
53 }
54
55
56 bool LyXTextClass::do_readStyle(LyXLex & lexrc, LyXLayout & lay)
57 {
58         lyxerr[Debug::TCLASS] << "Reading style " << lay.name() << endl;
59         if (!lay.Read(lexrc, *this)) {
60                 // Resolve fonts
61                 lay.resfont = lay.font;
62 #ifndef INHERIT_LANGUAGE
63                 lay.resfont.realize(defaultfont());
64                 lay.reslabelfont = lay.labelfont;
65                 lay.reslabelfont.realize(defaultfont());
66 #else
67                 lay.resfont.realize(defaultfont(), default_language);
68                 lay.reslabelfont = lay.labelfont;
69                 lay.reslabelfont.realize(defaultfont(), default_language);
70 #endif
71                 return false; // no errors
72         } 
73         lyxerr << "Error parsing style `" << lay.name() << "'" << endl;
74         return true;
75 }
76
77
78 enum TextClassTags {
79         TC_OUTPUTTYPE = 1,
80         TC_INPUT,
81         TC_STYLE,
82         TC_DEFAULTSTYLE,
83         TC_NOSTYLE,
84         TC_COLUMNS,
85         TC_SIDES,
86         TC_PAGESTYLE,
87         TC_DEFAULTFONT,
88         TC_MAXCOUNTER,
89         TC_SECNUMDEPTH,
90         TC_TOCDEPTH,
91         TC_CLASSOPTIONS,
92         TC_PREAMBLE,
93         TC_PROVIDESAMSMATH,
94         TC_PROVIDESMAKEIDX,
95         TC_PROVIDESURL,
96         TC_LEFTMARGIN,
97         TC_RIGHTMARGIN
98 };
99
100
101 // Reads a textclass structure from file.
102 bool LyXTextClass::Read(string const & filename, bool merge)
103 {
104         keyword_item textClassTags[] = {
105                 { "classoptions",    TC_CLASSOPTIONS },
106                 { "columns",         TC_COLUMNS },
107                 { "defaultfont",     TC_DEFAULTFONT },
108                 { "defaultstyle",    TC_DEFAULTSTYLE },
109                 { "input",           TC_INPUT },
110                 { "leftmargin",      TC_LEFTMARGIN },
111                 { "maxcounter",      TC_MAXCOUNTER },
112                 { "nostyle",         TC_NOSTYLE },
113                 { "outputtype",      TC_OUTPUTTYPE },
114                 { "pagestyle",       TC_PAGESTYLE },
115                 { "preamble",        TC_PREAMBLE },
116                 { "providesamsmath", TC_PROVIDESAMSMATH },
117                 { "providesmakeidx", TC_PROVIDESMAKEIDX },
118                 { "providesurl",     TC_PROVIDESURL },
119                 { "rightmargin",     TC_RIGHTMARGIN },
120                 { "secnumdepth",     TC_SECNUMDEPTH },
121                 { "sides",           TC_SIDES },
122                 { "style",           TC_STYLE },
123                 { "tocdepth",        TC_TOCDEPTH }
124         };
125
126         if (!merge)
127                 lyxerr[Debug::TCLASS] << "Reading textclass "
128                                       << MakeDisplayPath(filename)
129                                       << endl;
130         else
131                 lyxerr[Debug::TCLASS] << "Reading input file "
132                                      << MakeDisplayPath(filename)
133                                      << endl;
134         
135         LyXLex lexrc(textClassTags, TC_RIGHTMARGIN);
136         bool error = false;
137
138         lexrc.setFile(filename);
139         if (!lexrc.isOK()) error = true; 
140
141         // parsing
142         while (lexrc.isOK() && !error) {
143                 int le = lexrc.lex();
144                 switch (le) {
145                 case LyXLex::LEX_FEOF:
146                         continue; 
147
148                 case LyXLex::LEX_UNDEF:                                 
149                         lexrc.printError("Unknown TextClass tag `$$Token'");
150                         error = true;
151                         continue; 
152                 default: break;
153                 }
154                 switch (static_cast<TextClassTags>(le)) {
155                 case TC_OUTPUTTYPE:   // output type definition
156                         readOutputType(lexrc);
157                         break;
158                         
159                 case TC_INPUT: // Include file
160                         if (lexrc.next()) {
161                                 string tmp = LibFileSearch("layouts",
162                                                             lexrc.getString(), 
163                                                             "layout");
164                                 
165                                 if (Read(tmp, true)) {
166                                         lexrc.printError("Error reading input"
167                                                          "file: "+tmp);
168                                         error = true;
169                                 }
170                         }
171                         break;
172
173                 case TC_DEFAULTSTYLE:
174                         if (lexrc.next()) {
175                                 string const name = subst(lowercase(lexrc.getString()), '_', ' ');
176                                 defaultlayout_ = name;
177                         }
178                         break;
179                         
180                 case TC_STYLE:
181                         if (lexrc.next()) {
182                                 string const name = subst(lowercase(lexrc.getString()),
183                                                     '_', ' ');
184                                 if (hasLayout(name)) {
185                                         LyXLayout & lay = operator[](name);
186                                         error = do_readStyle(lexrc, lay);
187                                 } else {
188                                         LyXLayout lay;
189                                         lay.setName(name);
190                                         if (!(error = do_readStyle(lexrc, lay)))
191                                                 layoutlist.push_back(lay);
192                                 }
193                         }
194                         else {
195                                 lexrc.printError("No name given for style: `$$Token'.");
196                                 error = true;
197                         }
198                         break;
199
200                 case TC_NOSTYLE:
201                         if (lexrc.next()) {
202                                 string const style = subst(lowercase(lexrc.getString()),
203                                                      '_', ' ');
204                                 if (!delete_layout(style))
205                                         lyxerr << "Cannot delete style `" << style << "'" << endl;
206 //                                      lexrc.printError("Cannot delete style"
207 //                                                       " `$$Token'");
208                         }
209                         break;
210
211                 case TC_COLUMNS:
212                         if (lexrc.next())
213                                 columns_ = lexrc.getInteger();
214                         break;
215                         
216                 case TC_SIDES:
217                         if (lexrc.next()) {
218                                 switch (lexrc.getInteger()) {
219                                 case 1: sides_ = OneSide; break;
220                                 case 2: sides_ = TwoSides; break;
221                                 default:
222                                         lyxerr << "Impossible number of page"
223                                                 " sides, setting to one."
224                                                << endl;
225                                         sides_ = OneSide;
226                                         break;
227                                 }
228                         }
229                         break;
230                         
231                 case TC_PAGESTYLE:
232                         lexrc.next();
233                         pagestyle_ = strip(lexrc.getString());
234                         break;
235                         
236                 case TC_DEFAULTFONT:
237                         defaultfont_.lyxRead(lexrc);
238                         if (!defaultfont_.resolved()) {
239                                 lexrc.printError("Warning: defaultfont should "
240                                                  "be fully instantiated!");
241 #ifndef INHERIT_LANGUAGE
242                                 defaultfont_.realize(LyXFont(LyXFont::ALL_SANE));
243 #else
244                                 defaultfont_.realize(LyXFont(LyXFont::ALL_SANE),
245                                                      default_language);
246 #endif
247                         }
248                         break;
249
250                 case TC_MAXCOUNTER:
251                         readMaxCounter(lexrc);
252                         break;
253
254                 case TC_SECNUMDEPTH:
255                         lexrc.next();
256                         secnumdepth_ = lexrc.getInteger();
257                         break;
258
259                 case TC_TOCDEPTH:
260                         lexrc.next();
261                         tocdepth_ = lexrc.getInteger();
262                         break;
263
264                         // First step to support options 
265                 case TC_CLASSOPTIONS:
266                         readClassOptions(lexrc);
267                         break;
268
269                 case TC_PREAMBLE:
270                         preamble_ = lexrc.getLongString("EndPreamble");
271                         break;
272
273                 case TC_PROVIDESAMSMATH:
274                         if (lexrc.next() && lexrc.getInteger())
275                                 provides_ |= amsmath;
276                         break;
277
278                 case TC_PROVIDESMAKEIDX:
279                         if (lexrc.next() && lexrc.getInteger())
280                                 provides_ |= makeidx;
281                         break;
282
283                 case TC_PROVIDESURL:
284                         if (lexrc.next() && lexrc.getInteger())
285                                 provides_ = url;
286                         break;
287
288                 case TC_LEFTMARGIN:     // left margin type
289                         if (lexrc.next())
290                                 leftmargin_ = lexrc.getString();
291                         break;                  
292
293                 case TC_RIGHTMARGIN:    // right margin type
294                         if (lexrc.next())
295                                 rightmargin_ = lexrc.getString();
296                         break;
297                 }
298         }       
299
300         if (!merge) { // we are at top level here.
301                 lyxerr[Debug::TCLASS] << "Finished reading textclass " 
302                                       << MakeDisplayPath(filename)
303                                       << endl;
304                 if (defaultlayout_.empty()) {
305                         lyxerr << "Error: Textclass '" << name_
306                                << "' is missing a defaultstyle." << endl;
307                         error = true;
308                 }
309         } else
310                 lyxerr[Debug::TCLASS] << "Finished reading input file " 
311                                       << MakeDisplayPath(filename)
312                                       << endl;
313
314         return error;
315 }
316
317
318 void LyXTextClass::readOutputType(LyXLex & lexrc)
319 {
320         keyword_item outputTypeTags[] = {
321                 { "docbook", DOCBOOK },
322                 { "latex", LATEX },
323                 { "linuxdoc", LINUXDOC },
324                 { "literate", LITERATE }
325         };
326
327         pushpophelper pph(lexrc, outputTypeTags, LITERATE);
328
329         int le = lexrc.lex();
330         switch (le) {
331         case LyXLex::LEX_UNDEF:
332                 lexrc.printError("Unknown output type `$$Token'");
333                 return;
334         case LATEX:
335         case LINUXDOC:
336         case DOCBOOK:
337         case LITERATE:
338                 outputType_ = static_cast<OutputType>(le);
339                 break;
340         default:
341                 lyxerr << "Unhandled value " << le
342                        << " in LyXTextClass::readOutputType." << endl;
343
344                 break;
345         }
346 }
347
348
349 enum MaxCounterTags {
350         MC_COUNTER_CHAPTER = 1,
351         MC_COUNTER_SECTION,
352         MC_COUNTER_SUBSECTION,
353         MC_COUNTER_SUBSUBSECTION,
354         MC_COUNTER_PARAGRAPH,
355         MC_COUNTER_SUBPARAGRAPH,
356         MC_COUNTER_ENUMI,
357         MC_COUNTER_ENUMII,
358         MC_COUNTER_ENUMIII,
359         MC_COUNTER_ENUMIV
360 };
361
362
363 void LyXTextClass::readMaxCounter(LyXLex & lexrc)
364 {
365         keyword_item maxCounterTags[] = {
366                 {"counter_chapter", MC_COUNTER_CHAPTER },
367                 {"counter_enumi", MC_COUNTER_ENUMI },
368                 {"counter_enumii", MC_COUNTER_ENUMII },
369                 {"counter_enumiii", MC_COUNTER_ENUMIII },
370                 {"counter_enumiv", MC_COUNTER_ENUMIV },
371                 {"counter_paragraph", MC_COUNTER_PARAGRAPH },
372                 {"counter_section", MC_COUNTER_SECTION },
373                 {"counter_subparagraph", MC_COUNTER_SUBPARAGRAPH },
374                 {"counter_subsection", MC_COUNTER_SUBSECTION },
375                 {"counter_subsubsection", MC_COUNTER_SUBSUBSECTION }
376         };
377
378         pushpophelper pph(lexrc, maxCounterTags, MC_COUNTER_ENUMIV);
379         int le = lexrc.lex();
380         switch (le) {
381         case LyXLex::LEX_UNDEF:
382                 lexrc.printError("Unknown MaxCounter tag `$$Token'");
383                 return; 
384         default: break;
385         }
386         switch (static_cast<MaxCounterTags>(le)) {
387         case MC_COUNTER_CHAPTER:
388                 maxcounter_ = LABEL_COUNTER_CHAPTER;
389                 break;
390         case MC_COUNTER_SECTION:
391                 maxcounter_ = LABEL_COUNTER_SECTION;
392                 break;
393         case MC_COUNTER_SUBSECTION:
394                 maxcounter_ = LABEL_COUNTER_SUBSECTION;
395                 break;
396         case MC_COUNTER_SUBSUBSECTION:
397                 maxcounter_ = LABEL_COUNTER_SUBSUBSECTION;
398                 break;
399         case MC_COUNTER_PARAGRAPH:
400                 maxcounter_ = LABEL_COUNTER_PARAGRAPH;
401                 break;
402         case MC_COUNTER_SUBPARAGRAPH:
403                 maxcounter_ = LABEL_COUNTER_SUBPARAGRAPH;
404                 break;
405         case MC_COUNTER_ENUMI:
406                 maxcounter_ = LABEL_COUNTER_ENUMI;
407                 break;
408         case MC_COUNTER_ENUMII:
409                 maxcounter_ = LABEL_COUNTER_ENUMII;
410                 break;
411         case MC_COUNTER_ENUMIII:
412                 maxcounter_ = LABEL_COUNTER_ENUMIII;
413                 break;
414         case MC_COUNTER_ENUMIV:
415                 maxcounter_ = LABEL_COUNTER_ENUMIV;
416                 break;
417         }
418 }
419
420
421 enum ClassOptionsTags {
422         CO_FONTSIZE = 1,
423         CO_PAGESTYLE,
424         CO_OTHER,
425         CO_END
426 };
427
428
429 void LyXTextClass::readClassOptions(LyXLex & lexrc)
430 {
431         keyword_item classOptionsTags[] = {
432                 {"end", CO_END },
433                 {"fontsize", CO_FONTSIZE },
434                 {"other", CO_OTHER },
435                 {"pagestyle", CO_PAGESTYLE }
436         };
437
438         lexrc.pushTable(classOptionsTags, CO_END);
439         bool getout = false;
440         while (!getout && lexrc.isOK()) {
441                 int le = lexrc.lex();
442                 switch (le) {
443                 case LyXLex::LEX_UNDEF:
444                         lexrc.printError("Unknown ClassOption tag `$$Token'");
445                         continue; 
446                 default: break;
447                 }
448                 switch (static_cast<ClassOptionsTags>(le)) {
449                 case CO_FONTSIZE:
450                         lexrc.next();
451                         opt_fontsize_ = strip(lexrc.getString());
452                         break;
453                 case CO_PAGESTYLE:
454                         lexrc.next();
455                         opt_pagestyle_ = strip(lexrc.getString()); 
456                         break;
457                 case CO_OTHER:
458                         lexrc.next();
459                         options_ = lexrc.getString();
460                         break;
461                 case CO_END:
462                         getout = true;
463                         break;
464                 }
465         }
466         lexrc.popTable();
467 }
468
469
470 LyXFont const & LyXTextClass::defaultfont() const
471 {
472         return defaultfont_;
473 }
474
475
476 string const & LyXTextClass::leftmargin() const
477 {
478         return leftmargin_;
479 }
480
481
482 string const & LyXTextClass::rightmargin() const
483 {
484         return rightmargin_;
485 }
486
487
488 bool LyXTextClass::hasLayout(string const & n) const
489 {
490         string const name = (n.empty() ? defaultLayoutName() : lowercase(n));
491         
492         return find_if(layoutlist.begin(), layoutlist.end(),
493                        lyx::compare_memfun(&LyXLayout::name, name))
494                 != layoutlist.end();
495 }
496
497
498 LyXLayout const & LyXTextClass::operator[](string const & n) const
499 {
500         if (n.empty())
501                 lyxerr << "Operator[] called with empty n" << endl;
502         
503         string const name = (n.empty() ? defaultLayoutName() : lowercase(n));
504         
505         LayoutList::const_iterator cit =
506                 find_if(layoutlist.begin(),
507                         layoutlist.end(),
508                         lyx::compare_memfun(&LyXLayout::name, name));
509
510         if (cit == layoutlist.end()) {
511                 lyxerr << "We failed to find the layout '" << name
512                        << "' in the layout list. You MUST investigate!"
513                        << endl;
514                 
515                 // we require the name to exist
516                 lyx::Assert(false);
517         }
518
519         return *cit;
520 }
521
522
523 LyXLayout & LyXTextClass::operator[](string const & n)
524 {
525         if (n.empty())
526                 lyxerr << "Operator[] called with empty n" << endl;
527
528         string const name = (n.empty() ? defaultLayoutName() : lowercase(n));
529         
530         LayoutList::iterator it =
531                 find_if(layoutlist.begin(),
532                         layoutlist.end(),
533                         lyx::compare_memfun(&LyXLayout::name, name));
534
535         if (it == layoutlist.end()) {
536                 lyxerr << "We failed to find the layout '" << name
537                        << "' in the layout list. You MUST investigate!"
538                        << endl;
539                 
540                 // we require the name to exist
541                 lyx::Assert(false);
542         }
543         
544         return *it;
545 }
546
547
548 bool LyXTextClass::delete_layout(string const & n)
549 {
550         string const name = lowercase(n);
551
552         if (name == defaultLayoutName())
553                 return false;
554         
555         LayoutList::iterator it =
556                 remove_if(layoutlist.begin(), layoutlist.end(),
557                           lyx::compare_memfun(&LyXLayout::name, name));
558         LayoutList::iterator end = layoutlist.end();
559         bool const ret = (it != end);
560         layoutlist.erase(it, end);
561         return ret;
562 }
563
564
565 // Load textclass info if not loaded yet
566 bool LyXTextClass::load() const
567 {
568         if (loaded)
569                 return true;
570
571         // Read style-file
572         string const real_file = LibFileSearch("layouts", name_, "layout");
573
574         if (const_cast<LyXTextClass*>(this)->Read(real_file)) {
575                 lyxerr << "Error reading `"
576                        << MakeDisplayPath(real_file)
577                        << "'\n(Check `" << name_
578                        << "')\nCheck your installation and "
579                         "try Options/Reconfigure..." << endl;
580                 loaded = false;
581         }
582         loaded = true;
583         return loaded;
584         
585 }
586
587
588 string const LyXTextClass::defaultLayoutName() const
589 {
590         // This really should come from the actual layout... (Lgb)
591         return defaultlayout_;
592 }
593
594
595 LyXLayout const & LyXTextClass::defaultLayout() const
596 {
597         return operator[](defaultLayoutName());
598 }
599
600
601 LyXLayout & LyXTextClass::defaultLayout()
602 {
603         return operator[](defaultLayoutName());
604 }
605
606
607 string const & LyXTextClass::name() const
608 {
609         return name_;
610 }
611
612
613 string const & LyXTextClass::latexname() const
614 {
615         const_cast<LyXTextClass*>(this)->load();
616         return latexname_;
617 }
618
619
620 string const & LyXTextClass::description() const
621 {
622         return description_;
623 }
624
625
626 string const & LyXTextClass::opt_fontsize() const
627 {
628         return opt_fontsize_;
629 }
630
631
632 string const & LyXTextClass::opt_pagestyle() const
633 {
634         return opt_pagestyle_;
635 }
636
637
638 string const & LyXTextClass::options() const
639 {
640         return options_;
641 }
642
643
644 string const & LyXTextClass::pagestyle() const
645 {
646         return pagestyle_;
647 }
648
649
650 string const & LyXTextClass::preamble() const
651 {
652         return preamble_;
653 }
654
655
656 LyXTextClass::PageSides LyXTextClass::sides() const
657 {
658         return sides_;
659 }
660
661
662 int LyXTextClass::secnumdepth() const
663 {
664         return secnumdepth_;
665 }
666
667
668 int LyXTextClass::tocdepth() const
669 {
670         return tocdepth_;
671 }
672
673
674 OutputType LyXTextClass::outputType() const
675 {
676         return outputType_;
677 }
678
679
680 bool LyXTextClass::provides(LyXTextClass::Provides p) const
681 {
682         return provides_ & p;
683 }
684         
685
686 unsigned int LyXTextClass::columns() const
687 {
688         return columns_;
689 }
690
691
692 int LyXTextClass::maxcounter() const
693 {
694         return maxcounter_;
695 }
696
697
698 int LyXTextClass::size() const
699 {
700         return layoutlist.size();
701 }
702
703
704 ostream & operator<<(ostream & os, LyXTextClass::PageSides p)
705 {
706         switch (p) {
707         case LyXTextClass::OneSide:
708                 os << "1";
709                 break;
710         case LyXTextClass::TwoSides:
711                 os << "2";
712                 break;
713         }
714         return os;
715 }