5764aeae49875e09f8ad924cb86a2fbc951cd72a
[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 - 4;
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, 4);
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 - 4;
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, 4);
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         if( pos < 0 || pos >= script.size() ) return 1;
360         ScriptLines *texts = script[pos];
361         char *rp = texts->get_text_row(row);
362         if( !rp || *rp == '=' || *rp == '*' || *rp=='\n' ) return 1;
363         if( pos != script_entry_no || script_text_no < 0 ) {
364                 script_entry_no = pos;
365                 script_scroll->update_value(script_entry_no);
366                 script_position->update(script_entry_no);
367                 script_entry->set_text(texts->text);
368                 script_entry->set_text_row(0);
369         }
370         script_text_no = row;
371         char line[BCTEXTLEN], *bp = line;
372         char *ep = bp+sizeof(line)-1, *cp = rp;
373         while( bp < ep && *cp && *cp!='\n' ) *bp++ = *cp++;
374         *bp = 0;  bp = texts->text;
375         int char1 = rp-bp, char2 = cp-bp;
376         script_entry->set_selection(char1, char2, char2);
377         int rows = script_entry->get_rows();
378         int rows2 = rows / 2;
379         int rows1 = texts->lines+1 - rows;
380         if( (row-=rows2) > rows1 ) row = rows1;
381         if( row < 0 ) row = 0;
382         script_entry->set_text_row(row);
383         line_entry->update(line);
384         line_entry->set_text_row(0);
385         return 0;
386 }
387
388 int SWindowGUI::load_prev_selection()
389 {
390         int pos = script_entry_no, row = script_text_no;
391         if( pos < 0 || pos >= script.size() ) return 1;
392         for(;;) {
393                 if( --row < 0 ) {
394                         if( --pos < 0 ) break;
395                         row = script[pos]->lines;
396                         continue;
397                 }
398                 if( !load_selection(pos, row) )
399                         return 0;
400         }
401         return 1;
402 }
403
404 int SWindowGUI::load_next_selection()
405 {
406         int pos = script_entry_no, row = script_text_no;
407         if( pos < 0 || pos >= script.size() ) return 1;
408         for(;;) {
409                 if( ++row >= script[pos]->lines ) {
410                         if( ++pos >= script.size() ) break;
411                         row = -1;
412                         continue;
413                 }
414                 if( !load_selection(pos, row) )
415                         return 0;
416         }
417         return 1;
418 }
419
420 int SWindowGUI::update_selection()
421 {
422         EDL *edl = swindow->mwindow->edl;
423         LocalSession *lsn = edl->local_session;
424         double position = lsn->get_selectionstart();
425         Edit *edit = 0;
426         Tracks *tracks = edl->tracks;
427         for( Track *track=tracks->first; track && !edit; track=track->next ) {
428                 if( !track->record ) continue;
429                 if( track->data_type != TRACK_SUBTITLE ) continue;
430                 int64_t pos = track->to_units(position,0);
431                 edit = track->edits->editof(pos, PLAY_FORWARD, 0);
432         }
433         if( !edit ) return 1;
434         SEdit *sedit = (SEdit *)edit;
435         line_entry->update(sedit->get_text());
436         line_entry->set_text_row(0);
437         return 0;
438 }
439
440 int MWindow::paste_subtitle_text(char *text, double start, double end)
441 {
442         gui->lock_window("MWindow::paste_subtitle_text 1");
443         undo->update_undo_before();
444
445         Tracks *tracks = edl->tracks;
446         for( Track *track=tracks->first; track; track=track->next ) {
447                 if( track->data_type != TRACK_SUBTITLE ) continue;
448                 if( !track->record ) continue;
449                 int64_t start_i = track->to_units(start, 0);
450                 int64_t end_i = track->to_units(end, 1);
451                 track->edits->clear(start_i,end_i);
452                 SEdit *sedit = (SEdit *)track->edits->create_silence(start_i,end_i);
453                 strcpy(sedit->get_text(),text);
454                 track->edits->optimize();
455         }
456
457         save_backup();
458         undo->update_undo_after(_("paste subttl"), LOAD_EDITS | LOAD_PATCHES);
459
460         sync_parameters(CHANGE_EDL);
461         restart_brender();
462         gui->update(0, 1, 1, 0, 0, 0, 0);
463         gui->unlock_window();
464
465         return 0;
466 }
467
468 int SWindowGUI::paste_text(const char *text, double start, double end)
469 {
470         char stext[BCTEXTLEN], *cp = stext;
471         if( text ) { // remap charset, reformat if needed
472                 for( const char *bp=text; *bp!=0; ++bp,++cp ) {
473                         switch( *bp ) {
474                         case '\n':  *cp = '|';  break;
475                         default:    *cp = *bp;  break;
476                         }
477                 }
478         }
479         if( cp > stext && *(cp-1) == '|' ) --cp;
480         *cp = 0;
481         return swindow->mwindow->paste_subtitle_text(stext, start, end);
482 }
483
484 int SWindowGUI::paste_selection()
485 {
486         EDL *edl = swindow->mwindow->edl;
487         LocalSession *lsn = edl->local_session;
488         double start = lsn->get_selectionstart();
489         double end = lsn->get_selectionend();
490         if( start >= end ) return 1;
491         if( lsn->inpoint_valid() && lsn->outpoint_valid() ) {
492                 lsn->set_inpoint(lsn->get_outpoint());
493                 lsn->unset_outpoint();
494         }
495         paste_text(line_entry->get_text(), start, end);
496         load_next_selection();
497         return 0;
498 }
499
500 int SWindowGUI::clear_selection()
501 {
502         EDL *edl = swindow->mwindow->edl;
503         double start = edl->local_session->get_selectionstart();
504         double end = edl->local_session->get_selectionend();
505         if( end > start )
506                 paste_text(0, start, end);
507         return 0;
508 }
509
510
511 ScriptPrev::ScriptPrev(SWindowGUI *gui, int x, int y)
512  : BC_GenericButton(x, y, _("Prev"))
513 {
514         sw_gui = gui;
515 }
516
517 ScriptPrev::~ScriptPrev()
518 {
519 }
520
521 int ScriptPrev::handle_event()
522 {
523         sw_gui->load_prev_selection();
524         return 1;
525 }
526
527 ScriptNext::ScriptNext(SWindowGUI *gui, int x, int y)
528  : BC_GenericButton(x, y, _("Next"))
529 {
530         sw_gui = gui;
531 }
532
533 ScriptNext::~ScriptNext()
534 {
535 }
536
537 int ScriptNext::handle_event()
538 {
539         sw_gui->load_next_selection();
540         return 1;
541 }
542
543 ScriptPaste::ScriptPaste(SWindowGUI *gui, int x, int y)
544  : BC_GenericButton(x, y, _("Paste"))
545 {
546         sw_gui = gui;
547 }
548
549 ScriptPaste::~ScriptPaste()
550 {
551 }
552
553 int ScriptPaste::handle_event()
554 {
555         sw_gui->paste_selection();
556         return 1;
557 }
558
559 ScriptClear::ScriptClear(SWindowGUI *gui, int x, int y)
560  : BC_GenericButton(x, y, _("Clear"))
561 {
562         sw_gui = gui;
563 }
564
565 ScriptClear::~ScriptClear()
566 {
567 }
568
569 int ScriptClear::handle_event()
570 {
571         sw_gui->clear_selection();
572         return 1;
573 }
574
575
576 ScriptLines::ScriptLines()
577 {
578         used = 0;
579         lines = 0;
580         text = new char[allocated = 256];
581         *text = 0;
582 }
583
584 ScriptLines::~ScriptLines()
585 {
586         delete [] text;
587 }
588
589 void ScriptLines::append(char *cp)
590 {
591         int len = strlen(cp);
592         if( allocated-used < len+1 ) {
593                 int isz = allocated + used + len;
594                 char *new_text = new char[isz];
595                 allocated = isz;
596                 memcpy(new_text, text, used);
597                 delete [] text;  text = new_text;
598         }
599         memcpy(text+used, cp, len);
600         text[used += len] = 0;
601         ++lines;
602 }
603
604 int ScriptLines::break_lines()
605 {
606         int line_limit = 60;
607         int limit2 = line_limit/2;
608         int limit4 = line_limit/4-2;
609         char *cp = text, *dp = cp+used;
610         int n;  char *bp, *ep, *pp, *sp;
611         for( lines=0; cp<dp; ++lines ) {
612                 // find end of line/buffer
613                 for( ep=cp; ep<dp && *ep!='\n'; ++ep );
614                 // delete trailing spaces
615                 for( sp=ep; sp>=cp && (!*sp || isspace(*sp)); --sp);
616                 ++sp;
617                 if( (n=ep-sp) > 0 ) {
618                         memmove(sp,ep,dp+1-ep);
619                         used -= n;  dp -= n;  ep -= n;
620                 }
621                 ++ep;
622                 // skip, if comment or title line
623                 if( *cp == '*' || *cp == '=' ) {
624                         cp = ep;  continue;
625                 }
626                 // delete leading spaces
627                 for( sp=cp; sp<ep && isspace(*sp); ++sp);
628                 if( (n=sp-cp) > 0 ) {
629                         memmove(cp,sp,dp+1-sp);
630                         used -= n;  dp -= n;  ep -= n;
631                 }
632                 // target about half remaining line, constrain line_limit
633                 if( (n=(ep-1-cp)/2) < limit2 || n > line_limit )
634                         n = line_limit;
635                 // search for last punct, last space before line_limit
636                 for( bp=cp, pp=sp=0; --n>=0 && cp<ep; ++cp ) {
637                         if( ispunct(*cp) && isspace(*(cp+1)) ) pp = cp;
638                         else if( isspace(*cp) ) sp = cp;
639                 }
640                 // line not empty
641                 if( cp < ep ) {
642                         // first, after punctuation
643                         if( pp && pp-bp >= limit4 )
644                                 cp = pp+1;
645                         // then, on spaces
646                         else if( sp ) {
647                                 cp = sp;
648                         }
649                         // last, on next space
650                         else {
651                                 while( cp<dp && !isspace(*cp) ) ++cp;
652                         }
653                         // add new line
654                         if( !*cp ) break;
655                         *cp++ = '\n';
656                 }
657         }
658         return lines;
659 }
660
661 int ScriptLines::get_text_rows()
662 {
663         int ret = 1, i=used;
664         for( char *cp=text; --i>=0; ++cp )
665                 if( *cp == '\n' ) ++ret;
666         return ret;
667 }
668
669 char *ScriptLines::get_text_row(int n)
670 {
671         char *cp = text;
672         if( !n ) return cp;
673         for( int i=used; --i>=0; ) {
674                 if( *cp++ != '\n' ) continue;
675                 if( --n <= 0 ) return cp;
676         }
677         return 0;
678 }
679
680 ScriptScroll::ScriptScroll(SWindowGUI *gui, int x, int y, int w)
681  : BC_ScrollBar(x, y, SCROLL_HORIZ + SCROLL_STRETCH, w, 0, 0, 0)
682 {
683         this->sw_gui = gui;
684 }
685
686 ScriptScroll::~ScriptScroll()
687 {
688 }
689
690 int ScriptScroll::handle_event()
691 {
692         int64_t pos = get_value();
693         sw_gui->set_script_pos(pos);
694         sw_gui->script_position->update(pos);
695         return 1;
696 }
697
698
699 ScriptPosition::ScriptPosition(SWindowGUI *gui, int x, int y, int w, int v, int mn, int mx)
700  : BC_TumbleTextBox(gui, v, mn, mx, x, y, w-BC_Tumbler::calculate_w())
701 {
702         this->sw_gui = gui;
703 }
704
705 ScriptPosition::~ScriptPosition()
706 {
707 }
708
709 int ScriptPosition::handle_event()
710 {
711         int64_t pos = atol(get_text());
712         sw_gui->set_script_pos(pos);
713         sw_gui->script_scroll->update_value(pos);
714         return 1;
715 }
716
717
718 ScriptEntry::ScriptEntry(SWindowGUI *gui, int x, int y, int w, int rows, char *text)
719  : BC_ScrollTextBox(gui, x, y, w, rows, text, -strlen(text))
720 {
721         this->sw_gui = gui;
722         this->ttext = text;
723 }
724
725 ScriptEntry::ScriptEntry(SWindowGUI *gui, int x, int y, int w, int rows)
726  : BC_ScrollTextBox(gui, x, y, w, rows,(char*)0, BCTEXTLEN)
727 {
728         this->sw_gui = gui;
729         this->ttext = 0;
730 }
731
732 ScriptEntry::~ScriptEntry()
733 {
734 }
735
736 void ScriptEntry::set_text(char *text, int isz)
737 {
738         if( !text || !*text ) return;
739         if( isz < 0 ) isz = strlen(text)+1;
740         BC_ScrollTextBox::set_text(text, isz);
741         ttext = text;
742 }
743
744 int ScriptEntry::handle_event()
745 {
746         if( ttext && sw_gui->get_button_down() &&
747             sw_gui->get_buttonpress() == 1 &&
748             sw_gui->get_triple_click() ) {
749                 int ibeam = get_ibeam_letter(), row = 0;
750                 const char *tp = ttext;
751                 while( *tp && tp-ttext < ibeam ) {
752                         if( *tp++ == '\n' ) ++row;
753                 }
754                 int pos = sw_gui->script_entry_no;
755                 sw_gui->load_selection(pos, row);
756         }
757         return 1;
758 }
759
760 int SWindowGUI::load_script_line(FILE *fp)
761 {
762         char line[8192];
763         for(;;) { // skip blank lines
764                 char *cp = fgets(line,sizeof(line),fp);
765                 if( !cp ) return 1;
766                 ++script_line_no;
767                 while( *cp && isspace(*cp) ) ++cp;
768                 if( *cp ) break;
769         }
770
771         ScriptLines *entry = new ScriptLines();
772         script.append(entry);
773
774         for(;;) { // load non-blank lines
775                 //int len = strlen(line);
776                 //if( line[len-1] == '\n' ) line[len-1] = ' ';
777                 entry->append(line);
778                 char *cp = fgets(line,sizeof(line),fp);
779                 if( !cp ) break;
780                 ++script_line_no;
781                 while( *cp && isspace(*cp) ) ++cp;
782                 if( !*cp ) break;
783         }
784         script_text_lines += entry->break_lines();
785         return 0;
786 }
787
788 void SWindowGUI::load_script()
789 {
790         FILE *fp = fopen(script_path,"r");
791         if( !fp ) {
792                 char string[BCTEXTLEN];
793                 sprintf(string,_("cannot open: \"%s\"\n%s"),script_path,strerror(errno));
794                 MainError::show_error(string);
795                 return;
796         }
797         load_script(fp);
798         script_text_no = -1;
799         load_selection(script_entry_no=0, 0);
800 }
801
802 void SWindowGUI::load_script(FILE *fp)
803 {
804         script.remove_all_objects();
805         script_line_no = 0;
806         script_text_lines = 0;
807         while( !load_script_line(fp) );
808         char value[64];
809         sprintf(value,"%ld",ftell(fp));
810         script_filesz->update(value);
811         sprintf(value,"%jd",script_line_no);
812         script_lines->update(value);
813         sprintf(value,"%d",script.size());
814         script_entries->update(value);
815         sprintf(value,"%jd",script_text_lines);
816         script_texts->update(value);
817         int hw = script_scroll->get_h();
818         script_scroll->update_length(script.size(), script_entry_no, hw, 0);
819         script_position->update(script_entry_no);
820         script_position->set_boundaries((int64_t)0, (int64_t)script.size()-1);
821         fclose(fp);
822 }
823
824 void SWindowGUI::save_spumux_data()
825 {
826         char filename[BCTEXTLEN], track_title[BCTEXTLEN];
827         snprintf(filename,sizeof(filename),"%s",script_path);
828         filename[sizeof(filename)-1] = 0;
829         char *bp = strrchr(filename,'/');
830         if( !bp ) bp = filename; else ++bp;
831         char *ext = strrchr(bp, '.');
832         if( !ext ) ext = bp + strlen(bp);
833         int len = sizeof(filename)-1 - (ext-filename);
834
835         Tracks *tracks = swindow->mwindow->edl->tracks;
836         for( Track *track=tracks->first; track; track=track->next ) {
837                 if( track->data_type != TRACK_SUBTITLE ) continue;
838                 if( !track->record ) continue;
839                 char *cp = track_title, *ep = cp+sizeof(track_title)-6;
840                 for( const char *bp=track->title; cp<ep && *bp!=0; ) {
841                         int b = butf8(bp), c = !iswalnum(b) ? '_' : b;
842                         butf8(c, cp);
843                 }
844                 *cp = 0;
845                 snprintf(ext,len,"-%s.udvd",track_title);
846                 FILE *fp = fopen(filename, "w");
847                 if( !fp ) {
848                         eprintf(_("Unable to open %s:\n%m"), filename);
849                         continue;
850                 }
851                 int64_t start = 0;
852                 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
853                         SEdit *sedit = (SEdit *)edit;
854                         if( sedit->length > 0 ) {
855                                 int64_t end = start + sedit->length;
856                                 char *text = sedit->get_text();
857                                 if( *text ) {
858                                         fprintf(fp, "{%jd}{%jd}%s\n", start, end-1, text);
859                                 }
860                                 start = end;
861                         }
862                 }
863                 fclose(fp);
864         }
865 }
866
867
868
869 SWindow::SWindow(MWindow *mwindow)
870  : Thread(1, 0, 0)
871 {
872         this->mwindow = mwindow;
873         window_lock = new Mutex("SWindow::window_lock");
874         swin_lock = new Condition(0,"SWindow::swin_lock");
875         gui = 0;
876         done = 1;
877         gui_done = 1;
878
879         start();
880 }
881
882 SWindow::~SWindow()
883 {
884         stop();
885         delete gui;
886         delete swin_lock;
887         delete window_lock;
888 }
889
890
891 void SWindow::start()
892 {
893         if( !Thread::running() ) {
894                 done = 0;
895                 Thread::start();
896         }
897 }
898
899 void SWindow::stop()
900 {
901         if( Thread::running() ) {
902                 done = 1;
903                 swin_lock->unlock();
904                 window_lock->lock("SWindow::stop");
905                 if( gui ) gui->stop(1);
906                 window_lock->unlock();
907                 Thread::cancel();
908         }
909         Thread::join();
910 }
911
912 void SWindow::run()
913 {
914         int root_w = mwindow->gui->get_root_w(1);
915         int root_h = mwindow->gui->get_root_h(1);
916
917         while( !done ) {
918                 swin_lock->reset();
919                 swin_lock->lock();
920                 if( done ) break;
921                 int x = mwindow->session->swindow_x;
922                 int y = mwindow->session->swindow_y;
923                 int w = mwindow->session->swindow_w;
924                 int h = mwindow->session->swindow_h;
925                 if( w < 600 ) w = 600;
926                 if( h < 500 ) h = 500;
927                 int scr_x = mwindow->gui->get_screen_x(1, -1);
928                 int scr_w = mwindow->gui->get_screen_w(1, -1);
929                 if( x < scr_x ) x = scr_x;
930                 if( x > scr_x+scr_w ) x = scr_x+scr_w;
931                 if( x+w > root_w ) x = root_w - w;
932                 if( x < 0 ) { x = 0;  w = scr_w; }
933                 if( y+h > root_h ) y = root_h - h;
934                 if( y < 0 ) { y = 0;  h = root_h; }
935                 if( y+h > root_h ) h = root_h-y;
936                 mwindow->session->swindow_x = x;
937                 mwindow->session->swindow_y = y;
938                 mwindow->session->swindow_w = w;
939                 mwindow->session->swindow_h = h;
940
941                 gui_done = 0;
942                 gui = new SWindowGUI(this, x, y, w, h);
943                 gui->lock_window("ChannelInfo::gui_create_objects");
944                 gui->load_defaults();
945                 gui->create_objects();
946                 gui->set_icon(mwindow->theme->get_image("record_icon"));
947                 gui->reposition_window(x, y);
948                 gui->resize_event(w, h);
949                 gui->load();
950                 gui->show_window();
951                 gui->unlock_window();
952
953                 int result = gui->run_window();
954                 if( !result ) {
955                         gui->save_spumux_data();
956                 }
957
958                 window_lock->lock("ChannelInfo::run 1");
959                 gui->save_defaults();
960                 delete gui;  gui = 0;
961                 window_lock->unlock();
962         }
963 }
964
965
966
967
968 void SWindow::run_swin()
969 {
970         window_lock->lock("SWindow::run_swin");
971         if( gui ) {
972                 gui->lock_window("SWindow::run_swin");
973                 gui->raise_window();
974                 gui->unlock_window();
975         }
976         else
977                 swin_lock->unlock();
978         window_lock->unlock();
979 }
980
981 void SWindow::paste_subttl()
982 {
983         window_lock->lock("SWindow::paste_subttl 1");
984         if( gui ) {
985                 gui->lock_window("SWindow::paste_subttl 2");
986                 gui->paste_selection();
987                 gui->unlock_window();
988         }
989         window_lock->unlock();
990 }
991
992 int SWindow::update_selection()
993 {
994         window_lock->lock("SWindow::update_selection 1");
995         if( gui ) {
996                 gui->lock_window("SWindow::update_selection 2");
997                 gui->update_selection();
998                 gui->unlock_window();
999         }
1000         window_lock->unlock();
1001         return 0;
1002 }
1003
1004
1005
1006 SubttlSWin::SubttlSWin(MWindow *mwindow)
1007  : BC_MenuItem(_("SubTitle..."), _("Alt-y"), 'y')
1008 {
1009         set_alt();
1010         this->mwindow = mwindow;
1011 }
1012
1013 SubttlSWin::~SubttlSWin()
1014 {
1015 }
1016
1017 int SubttlSWin::handle_event()
1018 {
1019         mwindow->gui->swindow->run_swin();
1020         return 1;
1021 };
1022