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