add bluray dv, misc fixes
[goodguy/history.git] / cinelerra-5.1 / cinelerra / swindow.C
1
2 #include "bchash.h"
3 #include "bctimer.h"
4 #include "condition.h"
5 #include "cstrdup.h"
6 #include "edl.h"
7 #include "filesystem.h"
8 #include "language.h"
9 #include "localsession.h"
10 #include "mainerror.h"
11 #include "mainmenu.h"
12 #include "mainsession.h"
13 #include "mainundo.h"
14 #include "mwindow.h"
15 #include "mwindowgui.h"
16 #include "mutex.h"
17 #include "strack.h"
18 #include "swindow.h"
19 #include "theme.h"
20 #include "track.h"
21 #include "tracks.h"
22
23 #include<ctype.h>
24 #include<errno.h>
25 #include<stdint.h>
26 #include<stdlib.h>
27 #include<string.h>
28 #include<unistd.h>
29
30 SWindowOK::SWindowOK(SWindowGUI *gui, int x, int y)
31  : BC_OKButton(x, y)
32 {
33         this->gui = gui;
34 }
35
36 SWindowOK::~SWindowOK()
37 {
38 }
39
40 int SWindowOK::button_press_event()
41 {
42         if(get_buttonpress() == 1 && is_event_win() && cursor_inside()) {
43                 gui->stop(0);
44                 return 1;
45         }
46         return 0;
47 }
48
49 int SWindowOK::keypress_event()
50 {
51         return 0;
52 }
53
54
55 SWindowCancel::SWindowCancel(SWindowGUI *gui, int x, int y)
56  : BC_CancelButton(x, y)
57 {
58         this->gui = gui;
59 }
60
61 SWindowCancel::~SWindowCancel()
62 {
63 }
64
65 int SWindowCancel::button_press_event()
66 {
67         if(get_buttonpress() == 1 && is_event_win() && cursor_inside()) {
68                 gui->stop(1);
69                 return 1;
70         }
71         return 0;
72 }
73
74
75 SWindowLoadPath::SWindowLoadPath(SWindowGUI *gui, int x, int y, char *path)
76  : BC_TextBox(x, y, 200, 1, path)
77 {
78         this->sw_gui = gui;
79 }
80
81 SWindowLoadPath::~SWindowLoadPath()
82 {
83 }
84
85 int SWindowLoadPath::handle_event()
86 {
87         calculate_suggestions();
88         strcpy(sw_gui->script_path, get_text());
89         return 1;
90 }
91
92
93 SWindowLoadFile::SWindowLoadFile(SWindowGUI *gui, int x, int y)
94  : BC_GenericButton(x, y, _("Load"))
95 {
96         this->sw_gui = gui;
97 }
98
99 SWindowLoadFile::~SWindowLoadFile()
100 {
101 }
102
103 int SWindowLoadFile::handle_event()
104 {
105         if( sw_gui->script_path[0] ) {
106                 sw_gui->load_path->set_suggestions(0,0);
107                 sw_gui->load_script();
108                 sw_gui->set_script_pos(0);
109         }
110         else {
111                 eprintf(_("script text file path required"));
112         }
113         return 1;
114 }
115
116 SWindowSaveFile::SWindowSaveFile(SWindowGUI *gui, int x, int y)
117  : BC_GenericButton(x, y, _("Save"))
118 {
119         this->sw_gui = gui;
120 }
121
122 SWindowSaveFile::~SWindowSaveFile()
123 {
124 }
125
126 int SWindowSaveFile::handle_event()
127 {
128         if( sw_gui->script_path[0] ) {
129                 sw_gui->save_spumux_data();
130         }
131         else {
132                 eprintf(_("script microdvd file path required"));
133         }
134         return 1;
135 }
136
137
138
139
140 void SWindowGUI::create_objects()
141 {
142         int x = 10, y = 10;
143         BC_Title *title = new BC_Title(x, y, _("Path:"));
144         add_subwindow(title);
145         int x1 = x + title->get_w() + pad, y1 = y;
146         load_path = new SWindowLoadPath(this, x1, y1, script_path);
147         add_subwindow(load_path);
148         x1 += load_path->get_w() + 2*pad;
149         add_subwindow(load_file = new SWindowLoadFile(this, x1, y1));
150         x1 += load_file->get_w() + 2*pad;
151         add_subwindow(save_file = new SWindowSaveFile(this, x1, y1));
152         y += max(load_path->get_h(), load_file->get_h()) + pad;
153         x1 = x + pad, y1 = y;
154         BC_Title *title1, *title2;
155         add_subwindow(title1 = new BC_Title(x1, y1, _("File Size:")));
156         y += title1->get_h() + pad;
157         int y2 = y;
158         add_subwindow(title2 = new BC_Title(x1, y2, _("Entries:")));
159         int x2 = x1 + max(title1->get_w(), title2->get_w()) + pad;
160         add_subwindow(script_filesz = new BC_Title(x2, y1, "0", MEDIUMFONT, YELLOW));
161         add_subwindow(script_entries = new BC_Title(x2, y2, "0", MEDIUMFONT, YELLOW));
162         int x3 = x2 + max(script_entries->get_w()*8, script_filesz->get_w()*8) + pad;
163         add_subwindow(title1 = new BC_Title(x3, y1, _("Lines:")));
164         add_subwindow(title2 = new BC_Title(x3, y2, _("Texts:")));
165         int x4 = x3 + max(title1->get_w(), title2->get_w()) + pad;
166         add_subwindow(script_lines = new BC_Title(x4, y1, "0", MEDIUMFONT, YELLOW));
167         add_subwindow(script_texts = new BC_Title(x4, y2, "0", MEDIUMFONT, YELLOW));
168         int x5 = x4 + max(script_lines->get_w()*8, script_texts->get_w()*8) + 2*pad;
169         add_subwindow(prev_script = new ScriptPrev(this, x5, y1));
170         add_subwindow(next_script = new ScriptNext(this, x5, y2));
171         int x6 = x5 + max(prev_script->get_w(), next_script->get_w()) + 2*pad;
172         add_subwindow(paste_script = new ScriptPaste(this, x6, y1));
173         add_subwindow(clear_script = new ScriptClear(this, x6, y2));
174         y += max(title1->get_h(), title2->get_h()) + 2*pad;
175
176         script_position = new ScriptPosition(this, x, y, 100);
177         script_position->create_objects();
178         x1 = x + script_position->get_w() + pad;
179         add_subwindow(script_scroll = new ScriptScroll(this, x1, y, get_w()-x1-pad));
180         y += script_scroll->get_h() + 2*pad;
181         x1 = x + pad;
182         blank_line = new char[2];
183         blank_line[0] = ' ';  blank_line[1] = 0;
184         add_subwindow(script_title = new BC_Title(x1, y, _("Script Text:")));
185         y += script_title->get_h() + pad;
186         int rows = (ok_y - y - BC_Title::calculate_h(this,_("Line Text:")) -
187                 4*pad) / text_rowsz - 3;
188         int w1 = get_w() - x1 - pad;
189         script_entry = new ScriptEntry(this, x1, y, w1, rows, blank_line);
190         script_entry->create_objects();
191         y += script_entry->get_h() + pad;
192         add_subwindow(line_title = new BC_Title(x1, y, _("Line Text:")));
193         y += line_title->get_h() + pad;
194         line_entry = new ScriptEntry(this, x1, y, w1, 3);
195         line_entry->create_objects();
196         ok = new SWindowOK(this, ok_x, ok_y);
197         add_subwindow(ok);
198         cancel = new SWindowCancel(this, cancel_x, cancel_y);
199         add_subwindow(cancel);
200 }
201
202 void SWindowGUI::load()
203 {
204         const char *script_text =
205         _("Adding Subtitles: quick \"How To\" (= or * indicates comment)\n"
206         "*2345678901234567890123456789012345678901234567890123456789\n"
207         "For regular DVD subtitles, put script in a text file. "
208         "Lines can be any length but they will be broken up "
209         "to fit according to some criteria below.\n"
210         "Running text used as script lines will be broken into multilple lines.\n"
211         "The target line length is 60 characters.\n"
212         "Punctuation may be flagged to create an early line break.\n"
213         "Single carriage return ends an individual script line.\n"
214         "Double carriage return indicates the end of an entry.\n"
215         "Whitespace at beginning or end of line is removed.\n"
216         "You can edit the active line in the Line Text box.\n"
217         "\n"
218         "== A new entry is here for illustration purposes.\n"
219         "*  Entry 2\n"
220         "This is the second entry.\n");
221
222         if( script_path[0] && !access(script_path,R_OK) ) {
223                 load_script();
224         }
225         else {
226                 script_path[0] = 0;
227                 load_path->update(script_path);
228                 FILE *fp = fmemopen((void *)script_text, strlen(script_text), "r");
229                 load_script(fp);
230         }
231         int text_no = script_text_no;
232         script_text_no = -1;
233         load_selection(script_entry_no, text_no);
234 }
235
236 SWindowGUI::SWindowGUI(SWindow *swindow, int x, int y, int w, int h)
237  : BC_Window(_(PROGRAM_NAME ": Subtitle"), x, y, w, h, 600, 500,
238         1, 0, 0 , -1, swindow->mwindow->get_cwindow_display())
239 {
240         this->swindow = swindow;
241         pad = 8;
242
243         ok = 0;
244         ok_w = BC_OKButton::calculate_w();
245         ok_h = BC_OKButton::calculate_h();
246         ok_x = 10;
247         ok_y = h - ok_h - 10;
248         cancel = 0;
249         cancel_w = BC_CancelButton::calculate_w();
250         cancel_h = BC_CancelButton::calculate_h();
251         cancel_x = w - cancel_w - 10,
252         cancel_y = h - cancel_h - 10;
253
254         load_path = 0;
255         load_file = 0;
256         save_file = 0;
257         script_filesz = 0;
258         script_lines = 0;
259         script_entries = 0;
260         script_texts = 0;
261         script_entry_no = 0;
262         script_text_no = 0;
263         script_line_no = 0;
264         script_text_lines = 0;
265         prev_script = 0;
266         next_script = 0;
267         paste_script = 0;
268         clear_script = 0;
269         script_position = 0;
270         script_entry = 0;
271         line_entry = 0;
272         script_scroll = 0;
273         blank_line = 0;
274         text_font = MEDIUMFONT;
275         text_rowsz = get_text_ascent(text_font)+1 + get_text_descent(text_font)+1;
276 }
277
278 SWindowGUI::~SWindowGUI()
279 {
280         delete script_entry;
281         delete line_entry;
282         delete script_position;
283         delete [] blank_line;
284 }
285
286 void SWindowGUI::stop(int v)
287 {
288         if( !swindow->gui_done ) {
289                 swindow->gui_done = 1;
290                 set_done(v);
291         }
292 }
293
294 int SWindowGUI::translation_event()
295 {
296         swindow->mwindow->session->swindow_x = get_x();
297         swindow->mwindow->session->swindow_y = get_y();
298         return 0;
299 }
300
301 int SWindowGUI::resize_event(int w, int h)
302 {
303         swindow->mwindow->session->swindow_w = w;
304         swindow->mwindow->session->swindow_h = h;
305
306         ok_x = 10;
307         ok_y = h - ok_h - 10;
308         ok->reposition_window(ok_x, ok_y);
309         cancel_x = w - cancel_w - 10,
310         cancel_y = h - cancel_h - 10;
311         cancel->reposition_window(cancel_x, cancel_y);
312
313         int x = script_position->get_x();
314         int y = script_position->get_y();
315         int hh = script_position->get_h();
316         int ww = script_position->get_w();
317         int x1 = x + ww + pad;
318         int w1 = w - x1 - pad;
319         script_scroll->reposition_window(x1, y, w1);
320         y += hh + 2*pad;
321         script_title->reposition_window(x, y);
322         y += script_title->get_h() + pad;
323         w1 = w - x - pad;
324         int rows = (ok_y - y - line_title->get_h() - 4*pad) / text_rowsz - 3;
325         script_entry->reposition_window(x, y, w1, rows);
326         y += script_entry->get_h() + 2*pad;
327         line_title->reposition_window(x, y);
328         y += line_title->get_h() + pad;
329         line_entry->reposition_window(x, y, w1, 3);
330         return 0;
331 }
332
333 void SWindowGUI::load_defaults()
334 {
335         BC_Hash *defaults = swindow->mwindow->defaults;
336         defaults->get("SUBTTL_SCRIPT_PATH", script_path);
337         script_entry_no = defaults->get("SUBTTL_SCRIPT_ENTRY_NO", script_entry_no);
338         script_text_no = defaults->get("SUBTTL_SCRIPT_TEXT_NO", script_text_no);
339 }
340
341 void SWindowGUI::save_defaults()
342 {
343         BC_Hash *defaults = swindow->mwindow->defaults;
344         defaults->update("SUBTTL_SCRIPT_PATH", script_path);
345         defaults->update("SUBTTL_SCRIPT_ENTRY_NO", script_entry_no);
346         defaults->update("SUBTTL_SCRIPT_TEXT_NO", script_text_no);
347 }
348
349 void SWindowGUI::set_script_pos(int64_t entry_no, int text_no)
350 {
351         script_entry_no = entry_no<0 ? 0 :
352                 entry_no>script.size()-1 ? script.size()-1 : entry_no;
353         script_text_no = text_no-1;
354         load_next_selection();
355 }
356
357 int SWindowGUI::load_selection(int pos, int row)
358 {
359         ScriptLines *texts = script[pos];
360         char *rp = texts->get_text_row(row);
361         if( !rp || *rp == '=' || *rp == '*' || *rp=='\n' ) return 1;
362         if( pos != script_entry_no || script_text_no < 0 ) {
363                 script_entry_no = pos;
364                 script_scroll->update_value(script_entry_no);
365                 script_position->update(script_entry_no);
366                 script_entry->set_text(texts->text);
367                 script_entry->set_text_row(0);
368         }
369         script_text_no = row;
370         char line[BCTEXTLEN], *bp = line;
371         char *ep = bp+sizeof(line)-1, *cp = rp;
372         while( bp < ep && *cp && *cp!='\n' ) *bp++ = *cp++;
373         *bp = 0;  bp = texts->text;
374         int char1 = rp-bp, char2 = cp-bp;
375         script_entry->set_selection(char1, char2, char2);
376         int rows = script_entry->get_rows();
377         int rows2 = rows / 2;
378         int rows1 = texts->lines+1 - rows;
379         if( (row-=rows2) > rows1 ) row = rows1;
380         if( row < 0 ) row = 0;
381         script_entry->set_text_row(row);
382         line_entry->update(line);
383         line_entry->set_text_row(0);
384         return 0;
385 }
386
387 int SWindowGUI::load_prev_selection()
388 {
389         int pos = script_entry_no, row = script_text_no;
390         if( pos < 0 || pos >= script.size() ) return 1;
391         for(;;) {
392                 if( --row < 0 ) {
393                         if( --pos < 0 ) break;
394                         row = script[pos]->lines;
395                         continue;
396                 }
397                 if( !load_selection(pos, row) )
398                         return 0;
399         }
400         return 1;
401 }
402
403 int SWindowGUI::load_next_selection()
404 {
405         int pos = script_entry_no, row = script_text_no;
406         if( pos < 0 || pos >= script.size() ) return 1;
407         for(;;) {
408                 if( ++row >= script[pos]->lines ) {
409                         if( ++pos >= script.size() ) break;
410                         row = -1;
411                         continue;
412                 }
413                 if( !load_selection(pos, row) )
414                         return 0;
415         }
416         return 1;
417 }
418
419 int SWindowGUI::update_selection()
420 {
421         EDL *edl = swindow->mwindow->edl;
422         LocalSession *lsn = edl->local_session;
423         double position = lsn->get_selectionstart();
424         Edit *edit = 0;
425         Tracks *tracks = edl->tracks;
426         for( Track *track=tracks->first; track && !edit; track=track->next ) {
427                 if( !track->record ) continue;
428                 if( track->data_type != TRACK_SUBTITLE ) continue;
429                 int64_t pos = track->to_units(position,0);
430                 edit = track->edits->editof(pos, PLAY_FORWARD, 0);
431         }
432         if( !edit ) return 1;
433         SEdit *sedit = (SEdit *)edit;
434         line_entry->update(sedit->get_text());
435         line_entry->set_text_row(0);
436         return 0;
437 }
438
439 int MWindow::paste_subtitle_text(char *text, double start, double end)
440 {
441         gui->lock_window("MWindow::paste_subtitle_text 1");
442         undo->update_undo_before();
443
444         Tracks *tracks = edl->tracks;
445         for( Track *track=tracks->first; track; track=track->next ) {
446                 if( track->data_type != TRACK_SUBTITLE ) continue;
447                 if( !track->record ) continue;
448                 int64_t start_i = track->to_units(start, 0);
449                 int64_t end_i = track->to_units(end, 1);
450                 track->edits->clear(start_i,end_i);
451                 SEdit *sedit = (SEdit *)track->edits->create_silence(start_i,end_i);
452                 strcpy(sedit->get_text(),text);
453                 track->edits->optimize();
454         }
455
456         save_backup();
457         undo->update_undo_after(_("paste subttl"), LOAD_EDITS | LOAD_PATCHES);
458
459         sync_parameters(CHANGE_EDL);
460         restart_brender();
461         gui->update(0, 1, 1, 0, 0, 0, 0);
462         gui->unlock_window();
463
464         return 0;
465 }
466
467 int SWindowGUI::paste_text(const char *text, double start, double end)
468 {
469         char stext[BCTEXTLEN], *cp = stext;
470         if( text ) { // remap charset, reformat if needed
471                 for( const char *bp=text; *bp!=0; ++bp,++cp ) {
472                         switch( *bp ) {
473                         case '\n':  *cp = '|';  break;
474                         default:    *cp = *bp;  break;
475                         }
476                 }
477         }
478         if( cp > stext && *(cp-1) == '|' ) --cp;
479         *cp = 0;
480         return swindow->mwindow->paste_subtitle_text(stext, start, end);
481 }
482
483 int SWindowGUI::paste_selection()
484 {
485         EDL *edl = swindow->mwindow->edl;
486         LocalSession *lsn = edl->local_session;
487         double start = lsn->get_selectionstart();
488         double end = lsn->get_selectionend();
489         if( start >= end ) return 1;
490         if( lsn->inpoint_valid() && lsn->outpoint_valid() ) {
491                 lsn->set_inpoint(lsn->get_outpoint());
492                 lsn->unset_outpoint();
493         }
494         paste_text(line_entry->get_text(), start, end);
495         load_next_selection();
496         return 0;
497 }
498
499 int SWindowGUI::clear_selection()
500 {
501         EDL *edl = swindow->mwindow->edl;
502         double start = edl->local_session->get_selectionstart();
503         double end = edl->local_session->get_selectionend();
504         if( end > start )
505                 paste_text(0, start, end);
506         return 0;
507 }
508
509
510 ScriptPrev::ScriptPrev(SWindowGUI *gui, int x, int y)
511  : BC_GenericButton(x, y, _("Prev"))
512 {
513         sw_gui = gui;
514 }
515
516 ScriptPrev::~ScriptPrev()
517 {
518 }
519
520 int ScriptPrev::handle_event()
521 {
522         sw_gui->load_prev_selection();
523         return 1;
524 }
525
526 ScriptNext::ScriptNext(SWindowGUI *gui, int x, int y)
527  : BC_GenericButton(x, y, _("Next"))
528 {
529         sw_gui = gui;
530 }
531
532 ScriptNext::~ScriptNext()
533 {
534 }
535
536 int ScriptNext::handle_event()
537 {
538         sw_gui->load_next_selection();
539         return 1;
540 }
541
542 ScriptPaste::ScriptPaste(SWindowGUI *gui, int x, int y)
543  : BC_GenericButton(x, y, _("Paste"))
544 {
545         sw_gui = gui;
546 }
547
548 ScriptPaste::~ScriptPaste()
549 {
550 }
551
552 int ScriptPaste::handle_event()
553 {
554         sw_gui->paste_selection();
555         return 1;
556 }
557
558 ScriptClear::ScriptClear(SWindowGUI *gui, int x, int y)
559  : BC_GenericButton(x, y, _("Clear"))
560 {
561         sw_gui = gui;
562 }
563
564 ScriptClear::~ScriptClear()
565 {
566 }
567
568 int ScriptClear::handle_event()
569 {
570         sw_gui->clear_selection();
571         return 1;
572 }
573
574
575 ScriptLines::ScriptLines()
576 {
577         used = 0;
578         lines = 0;
579         text = new char[allocated = 256];
580         *text = 0;
581 }
582
583 ScriptLines::~ScriptLines()
584 {
585         delete [] text;
586 }
587
588 void ScriptLines::append(char *cp)
589 {
590         int len = strlen(cp);
591         if( allocated-used < len+1 ) {
592                 int isz = allocated + used + len;
593                 char *new_text = new char[isz];
594                 allocated = isz;
595                 memcpy(new_text, text, used);
596                 delete [] text;  text = new_text;
597         }
598         memcpy(text+used, cp, len);
599         text[used += len] = 0;
600         ++lines;
601 }
602
603 int ScriptLines::break_lines()
604 {
605         int line_limit = 60;
606         int limit2 = line_limit/2;
607         int limit4 = line_limit/4-2;
608         char *cp = text, *dp = cp+used;
609         int n;  char *bp, *ep, *pp, *sp;
610         for( lines=0; cp<dp; ++lines ) {
611                 // find end of line/buffer
612                 for( ep=cp; ep<dp && *ep!='\n'; ++ep );
613                 // delete trailing spaces
614                 for( sp=ep; sp>=cp && (!*sp || isspace(*sp)); --sp);
615                 ++sp;
616                 if( (n=ep-sp) > 0 ) {
617                         memmove(sp,ep,dp+1-ep);
618                         used -= n;  dp -= n;  ep -= n;
619                 }
620                 ++ep;
621                 // skip, if comment or title line
622                 if( *cp == '*' || *cp == '=' ) {
623                         cp = ep;  continue;
624                 }
625                 // delete leading spaces
626                 for( sp=cp; sp<ep && isspace(*sp); ++sp);
627                 if( (n=sp-cp) > 0 ) {
628                         memmove(cp,sp,dp+1-sp);
629                         used -= n;  dp -= n;  ep -= n;
630                 }
631                 // target about half remaining line, constrain line_limit
632                 if( (n=(ep-1-cp)/2) < limit2 || n > line_limit )
633                         n = line_limit;
634                 // search for last punct, last space before line_limit
635                 for( bp=cp, pp=sp=0; --n>=0 && cp<ep; ++cp ) {
636                         if( ispunct(*cp) && isspace(*(cp+1)) ) pp = cp;
637                         else if( isspace(*cp) ) sp = cp;
638                 }
639                 // line not empty
640                 if( cp < ep ) {
641                         // first, after punctuation
642                         if( pp && pp-bp >= limit4 )
643                                 cp = pp+1;
644                         // then, on spaces
645                         else if( sp ) {
646                                 cp = sp;
647                         }
648                         // last, on next space
649                         else {
650                                 while( cp<dp && !isspace(*cp) ) ++cp;
651                         }
652                         // add new line
653                         if( !*cp ) break;
654                         *cp++ = '\n';
655                 }
656         }
657         return lines;
658 }
659
660 int ScriptLines::get_text_rows()
661 {
662         int ret = 1, i=used;
663         for( char *cp=text; --i>=0; ++cp )
664                 if( *cp == '\n' ) ++ret;
665         return ret;
666 }
667
668 char *ScriptLines::get_text_row(int n)
669 {
670         char *cp = text;
671         if( !n ) return cp;
672         for( int i=used; --i>=0; ) {
673                 if( *cp++ != '\n' ) continue;
674                 if( --n <= 0 ) return cp;
675         }
676         return 0;
677 }
678
679 ScriptScroll::ScriptScroll(SWindowGUI *gui, int x, int y, int w)
680  : BC_ScrollBar(x, y, SCROLL_HORIZ, w, 0, 0, 0)
681 {
682         this->sw_gui = gui;
683 }
684
685 ScriptScroll::~ScriptScroll()
686 {
687 }
688
689 int ScriptScroll::handle_event()
690 {
691         int64_t pos = get_value();
692         sw_gui->set_script_pos(pos);
693         sw_gui->script_position->update(pos);
694         return 1;
695 }
696
697
698 ScriptPosition::ScriptPosition(SWindowGUI *gui, int x, int y, int w, int v, int mn, int mx)
699  : BC_TumbleTextBox(gui, v, mn, mx, x, y, w-BC_Tumbler::calculate_w())
700 {
701         this->sw_gui = gui;
702 }
703
704 ScriptPosition::~ScriptPosition()
705 {
706 }
707
708 int ScriptPosition::handle_event()
709 {
710         int64_t pos = atol(get_text());
711         sw_gui->set_script_pos(pos);
712         sw_gui->script_scroll->update_value(pos);
713         return 1;
714 }
715
716
717 ScriptEntry::ScriptEntry(SWindowGUI *gui, int x, int y, int w, int rows, char *text)
718  : BC_ScrollTextBox(gui, x, y, w, rows, text, -strlen(text))
719 {
720         this->sw_gui = gui;
721         this->ttext = text;
722 }
723
724 ScriptEntry::ScriptEntry(SWindowGUI *gui, int x, int y, int w, int rows)
725  : BC_ScrollTextBox(gui, x, y, w, rows,(char*)0, BCTEXTLEN)
726 {
727         this->sw_gui = gui;
728         this->ttext = 0;
729 }
730
731 ScriptEntry::~ScriptEntry()
732 {
733 }
734
735 void ScriptEntry::set_text(char *text, int isz)
736 {
737         if( !text || !*text ) return;
738         if( isz < 0 ) isz = strlen(text)+1;
739         BC_ScrollTextBox::set_text(text, isz);
740         ttext = text;
741 }
742
743 int ScriptEntry::handle_event()
744 {
745         if( sw_gui->get_button_down() &&
746             sw_gui->get_buttonpress() == 1 &&
747             sw_gui->get_triple_click() ) {
748                 int ibeam = get_ibeam_letter(), row = 0;
749                 const char *tp = ttext;
750                 while( *tp && tp-ttext < ibeam ) {
751                         if( *tp++ == '\n' ) ++row;
752                 }
753                 int pos = sw_gui->script_entry_no;
754                 sw_gui->load_selection(pos, row);
755         }
756         return 1;
757 }
758
759 int SWindowGUI::load_script_line(FILE *fp)
760 {
761         char line[8192];
762         for(;;) { // skip blank lines
763                 char *cp = fgets(line,sizeof(line),fp);
764                 if( !cp ) return 1;
765                 ++script_line_no;
766                 while( *cp && isspace(*cp) ) ++cp;
767                 if( *cp ) break;
768         }
769
770         ScriptLines *entry = new ScriptLines();
771         script.append(entry);
772
773         for(;;) { // load non-blank lines
774                 //int len = strlen(line);
775                 //if( line[len-1] == '\n' ) line[len-1] = ' ';
776                 entry->append(line);
777                 char *cp = fgets(line,sizeof(line),fp);
778                 if( !cp ) break;
779                 ++script_line_no;
780                 while( *cp && isspace(*cp) ) ++cp;
781                 if( !*cp ) break;
782         }
783         script_text_lines += entry->break_lines();
784         return 0;
785 }
786
787 void SWindowGUI::load_script()
788 {
789         FILE *fp = fopen(script_path,"r");
790         if( !fp ) {
791                 char string[BCTEXTLEN];
792                 sprintf(string,_("cannot open: \"%s\"\n%s"),script_path,strerror(errno));
793                 MainError::show_error(string);
794                 return;
795         }
796         load_script(fp);
797 }
798
799 void SWindowGUI::load_script(FILE *fp)
800 {
801         script.remove_all_objects();
802         script_line_no = 0;
803         script_text_lines = 0;
804         while( !load_script_line(fp) );
805         char value[64];
806         sprintf(value,"%ld",ftell(fp));
807         script_filesz->update(value);
808         sprintf(value,"%jd",script_line_no);
809         script_lines->update(value);
810         sprintf(value,"%d",script.size());
811         script_entries->update(value);
812         sprintf(value,"%jd",script_text_lines);
813         script_texts->update(value);
814         int hw = 2*script_scroll->get_h();
815         script_scroll->update_length(script.size(), script_entry_no, hw, 0);
816         script_position->update(script_entry_no);
817         script_position->set_boundaries((int64_t)0, (int64_t)script.size()-1);
818
819         fclose(fp);
820 }
821
822 void SWindowGUI::save_spumux_data()
823 {
824         char filename[BCTEXTLEN], track_title[BCTEXTLEN];
825         snprintf(filename,sizeof(filename),"%s",script_path);
826         filename[sizeof(filename)-1] = 0;
827         char *bp = strrchr(filename,'/');
828         if( !bp ) bp = filename; else ++bp;
829         char *ext = strrchr(bp, '.');
830         if( !ext ) ext = bp + strlen(bp);
831         int len = sizeof(filename)-1 - (ext-filename);
832
833         Tracks *tracks = swindow->mwindow->edl->tracks;
834         for( Track *track=tracks->first; track; track=track->next ) {
835                 if( track->data_type != TRACK_SUBTITLE ) continue;
836                 if( !track->record ) continue;
837                 char *cp = track_title;
838                 for( char *bp=track->title; *bp; ++bp,++cp )
839                         *cp = !isalnum(*bp) ? '_' : *bp;
840                 *cp = 0;
841                 snprintf(ext,len,"-%s.udvd",track_title);
842                 FILE *fp = fopen(filename, "w");
843                 if( !fp ) {
844                         eprintf(_("Unable to open %s:\n%m"), filename);
845                         continue;
846                 }
847                 int64_t start = 0;
848                 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
849                         SEdit *sedit = (SEdit *)edit;
850                         if( sedit->length > 0 ) {
851                                 int64_t end = start + sedit->length;
852                                 char *text = sedit->get_text();
853                                 if( *text ) {
854                                         fprintf(fp, "{%jd}{%jd}%s\n", start, end-1, text);
855                                 }
856                                 start = end;
857                         }
858                 }
859                 fclose(fp);
860         }
861 }
862
863
864
865 SWindow::SWindow(MWindow *mwindow)
866  : Thread(1, 0, 0)
867 {
868         this->mwindow = mwindow;
869         window_lock = new Mutex("SWindow::window_lock");
870         swin_lock = new Condition(0,"SWindow::swin_lock");
871         gui = 0;
872         done = 1;
873         gui_done = 1;
874
875         start();
876 }
877
878 SWindow::~SWindow()
879 {
880         stop();
881         delete gui;
882         delete swin_lock;
883         delete window_lock;
884 }
885
886
887 void SWindow::start()
888 {
889         if( !Thread::running() ) {
890                 done = 0;
891                 Thread::start();
892         }
893 }
894
895 void SWindow::stop()
896 {
897         if( Thread::running() ) {
898                 done = 1;
899                 swin_lock->unlock();
900                 window_lock->lock("SWindow::stop");
901                 if( gui ) gui->stop(1);
902                 window_lock->unlock();
903                 Thread::cancel();
904         }
905         Thread::join();
906 }
907
908 void SWindow::run()
909 {
910         int root_w = mwindow->gui->get_root_w(1);
911         int root_h = mwindow->gui->get_root_h(1);
912
913         while( !done ) {
914                 swin_lock->reset();
915                 swin_lock->lock();
916                 if( done ) break;
917                 int x = mwindow->session->swindow_x;
918                 int y = mwindow->session->swindow_y;
919                 int w = mwindow->session->swindow_w;
920                 int h = mwindow->session->swindow_h;
921                 if( w < 600 ) w = 600;
922                 if( h < 500 ) h = 500;
923                 int scr_x = mwindow->gui->get_screen_x(1, -1);
924                 int scr_w = mwindow->gui->get_screen_w(1, -1);
925                 if( x < scr_x ) x = scr_x;
926                 if( x > scr_x+scr_w ) x = scr_x+scr_w;
927                 if( x+w > root_w ) x = root_w - w;
928                 if( x < 0 ) { x = 0;  w = scr_w; }
929                 if( y+h > root_h ) y = root_h - h;
930                 if( y < 0 ) { y = 0;  h = root_h; }
931                 if( y+h > root_h ) h = root_h-y;
932                 mwindow->session->swindow_x = x;
933                 mwindow->session->swindow_y = y;
934                 mwindow->session->swindow_w = w;
935                 mwindow->session->swindow_h = h;
936
937                 gui_done = 0;
938                 gui = new SWindowGUI(this, x, y, w, h);
939                 gui->lock_window("ChannelInfo::gui_create_objects");
940                 gui->load_defaults();
941                 gui->create_objects();
942                 gui->set_icon(mwindow->theme->get_image("record_icon"));
943                 gui->reposition_window(x, y);
944                 gui->resize_event(w, h);
945                 gui->load();
946                 gui->show_window();
947                 gui->unlock_window();
948
949                 int result = gui->run_window();
950                 if( !result ) {
951                         gui->save_spumux_data();
952                 }
953
954                 window_lock->lock("ChannelInfo::run 1");
955                 gui->save_defaults();
956                 delete gui;  gui = 0;
957                 window_lock->unlock();
958         }
959 }
960
961
962
963
964 void SWindow::run_swin()
965 {
966         window_lock->lock("SWindow::run_swin");
967         if( gui ) {
968                 gui->lock_window("SWindow::run_swin");
969                 gui->raise_window();
970                 gui->unlock_window();
971         }
972         else
973                 swin_lock->unlock();
974         window_lock->unlock();
975 }
976
977 void SWindow::paste_subttl()
978 {
979         window_lock->lock("SWindow::paste_subttl 1");
980         if( gui ) {
981                 gui->lock_window("SWindow::paste_subttl 2");
982                 gui->paste_selection();
983                 gui->unlock_window();
984         }
985         window_lock->unlock();
986 }
987
988 int SWindow::update_selection()
989 {
990         window_lock->lock("SWindow::update_selection 1");
991         if( gui ) {
992                 gui->lock_window("SWindow::update_selection 2");
993                 gui->update_selection();
994                 gui->unlock_window();
995         }
996         window_lock->unlock();
997         return 0;
998 }
999
1000
1001
1002 SubttlSWin::SubttlSWin(MWindow *mwindow)
1003  : BC_MenuItem(_("SubTitle..."), _("Alt-y"), 'y')
1004 {
1005         set_alt();
1006         this->mwindow = mwindow;
1007 }
1008
1009 SubttlSWin::~SubttlSWin()
1010 {
1011 }
1012
1013 int SubttlSWin::handle_event()
1014 {
1015         mwindow->gui->swindow->run_swin();
1016         return 1;
1017 };
1018