bacf80d75d10f15fa3b364f36a11da353999aba6
[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 "localsession.h"
9 #include "mainerror.h"
10 #include "mainmenu.h"
11 #include "mainsession.h"
12 #include "mainundo.h"
13 #include "mwindow.h"
14 #include "mwindowgui.h"
15 #include "mutex.h"
16 #include "strack.h"
17 #include "swindow.h"
18 #include "theme.h"
19 #include "track.h"
20 #include "tracks.h"
21
22 #include<ctype.h>
23 #include<errno.h>
24 #include<stdint.h>
25 #include<stdlib.h>
26 #include<string.h>
27 #include<unistd.h>
28
29 SWindowOK::SWindowOK(SWindowGUI *gui, int x, int y)
30  : BC_OKButton(x, y)
31 {
32         this->gui = gui;
33 }
34
35 SWindowOK::~SWindowOK()
36 {
37 }
38
39 int SWindowOK::button_press_event()
40 {
41         if(get_buttonpress() == 1 && is_event_win() && cursor_inside()) {
42                 gui->stop(0);
43                 return 1;
44         }
45         return 0;
46 }
47
48 int SWindowOK::keypress_event()
49 {
50         return 0;
51 }
52
53
54 SWindowCancel::SWindowCancel(SWindowGUI *gui, int x, int y)
55  : BC_CancelButton(x, y)
56 {
57         this->gui = gui;
58 }
59
60 SWindowCancel::~SWindowCancel()
61 {
62 }
63
64 int SWindowCancel::button_press_event()
65 {
66         if(get_buttonpress() == 1 && is_event_win() && cursor_inside()) {
67                 gui->stop(1);
68                 return 1;
69         }
70         return 0;
71 }
72
73
74 SWindowLoadPath::SWindowLoadPath(SWindowGUI *gui, int x, int y, char *path)
75  : BC_TextBox(x, y, 200, 1, path)
76 {
77         this->sw_gui = gui;
78
79         file_entries = new ArrayList<BC_ListBoxItem*>;
80         FileSystem fs;  char string[BCTEXTLEN];
81 // Load current directory
82         fs.update(getcwd(string, BCTEXTLEN));
83         int total_files = fs.total_files();
84         for(int i = 0; i < total_files; i++) {
85                 const char *name = fs.get_entry(i)->get_name();
86                 file_entries->append(new BC_ListBoxItem(name));
87         }
88 }
89
90 SWindowLoadPath::~SWindowLoadPath()
91 {
92         file_entries->remove_all_objects();
93         delete file_entries;
94 }
95
96 int SWindowLoadPath::handle_event()
97 {
98         calculate_suggestions(file_entries);
99         strcpy(sw_gui->script_path, get_text());
100         return 1;
101 }
102
103
104 SWindowLoadFile::SWindowLoadFile(SWindowGUI *gui, int x, int y)
105  : BC_GenericButton(x, y, _("Load"))
106 {
107         this->sw_gui = gui;
108 }
109
110 SWindowLoadFile::~SWindowLoadFile()
111 {
112 }
113
114 int SWindowLoadFile::handle_event()
115 {
116         sw_gui->load_path->set_suggestions(0,0);
117         sw_gui->load_script();
118         sw_gui->set_script_pos(0);
119         return 1;
120 }
121
122 SWindowSaveFile::SWindowSaveFile(SWindowGUI *gui, int x, int y)
123  : BC_GenericButton(x, y, _("Save"))
124 {
125         this->sw_gui = gui;
126 }
127
128 SWindowSaveFile::~SWindowSaveFile()
129 {
130 }
131
132 int SWindowSaveFile::handle_event()
133 {
134         sw_gui->save_spumux_data();
135         return 1;
136 }
137
138
139
140
141 void SWindowGUI::create_objects()
142 {
143         int x = 10, y = 10;
144         BC_Title *title = new BC_Title(x, y, _("Path:"));
145         add_subwindow(title);
146         int x1 = x + title->get_w() + pad, y1 = y;
147         load_path = new SWindowLoadPath(this, x1, y1, script_path);
148         add_subwindow(load_path);
149         x1 += load_path->get_w() + 2*pad;
150         add_subwindow(load_file = new SWindowLoadFile(this, x1, y1));
151         x1 += load_file->get_w() + 2*pad;
152         add_subwindow(save_file = new SWindowSaveFile(this, x1, y1));
153         y += max(load_path->get_h(), load_file->get_h()) + pad;
154         x1 = x + pad, y1 = y;
155         BC_Title *title1, *title2;
156         add_subwindow(title1 = new BC_Title(x1, y1, _("File Size:")));
157         y += title1->get_h() + pad;
158         int y2 = y;
159         add_subwindow(title2 = new BC_Title(x1, y2, _("Entries:")));
160         int x2 = x1 + max(title1->get_w(), title2->get_w()) + pad;
161         add_subwindow(script_filesz = new BC_Title(x2, y1, "0", MEDIUMFONT, YELLOW));
162         add_subwindow(script_entries = new BC_Title(x2, y2, "0", MEDIUMFONT, YELLOW));
163         int x3 = x2 + max(script_entries->get_w()*8, script_filesz->get_w()*8) + pad;
164         add_subwindow(title1 = new BC_Title(x3, y1, _("Lines:")));
165         add_subwindow(title2 = new BC_Title(x3, y2, _("Texts:")));
166         int x4 = x3 + max(title1->get_w(), title2->get_w()) + pad;
167         add_subwindow(script_lines = new BC_Title(x4, y1, "0", MEDIUMFONT, YELLOW));
168         add_subwindow(script_texts = new BC_Title(x4, y2, "0", MEDIUMFONT, YELLOW));
169         int x5 = x4 + max(script_lines->get_w()*8, script_texts->get_w()*8) + 2*pad;
170         add_subwindow(prev_script = new ScriptPrev(this, x5, y1));
171         add_subwindow(next_script = new ScriptNext(this, x5, y2));
172         int x6 = x5 + max(prev_script->get_w(), next_script->get_w()) + 2*pad;
173         add_subwindow(paste_script = new ScriptPaste(this, x6, y1));
174         add_subwindow(clear_script = new ScriptClear(this, x6, y2));
175         y += max(title1->get_h(), title2->get_h()) + 2*pad;
176
177         script_position = new ScriptPosition(this, x, y, 100);
178         script_position->create_objects();
179         x1 = x + script_position->get_w() + pad;
180         add_subwindow(script_scroll = new ScriptScroll(this, x1, y, get_w()-x1-pad));
181         y += script_scroll->get_h() + 2*pad;
182         x1 = x + pad;
183         blank_line = new char[2];
184         blank_line[0] = ' ';  blank_line[1] = 0;
185         int rows = (ok_y - y - 4*pad) / text_rowsz - 3;
186         int w1 = get_w()-x1-pad;
187         script_entry = new ScriptEntry(this, x1, y, w1, rows, blank_line);
188         script_entry->create_objects();
189         y += script_entry->get_h() + pad;
190         line_entry = new ScriptEntry(this, x1, y, w1, 3);
191         line_entry->create_objects();
192         ok = new SWindowOK(this, ok_x, ok_y);
193         add_subwindow(ok);
194         cancel = new SWindowCancel(this, cancel_x, cancel_y);
195         add_subwindow(cancel);
196 }
197
198 void SWindowGUI::load()
199 {
200         if( script_path[0] && !access(script_path,R_OK) ) {
201                 load_script();
202                 int text_no = script_text_no;
203                 script_text_no = -1;
204                 load_selection(script_entry_no, text_no);
205         }
206         else {
207                 script.remove_all_objects();
208                 script_path[0] = 0;
209                 load_path->update(script_path);
210                 script_entry_no = 0;
211                 script_text_no = 0;
212         }
213 }
214
215 SWindowGUI::SWindowGUI(SWindow *swindow, int x, int y, int w, int h)
216  : BC_Window(_(PROGRAM_NAME ": Subtitle"), x, y, w, h, 600, 500,
217         1, 0, 0 , -1, swindow->mwindow->get_cwindow_display())
218 {
219         this->swindow = swindow;
220         pad = 8;
221
222         ok = 0;
223         ok_w = BC_OKButton::calculate_w();
224         ok_h = BC_OKButton::calculate_h();
225         ok_x = 10;
226         ok_y = h - ok_h - 10;
227         cancel = 0;
228         cancel_w = BC_CancelButton::calculate_w();
229         cancel_h = BC_CancelButton::calculate_h();
230         cancel_x = w - cancel_w - 10,
231         cancel_y = h - cancel_h - 10;
232
233         load_path = 0;
234         load_file = 0;
235         save_file = 0;
236         script_filesz = 0;
237         script_lines = 0;
238         script_entries = 0;
239         script_texts = 0;
240         script_entry_no = 0;
241         script_text_no = 0;
242         script_line_no = 0;
243         script_text_lines = 0;
244         prev_script = 0;
245         next_script = 0;
246         paste_script = 0;
247         clear_script = 0;
248         script_position = 0;
249         script_entry = 0;
250         line_entry = 0;
251         script_scroll = 0;
252         blank_line = 0;
253         text_font = MEDIUMFONT;
254         text_rowsz = get_text_ascent(text_font)+1 + get_text_descent(text_font)+1;
255 }
256
257 SWindowGUI::~SWindowGUI()
258 {
259         delete script_entry;
260         delete line_entry;
261         delete script_position;
262         delete [] blank_line;
263 }
264
265 void SWindowGUI::stop(int v)
266 {
267         if( !swindow->gui_done ) {
268                 swindow->gui_done = 1;
269                 set_done(v);
270         }
271 }
272
273 int SWindowGUI::translation_event()
274 {
275         swindow->mwindow->session->swindow_x = get_x();
276         swindow->mwindow->session->swindow_y = get_y();
277         return 0;
278 }
279
280 int SWindowGUI::resize_event(int w, int h)
281 {
282         swindow->mwindow->session->swindow_w = w;
283         swindow->mwindow->session->swindow_h = h;
284
285         ok_x = 10;
286         ok_y = h - ok_h - 10;
287         ok->reposition_window(ok_x, ok_y);
288         cancel_x = w - cancel_w - 10,
289         cancel_y = h - cancel_h - 10;
290         cancel->reposition_window(cancel_x, cancel_y);
291
292         int x = script_position->get_x();
293         int y = script_position->get_y();
294         int hh = script_position->get_h();
295         int ww = script_position->get_w();
296         int x1 = x + ww + pad;
297         int w1 = w - x1 - pad;
298         script_scroll->reposition_window(x1, y, w1);
299         y += hh + 2*pad;
300         w1 = w - x - pad;
301         int rows = (ok_y - y - 4*pad) / text_rowsz - 3;
302         script_entry->reposition_window(x, y, w1, rows);
303         y += script_entry->get_h() + pad;
304         line_entry->reposition_window(x, y, w1, 3);
305         return 0;
306 }
307
308 void SWindowGUI::load_defaults()
309 {
310         BC_Hash *defaults = swindow->mwindow->defaults;
311         defaults->get("SUBTTL_SCRIPT_PATH", script_path);
312         script_entry_no = defaults->get("SUBTTL_SCRIPT_ENTRY_NO", script_entry_no);
313         script_text_no = defaults->get("SUBTTL_SCRIPT_TEXT_NO", script_text_no);
314 }
315
316 void SWindowGUI::save_defaults()
317 {
318         BC_Hash *defaults = swindow->mwindow->defaults;
319         defaults->update("SUBTTL_SCRIPT_PATH", script_path);
320         defaults->update("SUBTTL_SCRIPT_ENTRY_NO", script_entry_no);
321         defaults->update("SUBTTL_SCRIPT_TEXT_NO", script_text_no);
322 }
323
324 void SWindowGUI::set_script_pos(int64_t entry_no, int text_no)
325 {
326         script_entry_no = entry_no<0 ? 0 :
327                 entry_no>script.size()-1 ? script.size()-1 : entry_no;
328         script_text_no = text_no-1;
329         load_next_selection();
330 }
331
332 int SWindowGUI::load_selection(int pos, int row)
333 {
334         ScriptLines *texts = script[pos];
335         char *rp = texts->get_text_row(row);
336         if( !rp || *rp == '=' || *rp == '*' || *rp=='\n' ) return 1;
337         if( pos != script_entry_no || script_text_no < 0 ) {
338                 script_entry_no = pos;
339                 script_scroll->update_value(script_entry_no);
340                 script_position->update(script_entry_no);
341                 script_entry->set_text(texts->text);
342                 script_entry->set_text_row(0);
343         }
344         script_text_no = row;
345         char line[BCTEXTLEN], *bp = line;
346         char *ep = bp+sizeof(line)-1, *cp = rp;
347         while( bp < ep && *cp && *cp!='\n' ) *bp++ = *cp++;
348         *bp = 0;  bp = texts->text;
349         int char1 = rp-bp, char2 = cp-bp;
350         script_entry->set_selection(char1, char2, char2);
351         int rows = script_entry->get_rows();
352         int rows2 = rows / 2;
353         int rows1 = texts->lines+1 - rows;
354         if( (row-=rows2) > rows1 ) row = rows1;
355         if( row < 0 ) row = 0;
356         script_entry->set_text_row(row);
357         line_entry->update(line);
358         line_entry->set_text_row(0);
359         return 0;
360 }
361
362 int SWindowGUI::load_prev_selection()
363 {
364         int pos = script_entry_no, row = script_text_no;
365         if( pos < 0 || pos >= script.size() ) return 1;
366         for(;;) {
367                 if( --row < 0 ) {
368                         if( --pos < 0 ) break;
369                         row = script[pos]->lines;
370                         continue;
371                 }
372                 if( !load_selection(pos, row) )
373                         return 0;
374         }
375         return 1;
376 }
377
378 int SWindowGUI::load_next_selection()
379 {
380         int pos = script_entry_no, row = script_text_no;
381         if( pos < 0 || pos >= script.size() ) return 1;
382         for(;;) {
383                 if( ++row >= script[pos]->lines ) {
384                         if( ++pos >= script.size() ) break;
385                         row = -1;
386                         continue;
387                 }
388                 if( !load_selection(pos, row) )
389                         return 0;
390         }
391         return 1;
392 }
393
394 int SWindowGUI::update_selection()
395 {
396         EDL *edl = swindow->mwindow->edl;
397         LocalSession *lsn = edl->local_session;
398         double position = lsn->get_selectionstart();
399         Edit *edit = 0;
400         Tracks *tracks = edl->tracks;
401         for( Track *track=tracks->first; track && !edit; track=track->next ) {
402                 if( !track->record ) continue;
403                 if( track->data_type != TRACK_SUBTITLE ) continue;
404                 int64_t pos = track->to_units(position,0);
405                 edit = track->edits->editof(pos, PLAY_FORWARD, 0);
406         }
407         if( !edit ) return 1;
408         SEdit *sedit = (SEdit *)edit;
409         line_entry->update(sedit->get_text());
410         line_entry->set_text_row(0);
411         return 0;
412 }
413
414 int MWindow::paste_subtitle_text(char *text, double start, double end)
415 {
416         gui->lock_window("MWindow::paste_subtitle_text 1");
417         undo->update_undo_before();
418
419         Tracks *tracks = edl->tracks;
420         for( Track *track=tracks->first; track; track=track->next ) {
421                 if( track->data_type != TRACK_SUBTITLE ) continue;
422                 if( !track->record ) continue;
423                 int64_t start_i = track->to_units(start, 0);
424                 int64_t end_i = track->to_units(end, 1);
425                 track->edits->clear(start_i,end_i);
426                 SEdit *sedit = (SEdit *)track->edits->create_silence(start_i,end_i);
427                 strcpy(sedit->get_text(),text);
428                 track->edits->optimize();
429         }
430
431         save_backup();
432         undo->update_undo_after(_("paste subttl"), LOAD_EDITS | LOAD_PATCHES);
433
434         sync_parameters(CHANGE_EDL);
435         restart_brender();
436         gui->update(0, 1, 1, 0, 0, 0, 0);
437         gui->unlock_window();
438
439         return 0;
440 }
441
442 int SWindowGUI::paste_text(const char *text, double start, double end)
443 {
444         char stext[BCTEXTLEN], *cp = stext;
445         if( text ) { // remap charset, reformat if needed
446                 for( const char *bp=text; *bp!=0; ++bp,++cp ) {
447                         switch( *bp ) {
448                         case '\n':  *cp = '|';  break;
449                         default:    *cp = *bp;  break;
450                         }
451                 }
452         }
453         if( cp > stext && *(cp-1) == '|' ) --cp;
454         *cp = 0;
455         return swindow->mwindow->paste_subtitle_text(stext, start, end);
456 }
457
458 int SWindowGUI::paste_selection()
459 {
460         EDL *edl = swindow->mwindow->edl;
461         LocalSession *lsn = edl->local_session;
462         double start = lsn->get_selectionstart();
463         double end = lsn->get_selectionend();
464         if( start >= end ) return 1;
465         if( lsn->inpoint_valid() && lsn->outpoint_valid() ) {
466                 lsn->set_inpoint(lsn->get_outpoint());
467                 lsn->unset_outpoint();
468         }
469         paste_text(line_entry->get_text(), start, end);
470         load_next_selection();
471         return 0;
472 }
473
474 int SWindowGUI::clear_selection()
475 {
476         EDL *edl = swindow->mwindow->edl;
477         double start = edl->local_session->get_selectionstart();
478         double end = edl->local_session->get_selectionend();
479         if( end > start )
480                 paste_text(0, start, end);
481         return 0;
482 }
483
484
485 ScriptPrev::ScriptPrev(SWindowGUI *gui, int x, int y)
486  : BC_GenericButton(x, y, _("Prev"))
487 {
488         sw_gui = gui;
489 }
490
491 ScriptPrev::~ScriptPrev()
492 {
493 }
494
495 int ScriptPrev::handle_event()
496 {
497         sw_gui->load_prev_selection();
498         return 1;
499 }
500
501 ScriptNext::ScriptNext(SWindowGUI *gui, int x, int y)
502  : BC_GenericButton(x, y, _("Next"))
503 {
504         sw_gui = gui;
505 }
506
507 ScriptNext::~ScriptNext()
508 {
509 }
510
511 int ScriptNext::handle_event()
512 {
513         sw_gui->load_next_selection();
514         return 1;
515 }
516
517 ScriptPaste::ScriptPaste(SWindowGUI *gui, int x, int y)
518  : BC_GenericButton(x, y, _("Paste"))
519 {
520         sw_gui = gui;
521 }
522
523 ScriptPaste::~ScriptPaste()
524 {
525 }
526
527 int ScriptPaste::handle_event()
528 {
529         sw_gui->paste_selection();
530         return 1;
531 }
532
533 ScriptClear::ScriptClear(SWindowGUI *gui, int x, int y)
534  : BC_GenericButton(x, y, _("Clear"))
535 {
536         sw_gui = gui;
537 }
538
539 ScriptClear::~ScriptClear()
540 {
541 }
542
543 int ScriptClear::handle_event()
544 {
545         sw_gui->clear_selection();
546         return 1;
547 }
548
549
550 ScriptLines::ScriptLines()
551 {
552         used = 0;
553         lines = 0;
554         text = new char[allocated = 256];
555         *text = 0;
556 }
557
558 ScriptLines::~ScriptLines()
559 {
560         delete [] text;
561 }
562
563 void ScriptLines::append(char *cp)
564 {
565         int len = strlen(cp);
566         if( allocated-used < len+1 ) {
567                 int isz = allocated + used + len;
568                 char *new_text = new char[isz];
569                 allocated = isz;
570                 memcpy(new_text, text, used);
571                 delete [] text;  text = new_text;
572         }
573         memcpy(text+used, cp, len);
574         text[used += len] = 0;
575         ++lines;
576 }
577
578 int ScriptLines::break_lines()
579 {
580         int line_limit = 60;
581         int limit2 = line_limit/2;
582         int limit4 = line_limit/4-2;
583         char *cp = text, *dp = cp+used;
584         int n;  char *bp, *ep, *pp, *sp;
585         for( lines=0; cp<dp; ++lines ) {
586                 // find end of line/buffer
587                 for( ep=cp; ep<dp && *ep!='\n'; ++ep );
588                 // delete trailing spaces
589                 for( sp=ep; sp>=cp && (!*sp || isspace(*sp)); --sp);
590                 ++sp;
591                 if( (n=ep-sp) > 0 ) {
592                         memmove(sp,ep,dp+1-ep);
593                         used -= n;  dp -= n;  ep -= n;
594                 }
595                 ++ep;
596                 // skip, if comment or title line
597                 if( *cp == '*' || *cp == '=' ) {
598                         cp = ep;  continue;
599                 }
600                 // delete leading spaces
601                 for( sp=cp; sp<ep && isspace(*sp); ++sp);
602                 if( (n=sp-cp) > 0 ) {
603                         memmove(cp,sp,dp+1-sp);
604                         used -= n;  dp -= n;  ep -= n;
605                 }
606                 // target about half remaining line, constrain line_limit
607                 if( (n=(ep-1-cp)/2) < limit2 || n > line_limit )
608                         n = line_limit;
609                 // search for last punct, last space before line_limit
610                 for( bp=cp, pp=sp=0; --n>=0 && cp<ep; ++cp ) {
611                         if( ispunct(*cp) && isspace(*(cp+1)) ) pp = cp;
612                         else if( isspace(*cp) ) sp = cp;
613                 }
614                 // line not empty
615                 if( cp < ep ) {
616                         // first, after punctuation
617                         if( pp && pp-bp >= limit4 )
618                                 cp = pp+1;
619                         // then, on spaces
620                         else if( sp ) {
621                                 cp = sp;
622                         }
623                         // last, on next space
624                         else {
625                                 while( cp<dp && !isspace(*cp) ) ++cp;
626                         }
627                         // add new line
628                         if( !*cp ) break;
629                         *cp++ = '\n';
630                 }
631         }
632         return lines;
633 }
634
635 int ScriptLines::get_text_rows()
636 {
637         int ret = 1, i=used;
638         for( char *cp=text; --i>=0; ++cp )
639                 if( *cp == '\n' ) ++ret;
640         return ret;
641 }
642
643 char *ScriptLines::get_text_row(int n)
644 {
645         char *cp = text;
646         if( !n ) return cp;
647         for( int i=used; --i>=0; ) {
648                 if( *cp++ != '\n' ) continue;
649                 if( --n <= 0 ) return cp;
650         }
651         return 0;
652 }
653
654 ScriptScroll::ScriptScroll(SWindowGUI *gui, int x, int y, int w)
655  : BC_ScrollBar(x, y, SCROLL_HORIZ, w, 0, 0, 0)
656 {
657         this->sw_gui = gui;
658 }
659
660 ScriptScroll::~ScriptScroll()
661 {
662 }
663
664 int ScriptScroll::handle_event()
665 {
666         int64_t pos = get_value();
667         sw_gui->set_script_pos(pos);
668         sw_gui->script_position->update(pos);
669         return 1;
670 }
671
672
673 ScriptPosition::ScriptPosition(SWindowGUI *gui, int x, int y, int w, int v, int mn, int mx)
674  : BC_TumbleTextBox(gui, v, mn, mx, x, y, w-BC_Tumbler::calculate_w())
675 {
676         this->sw_gui = gui;
677 }
678
679 ScriptPosition::~ScriptPosition()
680 {
681 }
682
683 int ScriptPosition::handle_event()
684 {
685         int64_t pos = atol(get_text());
686         sw_gui->set_script_pos(pos);
687         sw_gui->script_scroll->update_value(pos);
688         return 1;
689 }
690
691
692 ScriptEntry::ScriptEntry(SWindowGUI *gui, int x, int y, int w, int rows, char *text)
693  : BC_ScrollTextBox(gui, x, y, w, rows, text, -strlen(text))
694 {
695         this->sw_gui = gui;
696         this->ttext = text;
697 }
698
699 ScriptEntry::ScriptEntry(SWindowGUI *gui, int x, int y, int w, int rows)
700  : BC_ScrollTextBox(gui, x, y, w, rows,(char*)0, BCTEXTLEN)
701 {
702         this->sw_gui = gui;
703         this->ttext = 0;
704 }
705
706 ScriptEntry::~ScriptEntry()
707 {
708 }
709
710 void ScriptEntry::set_text(char *text, int isz)
711 {
712         if( !text || !*text ) return;
713         if( isz < 0 ) isz = strlen(text);
714         BC_ScrollTextBox::set_text(text, isz);
715         ttext = text;
716 }
717
718
719 int SWindowGUI::load_script_line(FILE *fp)
720 {
721         char line[8192];
722         for(;;) { // skip blank lines
723                 char *cp = fgets(line,sizeof(line),fp);
724                 if( !cp ) return 1;
725                 ++script_line_no;
726                 while( *cp && isspace(*cp) ) ++cp;
727                 if( *cp ) break;
728         }
729
730         ScriptLines *entry = new ScriptLines();
731         script.append(entry);
732
733         for(;;) { // load non-blank lines
734                 //int len = strlen(line);
735                 //if( line[len-1] == '\n' ) line[len-1] = ' ';
736                 entry->append(line);
737                 char *cp = fgets(line,sizeof(line),fp);
738                 if( !cp ) break;
739                 ++script_line_no;
740                 while( *cp && isspace(*cp) ) ++cp;
741                 if( !*cp ) break;
742         }
743         script_text_lines += entry->break_lines();
744         return 0;
745 }
746
747 void SWindowGUI::load_script()
748 {
749         FILE *fp = fopen(script_path,"r");
750         if( !fp ) {
751                 char string[BCTEXTLEN];
752                 sprintf(string,_("cannot open: \"%s\"\n%s"),script_path,strerror(errno));
753                 MainError::show_error(string);
754                 return;
755         }
756
757         script.remove_all_objects();
758         script_line_no = 0;
759         script_text_lines = 0;
760         while( !load_script_line(fp) );
761         char value[64];
762         sprintf(value,"%ld",ftell(fp));
763         script_filesz->update(value);
764         sprintf(value,"%ld",script_line_no);
765         script_lines->update(value);
766         sprintf(value,"%d",script.size());
767         script_entries->update(value);
768         sprintf(value,"%ld",script_text_lines);
769         script_texts->update(value);
770         int hw = 2*script_scroll->get_h();
771         script_scroll->update_length(script.size(), script_entry_no, hw, 0);
772         script_position->update(script_entry_no);
773         script_position->set_boundaries((int64_t)0, (int64_t)script.size()-1);
774
775         fclose(fp);
776 }
777
778 void SWindowGUI::save_spumux_data()
779 {
780         char filename[BCTEXTLEN], track_title[BCTEXTLEN];
781         snprintf(filename,sizeof(filename),"%s",script_path);
782         filename[sizeof(filename)-1] = 0;
783         char *bp = strrchr(filename,'/');
784         if( !bp ) bp = filename; else ++bp;
785         char *ext = strrchr(bp, '.');
786         if( !ext ) ext = bp + strlen(bp);
787         int len = sizeof(filename)-1 - (ext-filename);
788
789         Tracks *tracks = swindow->mwindow->edl->tracks;
790         for( Track *track=tracks->first; track; track=track->next ) {
791                 if( track->data_type != TRACK_SUBTITLE ) continue;
792                 if( !track->record ) continue;
793                 char *cp = track_title;
794                 for( char *bp=track->title; *bp; ++bp,++cp )
795                         *cp = !isalnum(*bp) ? '_' : *bp;
796                 *cp = 0;
797                 snprintf(ext,len,"-%s.udvd",track_title);
798                 FILE *fp = fopen(filename, "w");
799                 int64_t start = 0;
800                 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
801                         SEdit *sedit = (SEdit *)edit;
802                         if( sedit->length > 0 ) {
803                                 int64_t end = start + sedit->length;
804                                 char *text = sedit->get_text();
805                                 if( *text ) {
806                                         fprintf(fp, "{%ld}{%ld}%s\n", start, end-1, text);
807                                 }
808                                 start = end;
809                         }
810                 }
811                 fclose(fp);
812         }
813 }
814
815
816
817 SWindow::SWindow(MWindow *mwindow)
818  : Thread(1, 0, 0)
819 {
820         this->mwindow = mwindow;
821         window_lock = new Mutex("SWindow::window_lock");
822         swin_lock = new Condition(0,"SWindow::swin_lock");
823         gui = 0;
824         done = 1;
825         gui_done = 1;
826
827         start();
828 }
829
830 SWindow::~SWindow()
831 {
832         stop();
833         delete gui;
834         delete swin_lock;
835         delete window_lock;
836 }
837
838
839 void SWindow::start()
840 {
841         if( !Thread::running() ) {
842                 done = 0;
843                 Thread::start();
844         }
845 }
846
847 void SWindow::stop()
848 {
849         if( Thread::running() ) {
850                 done = 1;
851                 swin_lock->unlock();
852                 window_lock->lock("SWindow::stop");
853                 if( gui ) gui->stop(1);
854                 window_lock->unlock();
855                 Thread::cancel();
856         }
857         Thread::join();
858 }
859
860 void SWindow::run()
861 {
862         int root_w = mwindow->gui->get_root_w(1);
863         int root_h = mwindow->gui->get_root_h(1);
864
865         while( !done ) {
866                 swin_lock->reset();
867                 swin_lock->lock();
868                 if( done ) break;
869                 int x = mwindow->session->swindow_x;
870                 int y = mwindow->session->swindow_y;
871                 int w = mwindow->session->swindow_w;
872                 int h = mwindow->session->swindow_h;
873                 if( w < 600 ) w = 600;
874                 if( h < 500 ) h = 500;
875                 int scr_x = mwindow->gui->get_screen_x(1, -1);
876                 int scr_w = mwindow->gui->get_screen_w(1, -1);
877                 if( x < scr_x ) x = scr_x;
878                 if( x > scr_x+scr_w ) x = scr_x+scr_w;
879                 if( x+w > root_w ) x = root_w - w;
880                 if( x < 0 ) { x = 0;  w = scr_w; }
881                 if( y+h > root_h ) y = root_h - h;
882                 if( y < 0 ) { y = 0;  h = root_h; }
883                 if( y+h > root_h ) h = root_h-y;
884                 mwindow->session->swindow_x = x;
885                 mwindow->session->swindow_y = y;
886                 mwindow->session->swindow_w = w;
887                 mwindow->session->swindow_h = h;
888
889                 gui_done = 0;
890                 gui = new SWindowGUI(this, x, y, w, h);
891                 gui->lock_window("ChannelInfo::gui_create_objects");
892                 gui->load_defaults();
893                 gui->create_objects();
894                 gui->set_icon(mwindow->theme->get_image("record_icon"));
895                 gui->reposition_window(x, y);
896                 gui->resize_event(w, h);
897                 gui->load();
898                 gui->show_window();
899                 gui->unlock_window();
900
901                 int result = gui->run_window();
902                 if( !result ) {
903                         gui->save_spumux_data();
904                 }
905
906                 window_lock->lock("ChannelInfo::run 1");
907                 gui->save_defaults();
908                 delete gui;  gui = 0;
909                 window_lock->unlock();
910         }
911 }
912
913
914
915
916 void SWindow::run_swin()
917 {
918         window_lock->lock("SWindow::run_swin");
919         if( gui ) {
920                 gui->lock_window("SWindow::run_swin");
921                 gui->raise_window();
922                 gui->unlock_window();
923         }
924         else
925                 swin_lock->unlock();
926         window_lock->unlock();
927 }
928
929 void SWindow::paste_subttl()
930 {
931         window_lock->lock("SWindow::paste_subttl 1");
932         if( gui ) {
933                 gui->lock_window("SWindow::paste_subttl 2");
934                 gui->paste_selection();
935                 gui->unlock_window();
936         }
937         window_lock->unlock();
938 }
939
940 int SWindow::update_selection()
941 {
942         window_lock->lock("SWindow::update_selection 1");
943         if( gui ) {
944                 gui->lock_window("SWindow::update_selection 2");
945                 gui->update_selection();
946                 gui->unlock_window();
947         }
948         window_lock->unlock();
949         return 0;
950 }
951
952
953
954 SubttlSWin::SubttlSWin(MWindow *mwindow)
955  : BC_MenuItem(_("SubTitle..."), _("Alt-y"), 'y')
956 {
957         set_alt();
958         this->mwindow = mwindow;
959 }
960
961 SubttlSWin::~SubttlSWin()
962 {
963 }
964
965 int SubttlSWin::handle_event()
966 {
967         mwindow->gui->swindow->run_swin();
968         return 1;
969 };
970