improve resize flash operation, fixup xv grab/ungrab, fixup label updates
[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         file_entries = new ArrayList<BC_ListBoxItem*>;
81         FileSystem fs;  char string[BCTEXTLEN];
82 // Load current directory
83         fs.update(getcwd(string, BCTEXTLEN));
84         int total_files = fs.total_files();
85         for(int i = 0; i < total_files; i++) {
86                 const char *name = fs.get_entry(i)->get_name();
87                 file_entries->append(new BC_ListBoxItem(name));
88         }
89 }
90
91 SWindowLoadPath::~SWindowLoadPath()
92 {
93         file_entries->remove_all_objects();
94         delete file_entries;
95 }
96
97 int SWindowLoadPath::handle_event()
98 {
99         calculate_suggestions(file_entries);
100         strcpy(sw_gui->script_path, get_text());
101         return 1;
102 }
103
104
105 SWindowLoadFile::SWindowLoadFile(SWindowGUI *gui, int x, int y)
106  : BC_GenericButton(x, y, _("Load"))
107 {
108         this->sw_gui = gui;
109 }
110
111 SWindowLoadFile::~SWindowLoadFile()
112 {
113 }
114
115 int SWindowLoadFile::handle_event()
116 {
117         if( sw_gui->script_path[0] ) {
118                 sw_gui->load_path->set_suggestions(0,0);
119                 sw_gui->load_script();
120                 sw_gui->set_script_pos(0);
121         }
122         else {
123                 eprintf(_("script text file path required"));
124         }
125         return 1;
126 }
127
128 SWindowSaveFile::SWindowSaveFile(SWindowGUI *gui, int x, int y)
129  : BC_GenericButton(x, y, _("Save"))
130 {
131         this->sw_gui = gui;
132 }
133
134 SWindowSaveFile::~SWindowSaveFile()
135 {
136 }
137
138 int SWindowSaveFile::handle_event()
139 {
140         if( sw_gui->script_path[0] ) {
141                 sw_gui->save_spumux_data();
142         }
143         else {
144                 eprintf(_("script microdvd file path required"));
145         }
146         return 1;
147 }
148
149
150
151
152 void SWindowGUI::create_objects()
153 {
154         int x = 10, y = 10;
155         BC_Title *title = new BC_Title(x, y, _("Path:"));
156         add_subwindow(title);
157         int x1 = x + title->get_w() + pad, y1 = y;
158         load_path = new SWindowLoadPath(this, x1, y1, script_path);
159         add_subwindow(load_path);
160         x1 += load_path->get_w() + 2*pad;
161         add_subwindow(load_file = new SWindowLoadFile(this, x1, y1));
162         x1 += load_file->get_w() + 2*pad;
163         add_subwindow(save_file = new SWindowSaveFile(this, x1, y1));
164         y += max(load_path->get_h(), load_file->get_h()) + pad;
165         x1 = x + pad, y1 = y;
166         BC_Title *title1, *title2;
167         add_subwindow(title1 = new BC_Title(x1, y1, _("File Size:")));
168         y += title1->get_h() + pad;
169         int y2 = y;
170         add_subwindow(title2 = new BC_Title(x1, y2, _("Entries:")));
171         int x2 = x1 + max(title1->get_w(), title2->get_w()) + pad;
172         add_subwindow(script_filesz = new BC_Title(x2, y1, "0", MEDIUMFONT, YELLOW));
173         add_subwindow(script_entries = new BC_Title(x2, y2, "0", MEDIUMFONT, YELLOW));
174         int x3 = x2 + max(script_entries->get_w()*8, script_filesz->get_w()*8) + pad;
175         add_subwindow(title1 = new BC_Title(x3, y1, _("Lines:")));
176         add_subwindow(title2 = new BC_Title(x3, y2, _("Texts:")));
177         int x4 = x3 + max(title1->get_w(), title2->get_w()) + pad;
178         add_subwindow(script_lines = new BC_Title(x4, y1, "0", MEDIUMFONT, YELLOW));
179         add_subwindow(script_texts = new BC_Title(x4, y2, "0", MEDIUMFONT, YELLOW));
180         int x5 = x4 + max(script_lines->get_w()*8, script_texts->get_w()*8) + 2*pad;
181         add_subwindow(prev_script = new ScriptPrev(this, x5, y1));
182         add_subwindow(next_script = new ScriptNext(this, x5, y2));
183         int x6 = x5 + max(prev_script->get_w(), next_script->get_w()) + 2*pad;
184         add_subwindow(paste_script = new ScriptPaste(this, x6, y1));
185         add_subwindow(clear_script = new ScriptClear(this, x6, y2));
186         y += max(title1->get_h(), title2->get_h()) + 2*pad;
187
188         script_position = new ScriptPosition(this, x, y, 100);
189         script_position->create_objects();
190         x1 = x + script_position->get_w() + pad;
191         add_subwindow(script_scroll = new ScriptScroll(this, x1, y, get_w()-x1-pad));
192         y += script_scroll->get_h() + 2*pad;
193         x1 = x + pad;
194         blank_line = new char[2];
195         blank_line[0] = ' ';  blank_line[1] = 0;
196         add_subwindow(script_title = new BC_Title(x1, y, _("Script Text:")));
197         y += script_title->get_h() + pad;
198         int rows = (ok_y - y - BC_Title::calculate_h(this,_("Line Text:")) -
199                 4*pad) / text_rowsz - 3;
200         int w1 = get_w() - x1 - pad;
201         script_entry = new ScriptEntry(this, x1, y, w1, rows, blank_line);
202         script_entry->create_objects();
203         y += script_entry->get_h() + pad;
204         add_subwindow(line_title = new BC_Title(x1, y, _("Line Text:")));
205         y += line_title->get_h() + pad;
206         line_entry = new ScriptEntry(this, x1, y, w1, 3);
207         line_entry->create_objects();
208         ok = new SWindowOK(this, ok_x, ok_y);
209         add_subwindow(ok);
210         cancel = new SWindowCancel(this, cancel_x, cancel_y);
211         add_subwindow(cancel);
212 }
213
214 void SWindowGUI::load()
215 {
216         const char *script_text =
217         _("Adding Subtitles: quick \"How To\" (= or * indicates comment)\n"
218         "*2345678901234567890123456789012345678901234567890123456789\n"
219         "For regular DVD subtitles, put script in a text file. "
220         "Lines can be any length but they will be broken up "
221         "to fit according to some criteria below.\n"
222         "Running text used as script lines will be broken into multilple lines.\n"
223         "The target line length is 60 characters.\n"
224         "Punctuation may be flagged to create an early line break.\n"
225         "Single carriage return ends an individual script line.\n"
226         "Double carriage return indicates the end of an entry.\n"
227         "Whitespace at beginning or end of line is removed.\n"
228         "You can edit the active line in the Line Text box.\n"
229         "\n"
230         "== A new entry is here for illustration purposes.\n"
231         "*  Entry 2\n"
232         "This is the second entry.\n");
233
234         if( script_path[0] && !access(script_path,R_OK) ) {
235                 load_script();
236         }
237         else {
238                 script_path[0] = 0;
239                 load_path->update(script_path);
240                 FILE *fp = fmemopen((void *)script_text, strlen(script_text), "r");
241                 load_script(fp);
242         }
243         int text_no = script_text_no;
244         script_text_no = -1;
245         load_selection(script_entry_no, text_no);
246 }
247
248 SWindowGUI::SWindowGUI(SWindow *swindow, int x, int y, int w, int h)
249  : BC_Window(_(PROGRAM_NAME ": Subtitle"), x, y, w, h, 600, 500,
250         1, 0, 0 , -1, swindow->mwindow->get_cwindow_display())
251 {
252         this->swindow = swindow;
253         pad = 8;
254
255         ok = 0;
256         ok_w = BC_OKButton::calculate_w();
257         ok_h = BC_OKButton::calculate_h();
258         ok_x = 10;
259         ok_y = h - ok_h - 10;
260         cancel = 0;
261         cancel_w = BC_CancelButton::calculate_w();
262         cancel_h = BC_CancelButton::calculate_h();
263         cancel_x = w - cancel_w - 10,
264         cancel_y = h - cancel_h - 10;
265
266         load_path = 0;
267         load_file = 0;
268         save_file = 0;
269         script_filesz = 0;
270         script_lines = 0;
271         script_entries = 0;
272         script_texts = 0;
273         script_entry_no = 0;
274         script_text_no = 0;
275         script_line_no = 0;
276         script_text_lines = 0;
277         prev_script = 0;
278         next_script = 0;
279         paste_script = 0;
280         clear_script = 0;
281         script_position = 0;
282         script_entry = 0;
283         line_entry = 0;
284         script_scroll = 0;
285         blank_line = 0;
286         text_font = MEDIUMFONT;
287         text_rowsz = get_text_ascent(text_font)+1 + get_text_descent(text_font)+1;
288 }
289
290 SWindowGUI::~SWindowGUI()
291 {
292         delete script_entry;
293         delete line_entry;
294         delete script_position;
295         delete [] blank_line;
296 }
297
298 void SWindowGUI::stop(int v)
299 {
300         if( !swindow->gui_done ) {
301                 swindow->gui_done = 1;
302                 set_done(v);
303         }
304 }
305
306 int SWindowGUI::translation_event()
307 {
308         swindow->mwindow->session->swindow_x = get_x();
309         swindow->mwindow->session->swindow_y = get_y();
310         return 0;
311 }
312
313 int SWindowGUI::resize_event(int w, int h)
314 {
315         swindow->mwindow->session->swindow_w = w;
316         swindow->mwindow->session->swindow_h = h;
317
318         ok_x = 10;
319         ok_y = h - ok_h - 10;
320         ok->reposition_window(ok_x, ok_y);
321         cancel_x = w - cancel_w - 10,
322         cancel_y = h - cancel_h - 10;
323         cancel->reposition_window(cancel_x, cancel_y);
324
325         int x = script_position->get_x();
326         int y = script_position->get_y();
327         int hh = script_position->get_h();
328         int ww = script_position->get_w();
329         int x1 = x + ww + pad;
330         int w1 = w - x1 - pad;
331         script_scroll->reposition_window(x1, y, w1);
332         y += hh + 2*pad;
333         script_title->reposition_window(x, y);
334         y += script_title->get_h() + pad;
335         w1 = w - x - pad;
336         int rows = (ok_y - y - line_title->get_h() - 4*pad) / text_rowsz - 3;
337         script_entry->reposition_window(x, y, w1, rows);
338         y += script_entry->get_h() + 2*pad;
339         line_title->reposition_window(x, y);
340         y += line_title->get_h() + pad;
341         line_entry->reposition_window(x, y, w1, 3);
342         return 0;
343 }
344
345 void SWindowGUI::load_defaults()
346 {
347         BC_Hash *defaults = swindow->mwindow->defaults;
348         defaults->get("SUBTTL_SCRIPT_PATH", script_path);
349         script_entry_no = defaults->get("SUBTTL_SCRIPT_ENTRY_NO", script_entry_no);
350         script_text_no = defaults->get("SUBTTL_SCRIPT_TEXT_NO", script_text_no);
351 }
352
353 void SWindowGUI::save_defaults()
354 {
355         BC_Hash *defaults = swindow->mwindow->defaults;
356         defaults->update("SUBTTL_SCRIPT_PATH", script_path);
357         defaults->update("SUBTTL_SCRIPT_ENTRY_NO", script_entry_no);
358         defaults->update("SUBTTL_SCRIPT_TEXT_NO", script_text_no);
359 }
360
361 void SWindowGUI::set_script_pos(int64_t entry_no, int text_no)
362 {
363         script_entry_no = entry_no<0 ? 0 :
364                 entry_no>script.size()-1 ? script.size()-1 : entry_no;
365         script_text_no = text_no-1;
366         load_next_selection();
367 }
368
369 int SWindowGUI::load_selection(int pos, int row)
370 {
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->record ) 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->record ) 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, 1, 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, 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( 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 }
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,"%ld",script_line_no);
821         script_lines->update(value);
822         sprintf(value,"%d",script.size());
823         script_entries->update(value);
824         sprintf(value,"%ld",script_text_lines);
825         script_texts->update(value);
826         int hw = 2*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
831         fclose(fp);
832 }
833
834 void SWindowGUI::save_spumux_data()
835 {
836         char filename[BCTEXTLEN], track_title[BCTEXTLEN];
837         snprintf(filename,sizeof(filename),"%s",script_path);
838         filename[sizeof(filename)-1] = 0;
839         char *bp = strrchr(filename,'/');
840         if( !bp ) bp = filename; else ++bp;
841         char *ext = strrchr(bp, '.');
842         if( !ext ) ext = bp + strlen(bp);
843         int len = sizeof(filename)-1 - (ext-filename);
844
845         Tracks *tracks = swindow->mwindow->edl->tracks;
846         for( Track *track=tracks->first; track; track=track->next ) {
847                 if( track->data_type != TRACK_SUBTITLE ) continue;
848                 if( !track->record ) continue;
849                 char *cp = track_title;
850                 for( char *bp=track->title; *bp; ++bp,++cp )
851                         *cp = !isalnum(*bp) ? '_' : *bp;
852                 *cp = 0;
853                 snprintf(ext,len,"-%s.udvd",track_title);
854                 FILE *fp = fopen(filename, "w");
855                 if( !fp ) {
856                         eprintf(_("Unable to open %s:\n%m"), filename);
857                         continue;
858                 }
859                 int64_t start = 0;
860                 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
861                         SEdit *sedit = (SEdit *)edit;
862                         if( sedit->length > 0 ) {
863                                 int64_t end = start + sedit->length;
864                                 char *text = sedit->get_text();
865                                 if( *text ) {
866                                         fprintf(fp, "{%ld}{%ld}%s\n", start, end-1, text);
867                                 }
868                                 start = end;
869                         }
870                 }
871                 fclose(fp);
872         }
873 }
874
875
876
877 SWindow::SWindow(MWindow *mwindow)
878  : Thread(1, 0, 0)
879 {
880         this->mwindow = mwindow;
881         window_lock = new Mutex("SWindow::window_lock");
882         swin_lock = new Condition(0,"SWindow::swin_lock");
883         gui = 0;
884         done = 1;
885         gui_done = 1;
886
887         start();
888 }
889
890 SWindow::~SWindow()
891 {
892         stop();
893         delete gui;
894         delete swin_lock;
895         delete window_lock;
896 }
897
898
899 void SWindow::start()
900 {
901         if( !Thread::running() ) {
902                 done = 0;
903                 Thread::start();
904         }
905 }
906
907 void SWindow::stop()
908 {
909         if( Thread::running() ) {
910                 done = 1;
911                 swin_lock->unlock();
912                 window_lock->lock("SWindow::stop");
913                 if( gui ) gui->stop(1);
914                 window_lock->unlock();
915                 Thread::cancel();
916         }
917         Thread::join();
918 }
919
920 void SWindow::run()
921 {
922         int root_w = mwindow->gui->get_root_w(1);
923         int root_h = mwindow->gui->get_root_h(1);
924
925         while( !done ) {
926                 swin_lock->reset();
927                 swin_lock->lock();
928                 if( done ) break;
929                 int x = mwindow->session->swindow_x;
930                 int y = mwindow->session->swindow_y;
931                 int w = mwindow->session->swindow_w;
932                 int h = mwindow->session->swindow_h;
933                 if( w < 600 ) w = 600;
934                 if( h < 500 ) h = 500;
935                 int scr_x = mwindow->gui->get_screen_x(1, -1);
936                 int scr_w = mwindow->gui->get_screen_w(1, -1);
937                 if( x < scr_x ) x = scr_x;
938                 if( x > scr_x+scr_w ) x = scr_x+scr_w;
939                 if( x+w > root_w ) x = root_w - w;
940                 if( x < 0 ) { x = 0;  w = scr_w; }
941                 if( y+h > root_h ) y = root_h - h;
942                 if( y < 0 ) { y = 0;  h = root_h; }
943                 if( y+h > root_h ) h = root_h-y;
944                 mwindow->session->swindow_x = x;
945                 mwindow->session->swindow_y = y;
946                 mwindow->session->swindow_w = w;
947                 mwindow->session->swindow_h = h;
948
949                 gui_done = 0;
950                 gui = new SWindowGUI(this, x, y, w, h);
951                 gui->lock_window("ChannelInfo::gui_create_objects");
952                 gui->load_defaults();
953                 gui->create_objects();
954                 gui->set_icon(mwindow->theme->get_image("record_icon"));
955                 gui->reposition_window(x, y);
956                 gui->resize_event(w, h);
957                 gui->load();
958                 gui->show_window();
959                 gui->unlock_window();
960
961                 int result = gui->run_window();
962                 if( !result ) {
963                         gui->save_spumux_data();
964                 }
965
966                 window_lock->lock("ChannelInfo::run 1");
967                 gui->save_defaults();
968                 delete gui;  gui = 0;
969                 window_lock->unlock();
970         }
971 }
972
973
974
975
976 void SWindow::run_swin()
977 {
978         window_lock->lock("SWindow::run_swin");
979         if( gui ) {
980                 gui->lock_window("SWindow::run_swin");
981                 gui->raise_window();
982                 gui->unlock_window();
983         }
984         else
985                 swin_lock->unlock();
986         window_lock->unlock();
987 }
988
989 void SWindow::paste_subttl()
990 {
991         window_lock->lock("SWindow::paste_subttl 1");
992         if( gui ) {
993                 gui->lock_window("SWindow::paste_subttl 2");
994                 gui->paste_selection();
995                 gui->unlock_window();
996         }
997         window_lock->unlock();
998 }
999
1000 int SWindow::update_selection()
1001 {
1002         window_lock->lock("SWindow::update_selection 1");
1003         if( gui ) {
1004                 gui->lock_window("SWindow::update_selection 2");
1005                 gui->update_selection();
1006                 gui->unlock_window();
1007         }
1008         window_lock->unlock();
1009         return 0;
1010 }
1011
1012
1013
1014 SubttlSWin::SubttlSWin(MWindow *mwindow)
1015  : BC_MenuItem(_("SubTitle..."), _("Alt-y"), 'y')
1016 {
1017         set_alt();
1018         this->mwindow = mwindow;
1019 }
1020
1021 SubttlSWin::~SubttlSWin()
1022 {
1023 }
1024
1025 int SubttlSWin::handle_event()
1026 {
1027         mwindow->gui->swindow->run_swin();
1028         return 1;
1029 };
1030