1 /*
2  * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/>
3  *           (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com>
4  *
5  * This file is part of lsp-plugins
6  * Created on: 19 июл. 2017 г.
7  *
8  * lsp-plugins is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * any later version.
12  *
13  * lsp-plugins is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <ui/tk/tk.h>
23 
24 namespace lsp
25 {
26     namespace tk
27     {
28         const w_class_t LSPText::metadata = { "LSPText", &LSPGraphItem::metadata };
29 
LSPText(LSPDisplay * dpy)30         LSPText::LSPText(LSPDisplay *dpy):
31             LSPGraphItem(dpy),
32             sText(this),
33             sFont(dpy, this)
34         {
35             nCoords             = 0;
36             vCoords             = NULL;
37             fHAlign             = 0.0;
38             fVAlign             = 0.0;
39             nCenter             = 0;
40 
41             pClass          = &metadata;
42         }
43 
~LSPText()44         LSPText::~LSPText()
45         {
46             do_destroy();
47         }
48 
destroy()49         void LSPText::destroy()
50         {
51             LSPGraphItem::destroy();
52             do_destroy();
53         }
54 
do_destroy()55         void LSPText::do_destroy()
56         {
57             if (vCoords != NULL)
58             {
59                 free(vCoords);
60                 vCoords = NULL;
61             }
62             nCoords = 0;
63         }
64 
init()65         status_t LSPText::init()
66         {
67             sText.bind();
68 
69             status_t result = LSPGraphItem::init();
70             if (result != STATUS_OK)
71                 return result;
72 
73             init_color(C_GRAPH_TEXT, sFont.color());
74             vCoords             = reinterpret_cast<coord_t *>(malloc(2 * sizeof(coord_t)));
75             if (vCoords == NULL)
76                 return STATUS_NO_MEM;
77             nCoords             = 2;
78             for (size_t i=0; i<nCoords; ++i)
79             {
80                 vCoords[i].nBasis   = i;
81                 vCoords[i].fCoord   = 0.0f;
82             }
83 
84             sFont.init();
85             sFont.set_size(10.0f);
86 
87             return STATUS_OK;
88         }
89 
set_axes(size_t axes)90         status_t LSPText::set_axes(size_t axes)
91         {
92             if (nCoords == axes)
93                 return STATUS_OK;
94             if (axes == 0)
95             {
96                 if (vCoords != NULL)
97                 {
98                     free(vCoords);
99                     vCoords = NULL;
100                 }
101                 nCoords = 0;
102                 query_draw();
103                 return STATUS_OK;
104             }
105 
106             coord_t *ptr = (vCoords != NULL) ?
107                     reinterpret_cast<coord_t *>(realloc(vCoords, sizeof(coord_t) * axes)) :
108                     reinterpret_cast<coord_t *>(malloc(sizeof(coord_t) * axes));
109             if (ptr == NULL)
110                 return STATUS_NO_MEM;
111             for (size_t i=nCoords; i<axes; ++i)
112             {
113                 ptr[i].nBasis   = i;
114                 ptr[i].fCoord   = 0.0f;
115             }
116 
117             vCoords     = ptr;
118             nCoords     = axes;
119             return STATUS_OK;
120         }
121 
set_coord(size_t axis,float value)122         status_t LSPText::set_coord(size_t axis, float value)
123         {
124             if ((axis < 0) || (axis >= nCoords))
125                 return STATUS_OVERFLOW;
126             if (vCoords[axis].fCoord == value)
127                 return STATUS_OK;
128             vCoords[axis].fCoord = value;
129             query_draw();
130 
131             return STATUS_OK;
132         }
133 
set_basis(size_t axis,size_t value)134         status_t LSPText::set_basis(size_t axis, size_t value)
135         {
136             if ((axis < 0) || (axis >= nCoords))
137                 return STATUS_OVERFLOW;
138             if (vCoords[axis].nBasis == value)
139                 return STATUS_OK;
140             vCoords[axis].nBasis = value;
141             query_draw();
142 
143             return STATUS_OK;
144         }
145 
get_coord(size_t axis) const146         float LSPText::get_coord(size_t axis) const
147         {
148             return ((axis < 0) || (axis >= nCoords)) ? vCoords[axis].fCoord : 0.0f;
149         }
150 
get_basis(size_t axis) const151         size_t LSPText::get_basis(size_t axis) const
152         {
153             return ((axis < 0) || (axis >= nCoords)) ? vCoords[axis].nBasis : 0;
154         }
155 
set_halign(float value)156         void LSPText::set_halign(float value)
157         {
158             if (fHAlign == value)
159                 return;
160             fHAlign = value;
161             query_draw();
162         }
163 
set_valign(float value)164         void LSPText::set_valign(float value)
165         {
166             if (fVAlign == value)
167                 return;
168             fVAlign = value;
169             query_draw();
170         }
171 
set_center(size_t value)172         void LSPText::set_center(size_t value)
173         {
174             if (nCenter == value)
175                 return;
176             nCenter = value;
177             query_draw();
178         }
179 
render(ISurface * s,bool force)180         void LSPText::render(ISurface *s, bool force)
181         {
182             if (vCoords == NULL)
183                 return;
184 
185             LSPString text;
186             sText.format(&text);
187             if (text.is_empty())
188                 return;
189 
190             LSPGraph *cv = graph();
191             if (cv == NULL)
192                 return;
193 
194             // Get palette
195             Color font_color(sFont.raw_color());
196             font_color.scale_lightness(brightness());
197 
198             // Get center
199             float x = 0.0f, y = 0.0f;
200             cv->center(nCenter, &x, &y);
201 
202             // Apply all axis
203             for (size_t i=0; i<nCoords; ++i)
204             {
205                 coord_t *coord = &vCoords[i];
206                 // Get axis
207                 LSPAxis *axis = cv->axis(coord->nBasis);
208                 if (axis == NULL)
209                     return;
210                 // Apply changes
211                 if (!axis->apply(&x, &y, &coord->fCoord, 1))
212                     return;
213             }
214 
215             // Now we are ready to output text
216             font_parameters_t fp;
217             text_parameters_t tp;
218 
219             sFont.get_parameters(s, &fp);
220             sFont.get_multiline_text_parameters(s, &tp, &text);
221 
222             // Center point
223             ssize_t n_lines = 1 + text.count('\n');
224             ssize_t ty      = y - fp.Height * n_lines * (fVAlign + 1.0f)*0.5f - fp.Descent;
225             ssize_t tw      = tp.Width;
226 
227             // Estimate text size
228             ssize_t last = 0, curr = 0, tail = 0, len = text.length();
229 
230             while (curr < len)
231             {
232                 // Get next line indexes
233                 curr    = text.index_of(last, '\n');
234                 if (curr < 0)
235                 {
236                     curr        = len;
237                     tail        = len;
238                 }
239                 else
240                 {
241                     tail        = curr;
242                     if ((tail > last) && (text.at(tail-1) == '\r'))
243                         --tail;
244                 }
245 
246                 // Calculate text location
247                 sFont.get_text_parameters(s, &tp, &text, last, tail);
248                 ssize_t tx  = x + (tw - tp.Width*0.5f)*(fHAlign - 1.0f) + fHAlign*2.0f;
249                 ty         += fp.Height;
250                 sFont.draw(s, tx, ty, font_color, &text, last, tail);
251 
252                 last    = curr + 1;
253             }
254         }
255 
256     } /* namespace tk */
257 } /* namespace lsp */
258