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