add file size toggle icons, ff_lut3d icon, fullscrn clk2play fix, timebar endpt curso...
[goodguy/history.git] / cinelerra-5.1 / guicast / bctheme.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program 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 2 of the License, or
9  * (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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "bcresources.h"
23 #include "bctheme.h"
24 #include "bcwindowbase.h"
25 #include "clip.h"
26 #include "language.h"
27 #include "vframe.h"
28
29 #include <errno.h>
30 #include <stdio.h>
31 #include <string.h>
32
33 BC_Theme::BC_Theme()
34 {
35         last_image_data = 0;
36         last_image_set = 0;
37         image_sets_start = -1;
38 }
39
40 BC_Theme::~BC_Theme()
41 {
42         image_sets.remove_all_objects();
43         images.remove_all_objects();
44 }
45
46 void BC_Theme::dump()
47 {
48         printf("BC_Theme::dump 1 images=%d\n", images.size());
49         for( int i=0; i<images.size(); ++i ) {
50                 BC_ImageData *image_data = images[i];
51                 printf("%4d. %s %p\n", i, image_data->name, image_data->data);
52         }
53         printf("BC_Theme::dump 2 image_sets=%d\n", image_sets.size());
54         for( int i=0; i<image_sets.size(); ++i ) {
55                 BC_ThemeSet *image_set = image_sets[i];
56                 printf("%4d. %s %p\n", i, image_set->title, image_set->data[0]);
57         }
58 }
59
60 BC_Resources* BC_Theme::get_resources()
61 {
62         return BC_WindowBase::get_resources();
63 }
64
65 // These create single images for storage in the image_sets table.
66 VFrame* BC_Theme::new_image(const char *title, const char *path)
67 {
68         VFrame *existing_image = title[0] ? get_image(title, 0) : 0;
69         if( existing_image ) return existing_image;
70
71         BC_ThemeSet *result = new BC_ThemeSet(1, 0, title);
72         result->data[0] = new VFramePng(get_image_data(path));
73         add_image_set(result);
74         return result->data[0];
75 }
76
77 VFrame* BC_Theme::new_image(const char *path)
78 {
79         return new_image("", path);
80 }
81
82 // These create image sets which are stored in the image_sets table.
83 VFrame** BC_Theme::new_image_set(const char *title, int total, va_list *args)
84 {
85         if( !total ) {
86                 printf("BC_Theme::new_image_set %d %s zero number of images\n",
87                         __LINE__, title);
88         }
89
90         VFrame **existing_image_set = title[0] ? get_image_set(title, 0) : 0;
91         if( existing_image_set ) return existing_image_set;
92
93         BC_ThemeSet *result = new BC_ThemeSet(total, 1, title);
94         add_image_set(result);
95         for( int i=0; i<total; ++i ) {
96                 char *path = va_arg(*args, char*);
97                 result->data[i] = new_image(path);
98         }
99         return result->data;
100 }
101
102 VFrame** BC_Theme::new_image_set_images(const char *title, int total, ...)
103 {
104         va_list list;
105         va_start(list, total);
106         BC_ThemeSet *existing_image_set = title[0] ? get_image_set_object(title) : 0;
107         if( existing_image_set ) {
108                 image_sets.remove_object(existing_image_set);
109         }
110
111         BC_ThemeSet *result = new BC_ThemeSet(total, 0, title);
112         add_image_set(result);
113         for( int i=0; i<total; ++i ) {
114                 result->data[i] = va_arg(list, VFrame*);
115         }
116         va_end(list);
117         return result->data;
118 }
119
120 VFrame** BC_Theme::new_image_set(const char *title, int total, ...)
121 {
122         va_list list;
123         va_start(list, total);
124         VFrame **result = new_image_set(title, total, &list);
125         va_end(list);
126
127         return result;
128 }
129
130 VFrame** BC_Theme::new_image_set(int total, ...)
131 {
132         va_list list;
133         va_start(list, total);
134         VFrame **result = new_image_set("", total, &list);
135         va_end(list);
136
137         return result;
138 }
139
140 void BC_Theme::add_image_set(BC_ThemeSet *image_set)
141 {
142         image_sets.append(image_set);
143         if( image_sets_start >= 0 ) {
144                 printf("BC_Theme::add_image_set image_sets unsorted, lookups degraded\n");
145                 image_sets_start = -1;
146         }
147 }
148
149 int BC_Theme::image_set_cmpr(const void *ap, const void *bp)
150 {
151         BC_ThemeSet*a = *(BC_ThemeSet**)ap, *b = *(BC_ThemeSet**)bp;
152         return strcmp(a->title, b->title);
153 }
154
155 void BC_Theme::sort_image_sets()
156 {
157         if( image_sets_start >= 0 ) return;
158         qsort(&image_sets[0], image_sets.size(), sizeof(image_sets[0]), image_set_cmpr);
159 // skip over un-titled image sets
160         int i = 0, n = image_sets.size();
161         while( i<n && !image_sets[i]->title[0] ) ++i;
162         image_sets_start = i;
163 }
164
165 BC_ThemeSet* BC_Theme::get_image_set_object(const char *title)
166 {
167         if( last_image_set && !strcmp(title,last_image_set->title) )
168                 return last_image_set;
169
170         if( image_sets_start >= 0 ) {
171 // binary search for image set
172                 int r = image_sets.size(), l = image_sets_start-1;
173                 int m = 0, v = -1;
174                 while( r-l > 1 ) {
175                         m = (l + r) / 2;
176                         BC_ThemeSet *image_set = image_sets[m];
177                         if( !(v=strcmp(title, image_set->title)) )
178                                 return last_image_set = image_set;
179                         if( v > 0 ) l = m; else r = m;
180                 }
181         }
182         else {
183 // compare title[0],title[1] for faster prefix test
184                 const unsigned char *tp = (const unsigned char*)title;
185                 unsigned short tval = tp[0];
186                 if( tval ) tval |= (tp[1] << 8);
187
188                 for( int i=0; i<image_sets.size(); ++i ) {
189                         tp = (const unsigned char *)image_sets[i]->title;
190                         unsigned short val = tp[0];
191                         if( val ) val |= (tp[1] << 8);
192                         if( val != tval ) continue;
193                         if( !strcmp((const char *)tp, title) )
194                                 return last_image_set = image_sets[i];
195                 }
196         }
197
198         return 0;
199 }
200
201 VFrame* BC_Theme::get_image(const char *title, int use_default)
202 {
203         BC_ThemeSet* tsp = get_image_set_object(title);
204         if( tsp ) return tsp->data[0];
205
206
207 // Return the first image it can find.  This should always work.
208         if( use_default ) {
209                 printf("BC_Theme::get_image: image \"%s\" not found.\n",
210                         title);
211                 if( image_sets.size() )
212                         return image_sets[0]->data[0];
213         }
214
215 // Give up and go to a movie.
216         return 0;
217 }
218
219 VFrame** BC_Theme::get_image_set(const char *title, int use_default)
220 {
221         BC_ThemeSet* tsp = get_image_set_object(title);
222         if( tsp ) return tsp->data;
223
224 // Get the image set with the largest number of images.
225         if( use_default ) {
226                 printf("BC_Theme::get_image_set: image set \"%s\" not found.\n",
227                         title);
228                 int max_total = 0;
229                 int max_number = -1;
230                 for( int i=0; i<image_sets.size(); ++i ) {
231                         if( image_sets[i]->total > max_total ) {
232                                 max_total = image_sets[i]->total;
233                                 max_number = i;
234                         }
235                 }
236
237                 if( max_number >= 0 )
238                         return image_sets[max_number]->data;
239         }
240
241 // Give up and go to a movie
242         return 0;
243 }
244
245
246
247
248
249
250
251
252
253
254
255 VFrame** BC_Theme::new_button(const char *overlay_path,
256         const char *up_path,
257         const char *hi_path,
258         const char *dn_path,
259         const char *title)
260 {
261         VFramePng default_data(get_image_data(overlay_path));
262         BC_ThemeSet *result = new BC_ThemeSet(3, 1, title ? title : "");
263         if( title ) add_image_set(result);
264
265         result->data[0] = new_image(up_path);
266         result->data[1] = new_image(hi_path);
267         result->data[2] = new_image(dn_path);
268         for( int i=0; i<3; ++i ) {
269                 overlay(result->data[i], &default_data, -1, -1, (i == 2));
270         }
271         return result->data;
272 }
273
274
275 VFrame** BC_Theme::new_button4(const char *overlay_path,
276         const char *up_path,
277         const char *hi_path,
278         const char *dn_path,
279         const char *disabled_path,
280         const char *title)
281 {
282         VFramePng default_data(get_image_data(overlay_path));
283         BC_ThemeSet *result = new BC_ThemeSet(4, 1, title ? title : "");
284         if( title ) add_image_set(result);
285
286         result->data[0] = new_image(up_path);
287         result->data[1] = new_image(hi_path);
288         result->data[2] = new_image(dn_path);
289         result->data[3] = new_image(disabled_path);
290         for( int i=0; i<4; ++i ) {
291                 overlay(result->data[i], &default_data, -1, -1, (i == 2));
292         }
293         return result->data;
294 }
295
296
297 VFrame** BC_Theme::new_button(const char *overlay_path,
298         VFrame *up,
299         VFrame *hi,
300         VFrame *dn,
301         const char *title)
302 {
303         VFramePng default_data(get_image_data(overlay_path));
304         BC_ThemeSet *result = new BC_ThemeSet(3, 0, title ? title : "");
305         if( title ) add_image_set(result);
306
307         result->data[0] = new VFrame(*up);
308         result->data[1] = new VFrame(*hi);
309         result->data[2] = new VFrame(*dn);
310         for( int i=0; i<3; ++i )
311                 overlay(result->data[i], &default_data, -1, -1, (i == 2));
312         return result->data;
313 }
314
315
316 VFrame** BC_Theme::new_toggle(const char *overlay_path,
317         const char *up_path,
318         const char *hi_path,
319         const char *checked_path,
320         const char *dn_path,
321         const char *checkedhi_path,
322         const char *title)
323 {
324         VFramePng default_data(get_image_data(overlay_path));
325         BC_ThemeSet *result = new BC_ThemeSet(5, 1, title ? title : "");
326         if( title ) add_image_set(result);
327
328         result->data[0] = new_image(up_path);
329         result->data[1] = new_image(hi_path);
330         result->data[2] = new_image(checked_path);
331         result->data[3] = new_image(dn_path);
332         result->data[4] = new_image(checkedhi_path);
333         for( int i=0; i<5; ++i )
334                 overlay(result->data[i], &default_data, -1, -1, (i == 3));
335         return result->data;
336 }
337
338 VFrame** BC_Theme::new_toggle(const char *overlay_path,
339         VFrame *up,
340         VFrame *hi,
341         VFrame *checked,
342         VFrame *dn,
343         VFrame *checkedhi,
344         const char *title)
345 {
346         VFramePng default_data(get_image_data(overlay_path));
347         BC_ThemeSet *result = new BC_ThemeSet(5, 0, title ? title : "");
348         if( title ) add_image_set(result);
349
350         result->data[0] = new VFrame(*up);
351         result->data[1] = new VFrame(*hi);
352         result->data[2] = new VFrame(*checked);
353         result->data[3] = new VFrame(*dn);
354         result->data[4] = new VFrame(*checkedhi);
355         for( int i=0; i<5; ++i )
356                 overlay(result->data[i], &default_data, -1, -1, (i == 3));
357         return result->data;
358 }
359
360 void BC_Theme::overlay(VFrame *dst, VFrame *src, int in_x1, int in_x2, int shift)
361 {
362         int w;
363         int h;
364         unsigned char **in_rows;
365         unsigned char **out_rows;
366
367         if( in_x1 < 0 ) {
368                 w = MIN(src->get_w(), dst->get_w());
369                 h = MIN(dst->get_h(), src->get_h());
370                 in_x1 = 0;
371                 in_x2 = w;
372         }
373         else {
374                 w = in_x2 - in_x1;
375                 h = MIN(dst->get_h(), src->get_h());
376         }
377         in_rows = src->get_rows();
378         out_rows = dst->get_rows();
379
380         switch( src->get_color_model() )
381         {
382                 case BC_RGBA8888:
383                         switch( dst->get_color_model() )
384                         {
385                                 case BC_RGBA8888:
386                                         for( int i=shift; i<h; ++i ) {
387                                                 unsigned char *in_row = 0;
388                                                 unsigned char *out_row;
389
390                                                 if( !shift ) {
391                                                         in_row = in_rows[i] + in_x1 * 4;
392                                                         out_row = out_rows[i];
393                                                 }
394                                                 else {
395                                                         in_row = in_rows[i - 1] + in_x1 * 4;
396                                                         out_row = out_rows[i] + 4;
397                                                 }
398
399                                                 for( int j=shift; j<w; ++j ) {
400                                                         int opacity = in_row[3];
401                                                         int transparency = 0xff - opacity;
402
403                                                         out_row[0] = (in_row[0] * opacity + out_row[0] * transparency) / 0xff;
404                                                         out_row[1] = (in_row[1] * opacity + out_row[1] * transparency) / 0xff;
405                                                         out_row[2] = (in_row[2] * opacity + out_row[2] * transparency) / 0xff;
406                                                         out_row[3] = MAX(in_row[3], out_row[3]);
407                                                         out_row += 4;
408                                                         in_row += 4;
409                                                 }
410                                         }
411                                         break;
412
413                                 case BC_RGB888:
414                                         for( int i=shift; i<h; ++i ) {
415                                                 unsigned char *in_row;
416                                                 unsigned char *out_row = out_rows[i];
417
418                                                 if( !shift ) {
419                                                         in_row = in_rows[i] + in_x1 * 3;
420                                                         out_row = out_rows[i];
421                                                 }
422                                                 else {
423                                                         in_row = in_rows[i - 1] + in_x1 * 3;
424                                                         out_row = out_rows[i] + 3;
425                                                 }
426
427                                                 for( int j=shift; j<w; ++j ) {
428                                                         int opacity = in_row[3];
429                                                         int transparency = 0xff - opacity;
430                                                         out_row[0] = (in_row[0] * opacity + out_row[0] * transparency) / 0xff;
431                                                         out_row[1] = (in_row[1] * opacity + out_row[1] * transparency) / 0xff;
432                                                         out_row[2] = (in_row[2] * opacity + out_row[2] * transparency) / 0xff;
433                                                         out_row += 3;
434                                                         in_row += 4;
435                                                 }
436                                         }
437                                         break;
438                         }
439                         break;
440         }
441 }
442
443 void BC_Theme::set_data(unsigned char *ptr)
444 {
445         int hdr_sz = *(int*)ptr - sizeof(int);
446         unsigned char *cp = ptr + sizeof(int);
447         unsigned char *dp = cp + hdr_sz;
448         int start_item = images.size();
449
450         while( cp < dp ) {
451                 char *nm = (char *)cp;
452                 while( cp < dp && *cp++ );
453                 if( cp + sizeof(unsigned) > dp ) break;
454                 unsigned ofs = 0;
455                 for( int i=sizeof(unsigned); --i>=0; ofs|=cp[i] ) ofs <<= 8;
456                 images.append(new BC_ImageData(nm, dp+ofs));
457                 cp += sizeof(unsigned);
458         }
459
460         int items = images.size() - start_item;
461         data_items.append(items);
462         qsort(&images[start_item], items, sizeof(images[0]), images_cmpr);
463 }
464
465 int BC_Theme::images_cmpr(const void *ap, const void *bp)
466 {
467         BC_ImageData *a = *(BC_ImageData**)ap, *b = *(BC_ImageData**)bp;
468         return strcmp(a->name, b->name);
469 }
470
471 unsigned char* BC_Theme::get_image_data(const char *name, int log_errs)
472 {
473 // Image is the same as the last one
474         if( last_image_data && !strcmp(last_image_data->name, name) )
475                 return last_image_data->data;
476
477 // look forwards thru data sets for name
478         int start_item = 0;
479         for( int i=0,n=data_items.size(); i<n; ++i ) {
480                 int end_item = start_item + data_items[i];
481                 int r = end_item, l = start_item-1;
482 // binary search for image
483                 int m = 0, v = -1;
484                 while( r-l > 1 ) {
485                         m = (l + r) / 2;
486                         BC_ImageData *image_data = images[m];
487                         if( !(v=strcmp(name, image_data->name)) ) {
488                                 image_data->used = 1;
489                                 last_image_data = image_data;
490                                 return image_data->data;
491                         }
492                         if( v > 0 ) l = m; else r = m;
493                 }
494                 start_item = end_item;
495         }
496
497         if( log_errs )
498                 fprintf(stderr, _("Theme::get_image: %s not found.\n"), name);
499         return 0;
500 }
501
502 void BC_Theme::check_used()
503 {
504 // Can't use because some images are gotten the old fashioned way.
505 return;
506         int got_it = 0;
507         for( int i=0; i<images.size(); ++i ) {
508                 if( !images[i]->used ) {
509                         if( !got_it ) printf(_("BC_Theme::check_used: Images aren't used.\n"));
510                         printf("%s ", images[i]->name);
511                         got_it = 1;
512                 }
513         }
514         if( got_it ) printf("\n");
515 }
516
517
518 BC_ThemeSet::BC_ThemeSet(int total, int is_reference, const char *title)
519 {
520         this->total = total;
521         this->title = new char[strlen(title) + 1];
522         strcpy(this->title, title);
523         this->is_reference = is_reference;
524         data = new VFrame*[total];
525 }
526
527 BC_ThemeSet::~BC_ThemeSet()
528 {
529         if( data ) {
530                 if( !is_reference ) {
531                         for( int i = 0; i < total; i++ )
532                                 delete data[i];
533                 }
534
535                 delete [] data;
536         }
537
538         delete [] title;
539 }
540