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