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