alt transport keys, snap editing, grab focus, inv hilight clr, subtitle fix
[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( 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 }
799
800 void SWindowGUI::load_script(FILE *fp)
801 {
802         script.remove_all_objects();
803         script_line_no = 0;
804         script_text_lines = 0;
805         while( !load_script_line(fp) );
806         char value[64];
807         sprintf(value,"%ld",ftell(fp));
808         script_filesz->update(value);
809         sprintf(value,"%jd",script_line_no);
810         script_lines->update(value);
811         sprintf(value,"%d",script.size());
812         script_entries->update(value);
813         sprintf(value,"%jd",script_text_lines);
814         script_texts->update(value);
815         int hw = script_scroll->get_h();
816         script_scroll->update_length(script.size(), script_entry_no, hw, 0);
817         script_position->update(script_entry_no);
818         script_position->set_boundaries((int64_t)0, (int64_t)script.size()-1);
819
820         fclose(fp);
821 }
822
823 void SWindowGUI::save_spumux_data()
824 {
825         char filename[BCTEXTLEN], track_title[BCTEXTLEN];
826         snprintf(filename,sizeof(filename),"%s",script_path);
827         filename[sizeof(filename)-1] = 0;
828         char *bp = strrchr(filename,'/');
829         if( !bp ) bp = filename; else ++bp;
830         char *ext = strrchr(bp, '.');
831         if( !ext ) ext = bp + strlen(bp);
832         int len = sizeof(filename)-1 - (ext-filename);
833
834         Tracks *tracks = swindow->mwindow->edl->tracks;
835         for( Track *track=tracks->first; track; track=track->next ) {
836                 if( track->data_type != TRACK_SUBTITLE ) continue;
837                 if( !track->record ) continue;
838                 char *cp = track_title;
839                 for( char *bp=track->title; *bp; ++bp,++cp )
840                         *cp = !isalnum(*bp) ? '_' : *bp;
841                 *cp = 0;
842                 snprintf(ext,len,"-%s.udvd",track_title);
843                 FILE *fp = fopen(filename, "w");
844                 if( !fp ) {
845                         eprintf(_("Unable to open %s:\n%m"), filename);
846                         continue;
847                 }
848                 int64_t start = 0;
849                 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
850                         SEdit *sedit = (SEdit *)edit;
851                         if( sedit->length > 0 ) {
852                                 int64_t end = start + sedit->length;
853                                 char *text = sedit->get_text();
854                                 if( *text ) {
855                                         fprintf(fp, "{%jd}{%jd}%s\n", start, end-1, text);
856                                 }
857                                 start = end;
858                         }
859                 }
860                 fclose(fp);
861         }
862 }
863
864
865
866 SWindow::SWindow(MWindow *mwindow)
867  : Thread(1, 0, 0)
868 {
869         this->mwindow = mwindow;
870         window_lock = new Mutex("SWindow::window_lock");
871         swin_lock = new Condition(0,"SWindow::swin_lock");
872         gui = 0;
873         done = 1;
874         gui_done = 1;
875
876         start();
877 }
878
879 SWindow::~SWindow()
880 {
881         stop();
882         delete gui;
883         delete swin_lock;
884         delete window_lock;
885 }
886
887
888 void SWindow::start()
889 {
890         if( !Thread::running() ) {
891                 done = 0;
892                 Thread::start();
893         }
894 }
895
896 void SWindow::stop()
897 {
898         if( Thread::running() ) {
899                 done = 1;
900                 swin_lock->unlock();
901                 window_lock->lock("SWindow::stop");
902                 if( gui ) gui->stop(1);
903                 window_lock->unlock();
904                 Thread::cancel();
905         }
906         Thread::join();
907 }
908
909 void SWindow::run()
910 {
911         int root_w = mwindow->gui->get_root_w(1);
912         int root_h = mwindow->gui->get_root_h(1);
913
914         while( !done ) {
915                 swin_lock->reset();
916                 swin_lock->lock();
917                 if( done ) break;
918                 int x = mwindow->session->swindow_x;
919                 int y = mwindow->session->swindow_y;
920                 int w = mwindow->session->swindow_w;
921                 int h = mwindow->session->swindow_h;
922                 if( w < 600 ) w = 600;
923                 if( h < 500 ) h = 500;
924                 int scr_x = mwindow->gui->get_screen_x(1, -1);
925                 int scr_w = mwindow->gui->get_screen_w(1, -1);
926                 if( x < scr_x ) x = scr_x;
927                 if( x > scr_x+scr_w ) x = scr_x+scr_w;
928                 if( x+w > root_w ) x = root_w - w;
929                 if( x < 0 ) { x = 0;  w = scr_w; }
930                 if( y+h > root_h ) y = root_h - h;
931                 if( y < 0 ) { y = 0;  h = root_h; }
932                 if( y+h > root_h ) h = root_h-y;
933                 mwindow->session->swindow_x = x;
934                 mwindow->session->swindow_y = y;
935                 mwindow->session->swindow_w = w;
936                 mwindow->session->swindow_h = h;
937
938                 gui_done = 0;
939                 gui = new SWindowGUI(this, x, y, w, h);
940                 gui->lock_window("ChannelInfo::gui_create_objects");
941                 gui->load_defaults();
942                 gui->create_objects();
943                 gui->set_icon(mwindow->theme->get_image("record_icon"));
944                 gui->reposition_window(x, y);
945                 gui->resize_event(w, h);
946                 gui->load();
947                 gui->show_window();
948                 gui->unlock_window();
949
950                 int result = gui->run_window();
951                 if( !result ) {
952                         gui->save_spumux_data();
953                 }
954
955                 window_lock->lock("ChannelInfo::run 1");
956                 gui->save_defaults();
957                 delete gui;  gui = 0;
958                 window_lock->unlock();
959         }
960 }
961
962
963
964
965 void SWindow::run_swin()
966 {
967         window_lock->lock("SWindow::run_swin");
968         if( gui ) {
969                 gui->lock_window("SWindow::run_swin");
970                 gui->raise_window();
971                 gui->unlock_window();
972         }
973         else
974                 swin_lock->unlock();
975         window_lock->unlock();
976 }
977
978 void SWindow::paste_subttl()
979 {
980         window_lock->lock("SWindow::paste_subttl 1");
981         if( gui ) {
982                 gui->lock_window("SWindow::paste_subttl 2");
983                 gui->paste_selection();
984                 gui->unlock_window();
985         }
986         window_lock->unlock();
987 }
988
989 int SWindow::update_selection()
990 {
991         window_lock->lock("SWindow::update_selection 1");
992         if( gui ) {
993                 gui->lock_window("SWindow::update_selection 2");
994                 gui->update_selection();
995                 gui->unlock_window();
996         }
997         window_lock->unlock();
998         return 0;
999 }
1000
1001
1002
1003 SubttlSWin::SubttlSWin(MWindow *mwindow)
1004  : BC_MenuItem(_("SubTitle..."), _("Alt-y"), 'y')
1005 {
1006         set_alt();
1007         this->mwindow = mwindow;
1008 }
1009
1010 SubttlSWin::~SubttlSWin()
1011 {
1012 }
1013
1014 int SubttlSWin::handle_event()
1015 {
1016         mwindow->gui->swindow->run_swin();
1017         return 1;
1018 };
1019