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