1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3 
4   Copyright (C) 2005--2021 Han-Wen Nienhuys <hanwen@xs4all.nl>
5 
6   LilyPond is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation, either version 3 of the License, or
9   (at your option) any later version.
10 
11   LilyPond 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 LilyPond.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "engraver.hh"
21 #include "output-def.hh"
22 #include "paper-column.hh"
23 #include "pointer-group-interface.hh"
24 #include "side-position-interface.hh"
25 #include "spanner.hh"
26 #include "staff-symbol.hh"
27 #include "system-start-delimiter.hh"
28 
29 #include "translator.icc"
30 
31 #include <memory>
32 
33 using std::unique_ptr;
34 using std::vector;
35 
36 struct Bracket_nesting_node
37 {
38 public:
~Bracket_nesting_nodeBracket_nesting_node39   virtual ~Bracket_nesting_node () {}
add_staffBracket_nesting_node40   virtual bool add_staff (Grob *) { return false; }
add_supportBracket_nesting_node41   virtual void add_support (Grob *) {}
set_boundBracket_nesting_node42   virtual void set_bound (Direction, Grob *) {}
set_nesting_supportBracket_nesting_node43   virtual void set_nesting_support (Grob *) {}
create_grobsBracket_nesting_node44   virtual void create_grobs (Engraver *, SCM) {}
45 };
46 
47 struct Bracket_nesting_group : public Bracket_nesting_node
48 {
49   Spanner *delimiter_;
50   vector<unique_ptr<Bracket_nesting_node>> children_;
51   SCM symbol_;
52 
53   void from_list (SCM);
54   void add_support (Grob *grob) override;
55   bool add_staff (Grob *grob) override;
56   void set_nesting_support (Grob *) override;
57   void set_bound (Direction, Grob *grob) override;
58   void create_grobs (Engraver *, SCM) override;
59   ~Bracket_nesting_group ();
60   Bracket_nesting_group ();
61 };
62 
63 struct Bracket_nesting_staff : public Bracket_nesting_node
64 {
65   Grob *staff_;
66 
Bracket_nesting_staffBracket_nesting_staff67   Bracket_nesting_staff (Grob *s) { staff_ = s; }
68   bool add_staff (Grob *) override;
69 };
70 
Bracket_nesting_group()71 Bracket_nesting_group::Bracket_nesting_group ()
72 {
73   symbol_ = SCM_EOL;
74   delimiter_ = 0;
75 }
76 
77 bool
add_staff(Grob * g)78 Bracket_nesting_staff::add_staff (Grob *g)
79 {
80   if (!staff_)
81     {
82       staff_ = g;
83       return true;
84     }
85   return false;
86 }
87 
88 void
create_grobs(Engraver * engraver,SCM default_type)89 Bracket_nesting_group::create_grobs (Engraver *engraver, SCM default_type)
90 {
91   SCM type = scm_is_symbol (symbol_) ? symbol_ : default_type;
92   delimiter_ = engraver->make_spanner (ly_symbol2string (type).c_str (),
93                                        SCM_EOL);
94 
95   for (vsize i = 0; i < children_.size (); i++)
96     children_[i]->create_grobs (engraver, default_type);
97 }
98 
99 void
add_support(Grob * g)100 Bracket_nesting_group::add_support (Grob *g)
101 {
102   Side_position_interface::add_support (g, delimiter_);
103   for (vsize i = 0; i < children_.size (); i++)
104     children_[i]->add_support (g);
105 }
106 
~Bracket_nesting_group()107 Bracket_nesting_group::~Bracket_nesting_group ()
108 {
109 }
110 
111 void
set_bound(Direction d,Grob * g)112 Bracket_nesting_group::set_bound (Direction d, Grob *g)
113 {
114   delimiter_->set_bound (d, g);
115   for (vsize i = 0; i < children_.size (); i++)
116     children_[i]->set_bound (d, g);
117 }
118 
119 void
set_nesting_support(Grob * parent)120 Bracket_nesting_group::set_nesting_support (Grob *parent)
121 {
122   if (parent)
123     Side_position_interface::add_support (delimiter_, parent);
124 
125   for (vsize i = 0; i < children_.size (); i++)
126     children_[i]->set_nesting_support (delimiter_);
127 }
128 
129 void
from_list(SCM x)130 Bracket_nesting_group::from_list (SCM x)
131 {
132   for (SCM s = x; scm_is_pair (s); s = scm_cdr (s))
133     {
134       SCM entry = scm_car (s);
135       if (scm_is_pair (entry))
136         {
137           unique_ptr<Bracket_nesting_group> node (new Bracket_nesting_group);
138           node->from_list (entry);
139           children_.push_back (std::move (node));
140         }
141       else if (scm_is_eq (entry, ly_symbol2scm ("SystemStartBrace"))
142                || scm_is_eq (entry, ly_symbol2scm ("SystemStartBracket"))
143                || scm_is_eq (entry, ly_symbol2scm ("SystemStartBar"))
144                || scm_is_eq (entry, ly_symbol2scm ("SystemStartSquare")))
145         symbol_ = entry;
146       else
147         {
148           children_.push_back (unique_ptr<Bracket_nesting_staff>
149                                (new Bracket_nesting_staff (0)));
150         }
151     }
152 }
153 
154 bool
add_staff(Grob * grob)155 Bracket_nesting_group::add_staff (Grob *grob)
156 {
157   for (vsize i = 0; i < children_.size (); i++)
158     {
159       if (children_[i]->add_staff (grob))
160         {
161           Pointer_group_interface::add_grob (delimiter_,
162                                              ly_symbol2scm ("elements"), grob);
163           return true;
164         }
165     }
166   return false;
167 }
168 
169 /****************/
170 
171 class System_start_delimiter_engraver : public Engraver
172 {
173 public:
174   TRANSLATOR_DECLARATIONS (System_start_delimiter_engraver);
175 
176 protected:
177   Bracket_nesting_group *nesting_;
178 
179   void acknowledge_system_start_delimiter (Grob_info);
180   void acknowledge_staff_symbol (Grob_info_t<Spanner>);
181 
182   void process_music ();
183   void finalize () override;
184 };
185 
System_start_delimiter_engraver(Context * c)186 System_start_delimiter_engraver::System_start_delimiter_engraver (Context *c)
187   : Engraver (c)
188 {
189   nesting_ = 0;
190 }
191 
192 void
process_music()193 System_start_delimiter_engraver::process_music ()
194 {
195   if (!nesting_)
196     {
197       nesting_ = new Bracket_nesting_group ();
198       SCM hierarchy = get_property (this, "systemStartDelimiterHierarchy");
199       SCM delimiter_name = get_property (this, "systemStartDelimiter");
200 
201       nesting_->from_list (hierarchy);
202       nesting_->create_grobs (this, delimiter_name);
203       nesting_->set_bound (LEFT,
204                            unsmob<Grob> (get_property (this, "currentCommandColumn")));
205     }
206 }
207 
208 void
finalize()209 System_start_delimiter_engraver::finalize ()
210 {
211   if (nesting_)
212     {
213       nesting_->set_bound (RIGHT,
214                            unsmob<Grob> (get_property (this, "currentCommandColumn")));
215       nesting_->set_nesting_support (0);
216 
217       delete nesting_;
218     }
219 }
220 
221 void
acknowledge_staff_symbol(Grob_info_t<Spanner> inf)222 System_start_delimiter_engraver::acknowledge_staff_symbol
223 (Grob_info_t<Spanner> inf)
224 {
225   auto *const staff = inf.grob ();
226   bool succ = nesting_->add_staff (staff);
227 
228   if (!succ)
229     {
230       nesting_->children_.push_back (unique_ptr<Bracket_nesting_staff>
231                                      (new Bracket_nesting_staff (0)));
232       nesting_->add_staff (staff);
233     }
234 }
235 
236 void
acknowledge_system_start_delimiter(Grob_info inf)237 System_start_delimiter_engraver::acknowledge_system_start_delimiter (Grob_info inf)
238 {
239   nesting_->add_support (inf.grob ());
240 }
241 
242 void
boot()243 System_start_delimiter_engraver::boot ()
244 {
245   ADD_ACKNOWLEDGER (System_start_delimiter_engraver, staff_symbol);
246   ADD_ACKNOWLEDGER (System_start_delimiter_engraver, system_start_delimiter);
247 }
248 
249 ADD_TRANSLATOR (System_start_delimiter_engraver,
250                 /* doc */
251                 "Create a system start delimiter (i.e., a"
252                 " @code{SystemStartBar}, @code{SystemStartBrace},"
253                 " @code{SystemStartBracket} or @code{SystemStartSquare}"
254                 " spanner).",
255 
256                 /* create */
257                 "SystemStartSquare "
258                 "SystemStartBrace "
259                 "SystemStartBracket "
260                 "SystemStartBar ",
261 
262                 /* read */
263                 "systemStartDelimiter "
264                 "systemStartDelimiterHierarchy "
265                 "currentCommandColumn ",
266 
267                 /* write */
268                 ""
269                );
270