Credit ffmpeg team with upgrade to 6.1
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / formattools.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2010-2013 Adam Williams <broadcast at earthling dot net>
5  * Copyright (C) 2003-2016 Cinelerra CV contributors
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  */
22
23 #include "asset.h"
24 #include "bcsignals.h"
25 #include "clip.h"
26 #include "guicast.h"
27 #include "file.h"
28 #include "filesystem.h"
29 #include "formattools.h"
30 #include "language.h"
31 #ifdef HAVE_DV
32 #include "libdv.h"
33 #endif
34 #include "libmjpeg.h"
35 #include "maxchannels.h"
36 #include "mwindow.h"
37 #include "preferences.h"
38 #include "theme.h"
39 #include "videodevice.inc"
40 #include <string.h>
41 #include <unistd.h>
42 #include <ctype.h>
43
44
45 FormatTools::FormatTools(MWindow *mwindow,
46                                 BC_WindowBase *window,
47                                 Asset *asset)
48 {
49         this->mwindow = mwindow;
50         this->window = window;
51         this->asset = asset;
52
53         aparams_button = 0;
54         vparams_button = 0;
55         aparams_thread = 0;
56         vparams_thread = 0;
57         audio_switch = 0;
58         video_switch = 0;
59         path_textbox = 0;
60         path_button = 0;
61         path_recent = 0;
62         format_title = 0;
63         format_button = 0;
64         format_text = 0;
65         audio_title = 0;
66         video_title = 0;
67         labeled_files = 0;
68         w = window->get_w();
69
70         recording = 0;
71         use_brender = 0;
72         do_audio = 0;
73         do_video = 0;
74         prompt_audio = 0;
75         prompt_audio_channels = 0;
76         prompt_video = 0;
77         prompt_video_compression = 0;
78         file_per_label = 0;
79         locked_compressor = 0;
80         video_driver = 0;
81 }
82
83 FormatTools::~FormatTools()
84 {
85         delete path_recent;
86 SET_TRACE
87         delete path_button;
88 SET_TRACE
89         delete path_textbox;
90 SET_TRACE
91         delete format_button;
92 SET_TRACE
93
94         if(aparams_button) delete aparams_button;
95 SET_TRACE
96         if(vparams_button) delete vparams_button;
97 SET_TRACE
98         if(aparams_thread) delete aparams_thread;
99 SET_TRACE
100         if(vparams_thread) delete vparams_thread;
101 SET_TRACE
102 }
103
104 void FormatTools::create_objects(
105                 int &init_x, int &init_y,
106                 int do_audio, int do_video,   // Include support for audio, video
107                 int prompt_audio, int prompt_video, // Include checkbox for audio, video
108                 int prompt_audio_channels,
109                 int prompt_video_compression,
110                 const char *locked_compressor,
111                 int recording,
112                 int *file_per_label,
113                 int brender,
114                 int horizontal_layout)
115 {
116         int ys10 = yS(10);
117         int x = init_x;
118         int y = init_y;
119         int ylev = init_y;
120         int margin = mwindow->theme->widget_border;
121
122         this->locked_compressor = locked_compressor;
123         this->recording = recording;
124         this->use_brender = brender;
125         this->do_audio = do_audio;
126         this->do_video = do_video;
127         this->prompt_audio = prompt_audio;
128         this->prompt_audio_channels = prompt_audio_channels;
129         this->prompt_video = prompt_video;
130         this->prompt_video_compression = prompt_video_compression;
131         this->file_per_label = file_per_label;
132
133
134         if(asset->format == FILE_UNKNOWN)
135                 asset->format = FILE_FFMPEG;
136 //printf("FormatTools::create_objects 1\n");
137
138         if(!recording)
139         {
140                 int px = x;
141                 window->add_subwindow(path_textbox = new FormatPathText(px, y, this));
142                 path_textbox->context_help_set_keyword("File Format section");
143                 px += path_textbox->get_w() + 5;
144                 path_recent = new BC_RecentList("PATH", mwindow->defaults,
145                                         path_textbox, 10, px, y, xS(300), yS(100));
146                 window->add_subwindow(path_recent);
147                 path_recent->load_items(File::formattostr(asset->format));
148                 path_recent->context_help_set_keyword("File Format section");
149                 px += path_recent->get_w();
150                 window->add_subwindow(path_button = new BrowseButton(
151                         mwindow->theme, window, path_textbox, px, y, asset->path,
152                         _("Output to file"), _("Select a file to write to:"), 0));
153                 path_button->context_help_set_keyword("File Format section");
154
155 // Set w for user.
156                 w = MAX(w, xS(305));
157                 y += path_textbox->get_h() + ys10;
158         }
159         else
160         {
161 //              w = x + xS(305);
162                 w = xS(305);
163         }
164
165         x = init_x;
166         window->add_subwindow(format_title = new BC_Title(x, y, _("File Format:")));
167         format_title->context_help_set_keyword("File Format section");
168         x += format_title->get_w() + margin;
169         window->add_subwindow(format_text = new BC_TextBox(x, y, xS(160), 1,
170                 File::formattostr(asset->format)));
171         format_text->context_help_set_keyword("File Format section");
172         x += format_text->get_w() + margin;
173 //printf("FormatTools::create_objects %d %p\n", __LINE__, window);
174         window->add_subwindow(format_button = new FormatFormat(x, y, this));
175         format_button->create_objects();
176         format_button->context_help_set_keyword("File Format section");
177         x += format_button->get_w() + 5;
178         window->add_subwindow(ffmpeg_type = new FFMpegType(x, y, xS(70), 1, asset->fformat));
179         FFMPEG::set_asset_format(asset, mwindow->edl, asset->fformat);
180         ffmpeg_type->context_help_set_keyword("File Format section");
181         x += ffmpeg_type->get_w();
182         window->add_subwindow(format_ffmpeg = new FormatFFMPEG(x, y, this));
183         format_ffmpeg->create_objects();
184         format_ffmpeg->context_help_set_keyword("File Format section");
185         x = init_x;
186         y += format_button->get_h() + ys10;
187         if( do_audio ) {
188                 window->add_subwindow(audio_title = new BC_Title(x, y, _("Audio:"), LARGEFONT,
189                         BC_WindowBase::get_resources()->audiovideo_color));
190                 audio_title->context_help_set_keyword("File Format section");
191                 x += audio_title->get_w() + margin;
192                 window->add_subwindow(aparams_button = new FormatAParams(mwindow, this, x, y));
193                 aparams_button->context_help_set_keyword("File Format section");
194                 x += aparams_button->get_w() + margin;
195                 if(prompt_audio) {
196                         window->add_subwindow(audio_switch = new FormatAudio(x, y, this, asset->audio_data));
197                         audio_switch->context_help_set_keyword("File Format section");
198                 }
199                 x = init_x;
200                 ylev = y;
201                 y += aparams_button->get_h() + ys10;
202
203 //printf("FormatTools::create_objects 6\n");
204                 aparams_thread = new FormatAThread(this);
205         }
206
207 //printf("FormatTools::create_objects 7\n");
208         if( do_video ) {
209                 if( horizontal_layout && do_audio ) {
210                         x += xS(370);
211                         y = ylev;
212                 }
213
214 //printf("FormatTools::create_objects 8\n");
215                 window->add_subwindow(video_title = new BC_Title(x, y, _("Video:"), LARGEFONT,
216                         BC_WindowBase::get_resources()->audiovideo_color));
217                 video_title->context_help_set_keyword("File Format section");
218                 x += video_title->get_w() + margin;
219                 if(prompt_video_compression) {
220                         window->add_subwindow(vparams_button = new FormatVParams(mwindow, this, x, y));
221                         vparams_button->context_help_set_keyword("File Format section");
222                         x += vparams_button->get_w() + margin;
223                 }
224
225 //printf("FormatTools::create_objects 9\n");
226                 if(prompt_video) {
227                         window->add_subwindow(video_switch = new FormatVideo(x, y, this, asset->video_data));
228                         video_switch->context_help_set_keyword("File Format section");
229                         y += video_switch->get_h();
230                 }
231                 else {
232                         y += vparams_button->get_h();
233                 }
234
235 //printf("FormatTools::create_objects 10\n");
236                 y += ys10;
237                 vparams_thread = new FormatVThread(this);
238         }
239
240 //printf("FormatTools::create_objects 11\n");
241
242         x = init_x;
243         if( file_per_label ) {
244                 labeled_files = new FormatFilePerLabel(this, x, y, file_per_label);
245                 window->add_subwindow(labeled_files);
246                 labeled_files->context_help_set_keyword("File Format section");
247                 y += labeled_files->get_h() + ys10;
248         }
249
250 //printf("FormatTools::create_objects 12\n");
251
252         init_y = y;
253         update_format();
254 }
255
256 void FormatTools::update_driver(int driver)
257 {
258         this->video_driver = driver;
259
260         locked_compressor = 0;
261         switch(driver)
262         {
263                 case CAPTURE_DVB:
264                 case VIDEO4LINUX2MPEG:
265 // Just give the user information about how the stream is going to be
266 // stored but don't change the asset.
267 // Want to be able to revert to user settings.
268                         if(asset->format == FILE_MPEG) break;
269                         asset->format = FILE_MPEG;
270                         format_text->update(File::formattostr(asset->format));
271                         asset->audio_data = 1;
272                         asset->video_data = 1;
273                         audio_switch->update(1);
274                         video_switch->update(1);
275                         break;
276
277                 case CAPTURE_IEC61883:
278                 case CAPTURE_FIREWIRE:
279                 case VIDEO4LINUX2JPEG:
280                 case CAPTURE_JPEG_WEBCAM:
281                         asset->format = FILE_FFMPEG;
282                         format_text->update(File::formattostr(asset->format));
283
284                         switch(driver) {
285 #ifdef HAVE_DV
286                         case CAPTURE_IEC61883:
287                         case CAPTURE_FIREWIRE:
288                                 locked_compressor = (char*)CODEC_TAG_DVSD;
289                                 break;
290 #endif
291                         case VIDEO4LINUX2JPEG:
292                                 locked_compressor = (char*)CODEC_TAG_MJPEG;
293                                 break;
294
295                         case CAPTURE_JPEG_WEBCAM:
296                                 locked_compressor = (char*)CODEC_TAG_JPEG;
297                                 break;
298                         }
299                         if( locked_compressor )
300                                 strcpy(asset->vcodec, locked_compressor);
301
302                         audio_switch->update(asset->audio_data);
303                         video_switch->update(asset->video_data);
304                         break;
305
306                 default:
307                         format_text->update(File::formattostr(asset->format));
308                         audio_switch->update(asset->audio_data);
309                         video_switch->update(asset->video_data);
310                         break;
311         }
312         close_format_windows();
313         update_format();
314 }
315
316 void FormatTools::update_format()
317 {
318         if( do_audio && prompt_audio && audio_switch ) {
319                 audio_switch->update(asset->audio_data);
320                 if( File::renders_audio(asset) )
321                         audio_switch->enable();
322                 else
323                         audio_switch->disable();
324         }
325         if( do_video && prompt_video && video_switch ) {
326                 video_switch->update(asset->video_data);
327                 if( File::renders_video(asset) )
328                         video_switch->enable();
329                 else
330                         video_switch->disable();
331         }
332         if( asset->format == FILE_FFMPEG ) {
333                 ffmpeg_type->show();
334                 format_ffmpeg->show();
335         }
336         else {
337                 ffmpeg_type->hide();
338                 format_ffmpeg->hide();
339         }
340 }
341
342 int FormatTools::handle_event()
343 {
344         return 0;
345 }
346
347 Asset* FormatTools::get_asset()
348 {
349         return asset;
350 }
351
352 void FormatTools::update_extension()
353 {
354         const char *extension = File::get_tag(asset->format);
355 // split multiple extensions
356         ArrayList<const char*> extensions;
357         int len = !extension ? -1 : strlen(extension);
358         const char *extension_ptr = extension;
359         for(int i = 0; i <= len; i++)
360         {
361                 if(extension[i] == '/' || extension[i] == 0)
362                 {
363                         extensions.append(extension_ptr);
364                         extension_ptr = extension + i + 1;
365                 }
366         }
367
368         if(extensions.size())
369         {
370                 char *ptr = strrchr(asset->path, '.');
371                 if(!ptr)
372                 {
373                         ptr = asset->path + strlen(asset->path);
374                         *ptr = '.';
375                 }
376                 ptr++;
377
378 // test for equivalent extension
379                 int need_extension = 1;
380                 //int extension_len = 0;
381                 for(int i = 0; i < extensions.size() && need_extension; i++)
382                 {
383                         char *ptr1 = ptr;
384                         extension_ptr = extensions.get(i);
385 // test an extension
386                         need_extension = 0;
387                         while(*ptr1 != 0 && *extension_ptr != 0 && *extension_ptr != '/')
388                         {
389                                 if(tolower(*ptr1) != tolower(*extension_ptr))
390                                 {
391                                         need_extension = 1;
392                                         break;
393                                 }
394                                 ptr1++;
395                                 extension_ptr++;
396                         }
397
398                         if( (!*ptr1 && (*extension_ptr && *extension_ptr != '/')) ||
399                             (*ptr1 && (!*extension_ptr || *extension_ptr == '/')) )
400                                 need_extension = 1;
401                 }
402
403 //printf("FormatTools::update_extension %d %d\n", __LINE__, need_extension);
404 // copy extension
405                 if(need_extension)
406                 {
407                         char *ptr1 = ptr;
408 // change "qt" to "mov" since ffmpeg does not know qt
409                         extension_ptr = asset->format != FILE_FFMPEG ? extensions.get(0) :
410                                 !strcmp(asset->fformat, "qt" ) ||
411                                 !strcmp(asset->fformat, "pro" ) ? "mov" : asset->fformat ;
412                         while(*extension_ptr != 0 && *extension_ptr != '/')
413                                 *ptr1++ = *extension_ptr++;
414                         *ptr1 = 0;
415                 }
416
417                 int character1 = ptr - asset->path;
418                 int character2 = strlen(asset->path);
419 //              *(asset->path + character2) = 0;
420                 if(path_textbox)
421                 {
422                         path_textbox->update(asset->path);
423                         path_textbox->set_selection(character1, character2, character2);
424                 }
425         }
426 }
427
428 void FormatTools::update(Asset *asset, int *file_per_label)
429 {
430         this->asset = asset;
431         this->file_per_label = file_per_label;
432         if( file_per_label ) labeled_files->update(file_per_label);
433         if( path_textbox ) path_textbox->update(asset->path);
434         format_text->update(File::formattostr(asset->format));
435         update_format();
436         close_format_windows();
437 }
438
439 void FormatTools::close_format_windows()
440 {
441 // This is done in ~file
442         if( aparams_thread ) {
443                 if( aparams_thread->running() )
444                         aparams_thread->file->close_window();
445                 aparams_thread->join();
446         }
447         if( vparams_thread ) {
448                 if( vparams_thread->running() )
449                         vparams_thread->file->close_window();
450                 vparams_thread->join();
451         }
452 }
453
454 int FormatTools::get_w()
455 {
456         return asset->format != FILE_FFMPEG ? w :
457                 format_ffmpeg->get_x() + format_ffmpeg->get_w();
458 }
459
460 void FormatTools::set_w(int w)
461 {
462         this->w = w;
463 }
464
465 void FormatTools::reposition_window(int &init_x, int &init_y)
466 {
467         int xs10 = xS(10), xs80 = xS(80);
468         int ys10 = yS(10);
469         int x = init_x;
470         int y = init_y;
471
472         if(path_textbox)
473         {
474                 int px = x;
475                 path_textbox->reposition_window(px, y);
476                 px += path_textbox->get_w() + xS(5);
477                 path_recent->reposition_window(px, y);
478                 px += path_recent->get_w() + xS(8);
479                 path_button->reposition_window(px, y);
480                 y += path_textbox->get_h() + ys10;
481         }
482
483         format_title->reposition_window(x, y);
484         x += xS(90);
485         format_text->reposition_window(x, y);
486         x += format_text->get_w();
487         format_button->reposition_window(x, y);
488         x += format_button->get_w() + 5;
489         ffmpeg_type->reposition_window(x, y);
490         x += ffmpeg_type->get_w();
491         format_ffmpeg->reposition_window(x, y);
492
493         x = init_x;
494         y += format_button->get_h() + ys10;
495
496         if(do_audio)
497         {
498                 audio_title->reposition_window(x, y);
499                 x += xs80;
500                 aparams_button->reposition_window(x, y);
501                 x += aparams_button->get_w() + xs10;
502                 if(prompt_audio) audio_switch->reposition_window(x, y);
503
504                 x = init_x;
505                 y += aparams_button->get_h() + ys10;
506         }
507
508
509         if(do_video)
510         {
511                 video_title->reposition_window(x, y);
512                 x += xs80;
513                 if(prompt_video_compression)
514                 {
515                         vparams_button->reposition_window(x, y);
516                         x += vparams_button->get_w() + xs10;
517                 }
518
519                 if(prompt_video)
520                 {
521                         video_switch->reposition_window(x, y);
522                         y += video_switch->get_h();
523                 }
524                 else
525                 {
526                         y += vparams_button->get_h();
527                 }
528
529                 y += ys10;
530                 x = init_x;
531         }
532
533         if( file_per_label ) {
534                 labeled_files->reposition_window(x, y);
535                 y += labeled_files->get_h() + ys10;
536         }
537
538         init_y = y;
539 }
540
541
542 int FormatTools::set_audio_options()
543 {
544 //      if(video_driver == CAPTURE_DVB)
545 //      {
546 //              return 0;
547 //      }
548
549         if(!aparams_thread->running())
550         {
551                 aparams_thread->start();
552         }
553         else
554         {
555                 aparams_thread->file->raise_window();
556         }
557         return 0;
558 }
559
560 int FormatTools::set_video_options()
561 {
562 //      if(video_driver == CAPTURE_DVB)
563 //      {
564 //              return 0;
565 //      }
566
567         if(!vparams_thread->running())
568         {
569                 vparams_thread->start();
570         }
571         else
572         {
573                 vparams_thread->file->raise_window();
574         }
575
576         return 0;
577 }
578
579
580
581
582
583 FormatAParams::FormatAParams(MWindow *mwindow, FormatTools *format, int x, int y)
584  : BC_Button(x, y, mwindow->theme->get_image_set("wrench"))
585 {
586         this->format = format;
587         set_tooltip(_("Configure audio compression"));
588 }
589
590 FormatAParams::~FormatAParams()
591 {
592 }
593
594 int FormatAParams::handle_event()
595 {
596         format->set_audio_options();
597         format->handle_event();
598         return 1;
599 }
600
601
602
603
604
605 FormatVParams::FormatVParams(MWindow *mwindow, FormatTools *format, int x, int y)
606  : BC_Button(x, y, mwindow->theme->get_image_set("wrench"))
607 {
608         this->format = format;
609         set_tooltip(_("Configure video compression"));
610 }
611
612 FormatVParams::~FormatVParams()
613 {
614 }
615
616 int FormatVParams::handle_event()
617 {
618         format->set_video_options();
619         format->handle_event();
620         return 1;
621 }
622
623
624
625
626
627 FormatAThread::FormatAThread(FormatTools *format)
628  : Thread(1, 0, 0)
629 {
630         this->format = format;
631         file = new File;
632         joined = 1;
633 }
634
635 FormatAThread::~FormatAThread()
636 {
637         delete file;  file = 0;
638         join();
639 }
640
641 void FormatAThread::start()
642 {
643         join();
644         joined = 0;
645         Thread::start();
646 }
647
648
649 void FormatAThread::run()
650 {
651         file->get_options(format, 1, 0);
652 }
653
654
655
656
657 FormatVThread::FormatVThread(FormatTools *format)
658  : Thread(1, 0, 0)
659 {
660         this->format = format;
661         file = new File;
662         joined = 1;
663 }
664
665 FormatVThread::~FormatVThread()
666 {
667         delete file;  file = 0;
668         join();
669 }
670
671 void FormatVThread::start()
672 {
673         join();
674         joined = 0;
675         Thread::start();
676 }
677
678 void FormatVThread::run()
679 {
680         file->get_options(format, 0, 1);
681 }
682
683
684
685
686
687 FormatPathText::FormatPathText(int x, int y, FormatTools *format)
688  : BC_TextBox(x, y, format->w - x -
689                 2*format->mwindow->theme->get_image_set("wrench")[0]->get_w() - xS(20), 1,
690         format->asset->path)
691 {
692         this->format = format;
693 }
694
695 FormatPathText::~FormatPathText()
696 {
697 }
698 int FormatPathText::handle_event()
699 {
700         calculate_suggestions();
701         strcpy(format->asset->path, get_text());
702         format->handle_event();
703         return 1;
704 }
705
706
707
708
709 FormatAudio::FormatAudio(int x, int y, FormatTools *format, int default_)
710  : BC_CheckBox(x,
711         y,
712         default_,
713         (char*)(format->recording ? _("Record audio tracks") : _("Render audio tracks")))
714 {
715         this->format = format;
716 }
717
718 FormatAudio::~FormatAudio() {}
719 int FormatAudio::handle_event()
720 {
721         format->asset->audio_data = get_value();
722         format->handle_event();
723         return 1;
724 }
725
726
727 FormatVideo::FormatVideo(int x, int y, FormatTools *format, int default_)
728  : BC_CheckBox(x,
729         y,
730         default_,
731         (char*)(format->recording ? _("Record video tracks") : _("Render video tracks")))
732 {
733 this->format = format;
734 }
735
736 FormatVideo::~FormatVideo() {}
737 int FormatVideo::handle_event()
738 {
739         format->asset->video_data = get_value();
740         format->handle_event();
741         return 1;
742 }
743
744
745
746
747 FormatFormat::FormatFormat(int x, int y, FormatTools *format)
748  : FormatPopup(x, y, format->do_audio, format->do_video, format->use_brender)
749 {
750         this->format = format;
751 }
752
753 FormatFormat::~FormatFormat()
754 {
755 }
756
757 int FormatFormat::handle_event()
758 {
759         BC_ListBoxItem *selection = get_selection(0, 0);
760         if( selection ) {
761                 int new_format = File::strtoformat(get_selection(0, 0)->get_text());
762 //              if(new_format != format->asset->format)
763                 {
764                         Asset *asset = format->asset;
765                         asset->format = new_format;
766                         asset->audio_data = File::renders_audio(asset);
767                         asset->video_data = File::renders_video(asset);
768                         asset->ff_audio_options[0] = 0;
769                         asset->ff_video_options[0] = 0;
770                         asset->ff_format_options[0] = 0;
771                         format->format_text->update(selection->get_text());
772                         if( !format->use_brender )
773                                 format->update_extension();
774                         format->close_format_windows();
775                         if (format->path_recent) format->path_recent->
776                                 load_items(File::formattostr(format->asset->format));
777                         format->update_format();
778                 }
779                 format->handle_event();
780         }
781         return 1;
782 }
783
784
785 FormatFFMPEG::FormatFFMPEG(int x, int y, FormatTools *format)
786  : FFMPEGPopup(x, y)
787 {
788         this->format = format;
789 }
790
791 FormatFFMPEG::~FormatFFMPEG()
792 {
793 }
794
795 int FormatFFMPEG::handle_event()
796 {
797         BC_ListBoxItem *selection = get_selection(0, 0);
798         if( selection ) {
799                 const char *text = get_selection(0, 0)->get_text();
800                 format->ffmpeg_type->update(text);
801 // forces options load defaults
802                 format->asset->ff_audio_options[0] = 0;
803                 format->asset->ff_video_options[0] = 0;
804                 format->asset->ff_format_options[0] = 0;
805                 FFMPEG::set_asset_format(format->asset, format->mwindow->edl, text);
806                 format->update_extension();
807                 format->close_format_windows();
808                 format->update_format();
809                 format->handle_event();
810         }
811         return 1;
812 }
813
814
815 FormatFilePerLabel::FormatFilePerLabel(FormatTools *format,
816         int x, int y, int *output)
817  : BC_CheckBox(x, y, *output, _("Create new file at each label"))
818 {
819         this->format = format;
820         this->output = output;
821 }
822
823 FormatFilePerLabel::~FormatFilePerLabel()
824 {
825 }
826
827 int FormatFilePerLabel::handle_event()
828 {
829         *output = get_value();
830         format->handle_event();
831         return 1;
832 }
833
834 void FormatFilePerLabel::update(int *output)
835 {
836         this->output = output;
837         set_value(*output ? 1 : 0);
838 }
839
840