Credit Andrew - fix vorbis audio which was scratchy and ensure aging plugin does...
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / channelinfo.C
1 #ifdef HAVE_DVB
2
3 #include "asset.h"
4 #include "batch.h"
5 #include "bctimer.h"
6 #include "channelinfo.h"
7 #include "channel.h"
8 #include "channeldb.h"
9 #include "condition.h"
10 #include "cstrdup.h"
11 #include "devicedvbinput.h"
12 #include "edl.h"
13 #include "edlsession.h"
14 #include "keys.h"
15 #include "mainerror.h"
16 #include "mainmenu.h"
17 #include "mainsession.h"
18 #include "mwindow.h"
19 #include "mwindowgui.h"
20 #include "mutex.h"
21 #include "bcmenuitem.h"
22 #include "record.h"
23 #include "recordconfig.h"
24 #include "theme.h"
25 #include "videodevice.h"
26 #include "videoconfig.h"
27 #include "videodevice.h"
28 #include "libzmpeg3.h"
29
30 #include <ctype.h>
31
32 static inline int min(int a,int b) { return a<b ? a : b; }
33
34 static inline int max(int a,int b) { return a>b ? a : b; }
35
36
37 ChanSearch::ChanSearch(ChannelInfo *iwindow)
38  : Thread(1, 0, 0)
39 {
40         this->iwindow = iwindow;
41         window_lock = new Mutex("ChanSearch::window_lock");
42         gui = 0;
43 }
44
45 ChanSearch::~ChanSearch()
46 {
47         stop();
48         delete gui;
49         delete window_lock;
50 }
51
52 void ChanSearch::start()
53 {
54         window_lock->lock("ChanSearch::start1");
55         if( Thread::running() ) {
56                 gui->lock_window("ChanSearch::start2");
57                 gui->raise_window();
58                 gui->unlock_window();
59         }
60         else {
61                 delete gui;
62                 gui = new ChanSearchGUI(this);
63                 Thread::start();
64         }
65         window_lock->unlock();
66 }
67
68 void ChanSearch::stop()
69 {
70         if( Thread::running() ) {
71                 window_lock->lock("ChanSearch::stop");
72                 if( gui ) gui->set_done(1);
73                 window_lock->unlock();
74                 Thread::cancel();
75         }
76         Thread::join();
77 }
78
79 void ChanSearch::run()
80 {
81         gui->lock_window("ChanSearch::run");
82         gui->create_objects();
83         gui->unlock_window();
84         gui->run_window();
85         window_lock->lock("ChanSearch::stop");
86         delete gui;  gui = 0;
87         window_lock->unlock();
88 }
89
90
91 ChanSearchTitleText::ChanSearchTitleText(ChanSearchGUI *gui, int x, int y)
92  : BC_CheckBox(x, y, &gui->title_text_enable, _("titles"))
93 {
94         this->gui = gui;
95 }
96
97 ChanSearchTitleText::~ChanSearchTitleText()
98 {
99 }
100
101 int ChanSearchTitleText::handle_event()
102 {
103         gui->title_text_enable = get_value();
104         if( !gui->title_text_enable ) gui->info_text->update(1);
105         return 1;
106 }
107
108
109 ChanSearchInfoText::ChanSearchInfoText(ChanSearchGUI *gui, int x, int y)
110  : BC_CheckBox(x, y, &gui->info_text_enable, _("info"))
111 {
112         this->gui = gui;
113 }
114
115 ChanSearchInfoText::~ChanSearchInfoText()
116 {
117 }
118
119 int ChanSearchInfoText::handle_event()
120 {
121         gui->info_text_enable = get_value();
122         if( !gui->info_text_enable ) gui->title_text->update(1);
123         return 1;
124 }
125
126
127 ChanSearchMatchCase::ChanSearchMatchCase(ChanSearchGUI *gui, int x, int y)
128  : BC_CheckBox(x, y, &gui->match_case_enable, _("match case"))
129 {
130         this->gui = gui;
131 }
132
133 ChanSearchMatchCase::~ChanSearchMatchCase()
134 {
135 }
136
137 int ChanSearchMatchCase::handle_event()
138 {
139         gui->match_case_enable = get_value();
140         return 1;
141 }
142
143
144 ChanSearchText::ChanSearchText(ChanSearchGUI *gui, int x, int y, int w)
145  : BC_TextBox(x, y, w, 1, "")
146 {
147         this->gui = gui;
148 }
149
150 ChanSearchText::~ChanSearchText()
151 {
152 }
153
154
155 int ChanSearchText::handle_event()
156 {
157         return 1;
158 }
159
160 int ChanSearchText::keypress_event()
161 {
162         switch(get_keypress())
163         {
164         case RETURN:
165                 gui->search();
166                 return 1;
167         }
168
169         return BC_TextBox::keypress_event();
170 }
171
172
173 ChanSearchStart::ChanSearchStart(ChanSearchGUI *gui, int x, int y)
174  : BC_GenericButton(x, y, _("Search"))
175 {
176         this->gui = gui;
177 }
178
179 ChanSearchStart::~ChanSearchStart()
180 {
181 }
182
183 int ChanSearchStart::handle_event()
184 {
185         gui->search();
186         return 1;
187 }
188
189
190 ChanSearchCancel::ChanSearchCancel(ChanSearchGUI *gui, int x, int y)
191  : BC_CancelButton(x, y)
192 {
193         this->gui = gui;
194 }
195
196 ChanSearchCancel::~ChanSearchCancel()
197 {
198 }
199
200 int ChanSearchCancel::handle_event()
201 {
202         gui->set_done(1);
203         return 1;
204 }
205
206
207 ChanSearchList::ChanSearchList(ChanSearchGUI *gui, int x, int y, int w, int h)
208  : BC_ListBox(x, y, w, h, LISTBOX_TEXT, &gui->search_items[0],
209                 &gui->search_column_titles[0], &gui->search_column_widths[0],
210                 lengthof(gui->search_items))
211 {
212         this->gui = gui;
213         set_sort_column(gui->sort_column);
214         set_sort_order(gui->sort_order);
215         set_allow_drag_column(1);
216 }
217
218 ChanSearchList::~ChanSearchList()
219 {
220 }
221
222 int ChanSearchList::handle_event()
223 {
224         if( get_double_click() ) {
225                 ChannelPanel *panel = gui->panel;
226                 panel->lock_window("ChanSearchList::handle_event");
227                 ChannelEvent *item = gui->highlighted_event;
228                 if( item ) {
229                         item->text_color(-1);
230                         item->draw_face();
231                 }
232                 int i = get_highlighted_item();
233                 item = gui->search_results.get(i);
234                 int x = item->x0-panel->x0 - panel->frame_w/2 + item->get_w()/2;
235                 if( x < 0 ) x = 0;
236                 int w = panel->x1 - panel->x0;
237                 if( x > w ) x = w;
238                 panel->time_line_scroll->update_value(x);
239                 panel->set_x_scroll(x);
240                 item->text_color(YELLOW);
241                 item->draw_face();
242                 gui->highlighted_event = item;
243                 panel->unlock_window();
244         }
245         return 1;
246 }
247
248 int ChanSearchList::sort_order_event()
249 {
250         gui->sort_events(get_sort_column(), get_sort_order());
251         return 1;
252 }
253
254 int ChanSearchList::move_column_event()
255 {
256         gui->move_column(get_from_column(), get_to_column());
257         return 1;
258 }
259
260 void ChanSearchGUI::create_objects()
261 {
262         int xs10 = xS(10), xs20 = xS(20);
263         int ys5 = yS(5), ys10 = yS(10);
264         lock_window("ChanSearchGUI::create_objects");
265         int pady = BC_TextBox::calculate_h(this, MEDIUMFONT, 0, 1) + ys5;
266         int padx = BC_Title::calculate_w(this, (char*)"X", MEDIUMFONT);
267         int x = padx/2, y = pady/4;
268         BC_Title *title = new BC_Title(text_x, text_y, _("Text:"), MEDIUMFONT, YELLOW);
269         add_subwindow(title);  x += title->get_w();
270         text_x = x;  text_y = y;
271         search_text = new ChanSearchText(this, x, y, get_w()-x-xs10);
272         add_subwindow(search_text);
273         x = padx;  y += pady + ys5;
274         title_text = new ChanSearchTitleText(this, x, y);
275         add_subwindow(title_text);  x += title_text->get_w() + padx;
276         info_text = new ChanSearchInfoText(this, x, y);
277         add_subwindow(info_text);  x += title_text->get_w() + padx;
278         match_case = new ChanSearchMatchCase(this, x, y);
279         add_subwindow(match_case); x += match_case->get_w() + padx;
280         results_x = x + xs20;  results_y = y + ys5;
281         results = new BC_Title(results_x, results_y, " ", MEDIUMFONT, YELLOW);
282         add_subwindow(results);
283         x = padx;  y += pady + ys5;
284         search_x = xs10;
285         search_y = get_h() - BC_GenericButton::calculate_h() - ys10;
286         search_start = new ChanSearchStart(this, search_x, search_y);
287         add_subwindow(search_start);
288         cancel_w = ChanSearchCancel::calculate_w();
289         cancel_h = ChanSearchCancel::calculate_h();
290         cancel_x = get_w() - cancel_w - xs10;
291         cancel_y = get_h() - cancel_h - ys10;
292         cancel = new ChanSearchCancel(this, cancel_x, cancel_y);
293         add_subwindow(cancel);
294         list_x = x;  list_y = y;
295         int list_w = get_w()-xs10 - list_x;
296         int list_h = min(search_y, cancel_y)-ys10 - list_y;
297         search_list = new ChanSearchList(this, list_x, list_y, list_w, list_h);
298         add_subwindow(search_list);
299         search_list->show_window();
300         int clktip_x = list_x;
301         int clktip_y = list_y + list_h + ys5;
302         click_tip = new BC_Title(clktip_x, clktip_y, _("dbl clk row to find title"));
303         add_subwindow(click_tip);
304
305         set_icon(iwindow->mwindow->theme->get_image("record_icon"));
306         search_text->activate();
307         unlock_window();
308 }
309
310 #define CSW_W xS(500)
311 #define CSW_H yS(300)
312
313 ChanSearchGUI::ChanSearchGUI(ChanSearch *cswindow)
314  : BC_Window(_(PROGRAM_NAME ": ChanSearch"),
315         cswindow->iwindow->gui->get_abs_cursor_x(1) - CSW_W/2,
316         cswindow->iwindow->gui->get_abs_cursor_y(1) - CSW_H/2,
317         CSW_W, CSW_H, xS(400), yS(300))
318 {
319         this->cswindow = cswindow;
320         this->iwindow = cswindow->iwindow;
321         this->panel = cswindow->iwindow->gui->panel;
322
323         search_text = 0;
324         title_text = 0;
325         info_text = 0;
326         match_case = 0;
327         search_start = 0;
328         search_list = 0;
329         cancel = 0;
330         results = 0;
331
332         search_x = search_y = text_x = text_y = 0;
333         cancel_x = cancel_y = cancel_w = cancel_h = 0;
334         list_x = list_y = list_w = list_h = 0;
335         results_x = results_y = 0;
336         sort_column = sort_order = 0;
337
338         title_text_enable = 1;
339         info_text_enable = 0;
340         match_case_enable = 0;
341         highlighted_event = 0;
342
343         search_columns[0] = 0;
344         search_columns[1] = 1;
345         search_columns[2] = 2;
346         search_column_titles[0] = _("Source");
347         search_column_titles[1] = C_("Title");
348         search_column_titles[2] = _("Start time");
349         search_column_widths[0] = xS(120);
350         search_column_widths[2] = xS(120);
351         search_column_widths[1] = get_w()-search_column_widths[0]-search_column_widths[2]-xS(32);
352 // *** CONTEXT_HELP ***
353         context_help_set_keyword("Capture at some Future Time via Batch");
354 }
355
356 ChanSearchGUI::~ChanSearchGUI()
357 {
358
359         ChannelEvent *item = highlighted_event;
360         if( item ) {
361                 panel->lock_window("ChanSearchGUI::~ChanSearchGUI");
362                 item->text_color(-1);
363                 item->draw_face();
364                 panel->unlock_window();
365         }
366         for( int i=0; i<lengthof(search_items); ++i )
367                 search_items[i].remove_all_objects();
368 }
369
370 int ChanSearchGUI::resize_event(int w, int h)
371 {
372         int xs5 = xS(5), xs10 = xS(10);
373         int ys10 = yS(10);
374         search_text->reposition_window(text_x, text_y, w-text_x-10);
375         int cancel_x = w - BC_CancelButton::calculate_w() - xs10;
376         int cancel_y = h - BC_CancelButton::calculate_h() - ys10;
377         cancel->reposition_window(cancel_x, cancel_y);
378         search_x = xs10;
379         search_y = h - BC_GenericButton::calculate_h() - ys10;
380         search_start->reposition_window(search_x, search_y);
381         int list_w = w-xs10 - list_x;
382         int list_h = min(search_y, cancel_y)-ys10 - list_y;
383         search_column_widths[1] = w-search_column_widths[0]-search_column_widths[2]-xS(32);
384         search_list->reposition_window(list_x, list_y, list_w, list_h);
385         int clktip_x = list_x;
386         int clktip_y = list_y + list_h + xs5;
387         click_tip->reposition_window(clktip_x, clktip_y);
388         update();
389         return 1;
390 }
391
392 int ChanSearchGUI::close_event()
393 {
394         set_done(1);
395         return 1;
396 }
397
398 int ChanSearchGUI::search(const char *sp)
399 {
400         char const *tp = search_text->get_text();
401         int n = strlen(tp);
402         for( const char *cp=sp; *cp!=0; ++cp ) {
403                 if( match_case_enable ? !strncmp(tp, cp, n) :
404                         !strncasecmp(tp, cp, n) ) return 1;
405         }
406         return 0;
407 }
408
409 void ChanSearchGUI::search()
410 {
411         search_results.remove_all();
412
413         int n = panel->channel_event_items.size();
414         for( int i=0; i<n; ++i ) {
415                 ChannelEvent *item = panel->channel_event_items.get(i);
416                 if( (title_text_enable && search(item->get_text())) ||
417                     (info_text_enable  && search(item->get_tooltip()) ) )
418                         search_results.append(item);
419         }
420
421         update();
422 }
423
424 void ChanSearchGUI::update()
425 {
426
427         for( int i=0; i<lengthof(search_items); ++i )
428                 search_items[i].remove_all_objects();
429
430         char text[BCTEXTLEN];
431         int n = search_results.size();
432         for( int i=0; i<n; ++i ) {
433                 ChannelEvent *item = search_results.get(i);
434                 for( int k=0; k<lengthof(search_columns); ++k ) {
435                         const char *cp = 0;
436                         switch( search_columns[k] ) {
437                         case 0:  cp = item->channel->title;  break;
438                         case 1:  cp = item->get_text();      break;
439                         case 2: {
440                                 struct tm stm;  localtime_r(&item->start_time,&stm);
441                                 double seconds = stm.tm_hour*3600 + stm.tm_min*60 + stm.tm_sec;
442                                 Units::totext(text, seconds, TIME_HMS3);
443                                 cp = text;                   break; }
444                         }
445                         if( cp ) search_items[k].append(new BC_ListBoxItem(cp, LTYELLOW));
446                 }
447         }
448
449         search_list->update(search_items, &search_column_titles[0],
450                         &search_column_widths[0], lengthof(search_items));
451         sprintf(text, _("%d found"), n);
452         results->update(text);
453 }
454
455
456 #define CmprFn(nm,key) int ChanSearchGUI:: \
457 cmpr_##nm(const void *a, const void *b) { \
458   ChannelEvent *&ap = *(ChannelEvent **)a; \
459   ChannelEvent *&bp = *(ChannelEvent **)b; \
460   int n = key; if( !n ) n = ap->no-bp->no; \
461   return n; \
462 }
463
464 CmprFn(Text_dn, strcasecmp(ap->get_text(), bp->get_text()))
465 CmprFn(text_dn, strcmp(ap->get_text(), bp->get_text()))
466 CmprFn(Text_up, strcasecmp(bp->get_text(), ap->get_text()))
467 CmprFn(text_up, strcmp(bp->get_text(), ap->get_text()))
468 CmprFn(time_dn, ap->start_time - bp->start_time)
469 CmprFn(time_up, bp->start_time - ap->start_time)
470 CmprFn(Title_dn, strcasecmp(ap->channel->title, bp->channel->title))
471 CmprFn(title_dn, strcmp(ap->channel->title, bp->channel->title))
472 CmprFn(Title_up, strcasecmp(bp->channel->title, ap->channel->title))
473 CmprFn(title_up, strcmp(bp->channel->title, ap->channel->title))
474
475
476 void ChanSearchGUI::sort_events(int column, int order)
477 {
478         sort_column = column;  sort_order = order;
479         int n = search_results.size();
480         if( !n ) return;
481         ChannelEvent **events = &search_results.values[0];
482         for( int i=0; i<n; ++i ) events[i]->no = i;
483
484         int(*cmpr)(const void *, const void *) = 0;
485         switch( search_columns[sort_column] ) {
486         case 0: cmpr = match_case_enable ?
487                         (sort_order ? cmpr_Title_up : cmpr_Title_dn) :
488                         (sort_order ? cmpr_title_up : cmpr_title_dn) ;
489                 break;
490         case 1: cmpr = match_case_enable ?
491                         (sort_order ? cmpr_Text_up : cmpr_Text_dn) :
492                         (sort_order ? cmpr_text_up : cmpr_text_dn) ;
493                 break;
494         case 2: cmpr = sort_order ? cmpr_time_up : cmpr_time_dn;
495                 break;
496         }
497
498         if( cmpr ) {
499                 ChannelEvent **events = &search_results.values[0];
500                 qsort(events, n, sizeof(*events), cmpr);
501         }
502         update();
503 }
504
505 void ChanSearchGUI::move_column(int src, int dst)
506 {
507         if( src == dst ) return;
508         int src_column = search_columns[src];
509         const char *src_column_title = search_column_titles[src];
510         int src_column_width = search_column_widths[src];
511         if( src < dst ) {
512                 for( int i=src; i<dst; ++i ) {
513                         search_columns[i] = search_columns[i+1];
514                         search_column_titles[i] = search_column_titles[i+1];
515                         search_column_widths[i] = search_column_widths[i+1];
516                 }
517         }
518         else {
519                 for( int i=src; i>dst; --i  ) {
520                         search_columns[i] = search_columns[i-1];
521                         search_column_titles[i] = search_column_titles[i-1];
522                         search_column_widths[i] = search_column_widths[i-1];
523                 }
524         }
525         search_columns[dst] = src_column;
526         search_column_titles[dst] = src_column_title;
527         search_column_widths[dst] = src_column_width;
528         update();
529 }
530
531
532 ChannelProgress::ChannelProgress(ChannelInfoGUI *gui,
533                 int x, int y, int w, int h, int len)
534  : Thread(1, 0, 0),
535    BC_SubWindow(x, y, w, h)
536 {
537         this->gui = gui;
538         eta = 0;
539         bar = 0;
540         done = 0;
541         length = len;
542         value = 0.;
543
544         eta_timer = new Timer;
545 }
546
547 ChannelProgress::~ChannelProgress()
548 {
549         stop();
550         delete eta_timer;
551 }
552
553 void ChannelProgress::create_objects()
554 {
555         int w = BC_Title::calculate_w(gui, (char*)"XXXXX", MEDIUMFONT);
556         eta = new BC_Title(0, 0, "+0:00", MEDIUMFONT, -1, 0, w);
557         add_subwindow(eta);
558         int x = eta->get_w();
559         bar = new BC_ProgressBar(x, 0, get_w()-x, length);
560         add_subwindow(bar);
561         start();
562 }
563
564 void ChannelProgress::start()
565 {
566         done = 0;
567         eta_timer->update();
568         Thread::start();
569 }
570
571 void ChannelProgress::stop()
572 {
573         if( Thread::running() ) {
574                 done = 1;
575                 Thread::cancel();
576         }
577         Thread::join();
578 }
579
580 void ChannelProgress::run()
581 {
582         gui->init_wait();
583         while( !done ) {
584                 if( update() ) break;
585                 enable_cancel();
586                 Timer::delay(500);
587                 disable_cancel();
588         }
589 }
590
591 int ChannelProgress::update()
592 {
593         double elapsed = (double)eta_timer->get_scaled_difference(1000) / 1000.;
594         double estimate = value < 1 ? 0 : (length/value-1)*elapsed;
595         char text[BCTEXTLEN], *cp = &text[0];
596         Units::totext(cp, estimate, TIME_MS1);
597         gui->lock_window("ChannelProgress::update");
598         eta->update(cp);
599         bar->update(value);
600         gui->unlock_window();
601         return value < length ? 0 : 1;
602 }
603
604
605
606 TimeLineItem::TimeLineItem(ChannelPanel *panel, int x, int y, char *text)
607  : BC_Title(x-panel->x_scroll, 0, text, MEDIUMFONT,
608                 x==panel->x_now ? GREEN : YELLOW)
609 {
610         this->panel = panel;
611         x0 = x;  y0 = y;
612 }
613
614 TimeLineItem::~TimeLineItem()
615 {
616 }
617
618
619 TimeLine::TimeLine(ChannelPanel *panel)
620  : BC_SubWindow(panel->frame_x, 0, panel->frame_w, panel->path_h)
621 {
622         this->panel = panel;
623 }
624
625 TimeLine::~TimeLine()
626 {
627 }
628
629 int TimeLine::resize_event(int w, int h)
630 {
631         clear_box(0,0, w,h);
632         return 1;
633 }
634
635
636 ChannelDataItem::ChannelDataItem(ChannelPanel *panel, int x, int y, int w,
637         int color, const char *text)
638  : BC_Title(x, y-panel->y_scroll, text, MEDIUMFONT, color)
639 {
640         this->panel = panel;
641         x0 = x;  y0 = y;
642         tip_info = 0;
643         set_force_tooltip(1);
644 }
645
646 ChannelDataItem::~ChannelDataItem()
647 {
648         delete [] tip_info;
649 }
650
651 int ChannelDataItem::repeat_event(int64_t duration)
652 {
653         if( tip_info && cursor_above() &&
654                 duration == get_resources()->tooltip_delay ) {
655                 show_tooltip();
656                 return 1;
657         }
658         return 0;
659 }
660
661 void ChannelDataItem::set_tooltip(const char *tip)
662 {
663         BC_Title::set_tooltip(tip_info = tip ? cstrdup(tip) : 0);
664 }
665
666
667 ChannelData::ChannelData(ChannelPanel *panel, int x, int y, int w, int h)
668  : BC_SubWindow(x, y, w, h, LTBLACK)
669 {
670         this->panel = panel;
671 }
672
673 ChannelData::~ChannelData()
674 {
675 }
676
677 int ChannelData::resize_event(int w, int h)
678 {
679         clear_box(0,0, w,h);
680         return 1;
681 }
682
683
684 ChannelScroll::ChannelScroll(ChannelPanel *panel, int x, int y, int h)
685  : BC_ScrollBar(x, y, SCROLL_VERT, h, 0, 0, 0)
686 {
687         this->panel = panel;
688 }
689
690 ChannelScroll::~ChannelScroll()
691 {
692 }
693
694 int ChannelScroll::handle_event()
695 {
696         panel->set_y_scroll(get_value());
697         return 1;
698 }
699
700
701 TimeLineScroll::TimeLineScroll(ChannelPanel *panel, int x, int y, int w)
702  : BC_ScrollBar(x, y, SCROLL_HORIZ, w, 0, 0, 0)
703 {
704         this->panel = panel;
705 }
706
707 TimeLineScroll::~TimeLineScroll()
708 {
709 }
710
711 int TimeLineScroll::handle_event()
712 {
713         panel->set_x_scroll(get_value());
714         return 1;
715 }
716
717
718 ChannelEventLine::ChannelEventLine(ChannelPanel *panel,
719         int x, int y, int w, int h, int color)
720  : BC_SubWindow(0, y, w, h, color)
721 {
722         this->panel = panel;
723         x0 = x;  y0 = y;
724 }
725
726 ChannelEventLine::~ChannelEventLine()
727 {
728 }
729
730 void ChannelEventLine::resize(int w, int h)
731 {
732         resize_window(w, h);
733 }
734
735
736 ChannelEvent::ChannelEvent(ChannelEventLine *channel_line, Channel *channel,
737         time_t start_time, time_t end_time, int x, int y, int w, const char *text)
738  : BC_GenericButton( x-channel_line->panel->x_scroll,
739                 y-channel_line->panel->y_scroll, w, text)
740 {
741         this->channel_line = channel_line;
742         this->channel = channel;
743         this->start_time = start_time;
744         this->end_time = end_time;
745         x0 = x;  y0 = y;  no = 0;
746         tip_info = 0;
747         set_force_tooltip(1);
748 }
749
750 ChannelEvent::~ChannelEvent()
751 {
752         delete [] tip_info;
753 }
754
755 int ChannelEvent::handle_event()
756 {
757         ChannelInfoGUI *gui = channel_line->panel->gui;
758         Batch *batch = gui->batch_bay->get_editing_batch();
759         char *path = batch->asset->path;
760         int len = sizeof(batch->asset->path)-1;
761         const char *text = get_text();
762         const char *dir = gui->channel_dir->get_directory();
763         int i = 0;
764         while( i<len && *dir ) path[i++] = *dir++;
765         if( i > 0 && dir[i-1] != '/' ) path[i++] = '/';
766         while( i<len && *text ) {
767                 int ch = *text++;
768                 if( !isalnum(ch) ) ch = '_';
769                 path[i++] = ch;
770         }
771         if( i < len ) path[i++] = '.';
772         if( i < len ) path[i++] = 't';
773         if( i < len ) path[i++] = 's';
774         path[i] = 0;
775         int early = (int)gui->early_time->get_time();
776         int late = (int)gui->late_time->get_time();
777         time_t st = start_time + early;
778         struct tm stm;  localtime_r(&st,&stm);
779         batch->record_mode = RECORD_TIMED;
780         batch->enabled = 1;
781         batch->start_day = stm.tm_wday;
782         batch->start_time = stm.tm_hour*3600 + stm.tm_min*60 + stm.tm_sec;
783         batch->duration = end_time - start_time - early + late;
784         batch->channel = channel;
785         gui->batch_bay->update_batches();
786         gui->update_channel_tools();
787         return 1;
788 }
789
790 void ChannelEvent::set_tooltip(const char *tip)
791 {
792         BC_GenericButton::set_tooltip(tip_info = tip ? cstrdup(tip) : 0);
793 }
794
795
796 ChannelFrame::ChannelFrame(ChannelPanel *panel)
797  : BC_SubWindow(panel->frame_x, panel->frame_y,
798                 panel->frame_w, panel->frame_h, BLACK)
799 {
800         this->panel = panel;
801 }
802
803 ChannelFrame::~ChannelFrame()
804 {
805 }
806
807
808 void ChannelPanel::create_objects()
809 {
810         iwd = BC_ScrollBar::get_span(SCROLL_VERT);
811         iht = BC_ScrollBar::get_span(SCROLL_HORIZ);
812
813         frame_x = path_w;  frame_y = path_h;
814         frame_w = iwindow_w-frame_x - iwd;
815         frame_h = iwindow_h-frame_y - iht;
816
817         time_line = new TimeLine(this);
818         add_subwindow(time_line);
819         time_line_scroll = new TimeLineScroll(this, frame_x, iwindow_h-iht, frame_w);
820         add_subwindow(time_line_scroll);
821         channel_data = new ChannelData(this, 0, frame_y, path_w, frame_h);
822         add_subwindow(channel_data);
823         channel_frame = new ChannelFrame(this);
824         add_subwindow(channel_frame);
825         channel_scroll = new ChannelScroll(this, iwindow_w-iwd, frame_y, frame_h);
826         add_subwindow(channel_scroll);
827 }
828
829 ChannelPanel::ChannelPanel(ChannelInfoGUI *gui,
830         int x, int y, int w, int h)
831  : BC_SubWindow(x, y, w, h)
832 {
833         this->gui = gui;
834         gettimeofday(&tv, &tz);
835         st_org = ((tv.tv_sec+1800-1) / 1800) * 1800 - 1800;
836         x_now = (tv.tv_sec-st_org)/1800 * hhr_w;
837         path_w = gui->path_w;  path_h = gui->path_h;
838         x0 = y0 = x1 = y1 = t0 = t1 = 0;
839         x_scroll = y_scroll = 0;
840         x_moved = y_moved = 0;
841         iwindow_w = w;  iwindow_h = h;
842         hhr_w = BC_GenericButton::calculate_w(gui, (char*)"XXXXXXXX") + 5;
843 }
844
845 ChannelPanel::~ChannelPanel()
846 {
847 }
848
849 ChannelEventLine *ChannelPanel::NewChannelLine(int y, int h, int color)
850 {
851         ChannelEventLine *channel_line =
852                  new ChannelEventLine(this, 0, y, frame_w, h, color);
853         channel_frame->add_subwindow(channel_line);
854         channel_line_items.append(channel_line);
855         return channel_line;
856 }
857
858 void ChannelPanel::resize(int w, int h)
859 {
860         frame_w = (iwindow_w=w) - frame_x - iwd;
861         frame_h = (iwindow_h=h) - frame_y - iht;
862         time_line->resize_window(frame_w, path_h);
863         time_line_scroll->reposition_window(frame_x, frame_y+frame_h, frame_w);
864         time_line_scroll->update_length(x1-x0, x_scroll-x0, frame_w,0);
865         time_line_scroll->update_value(-x0);
866         channel_data->resize_window(path_w, frame_h);
867         channel_frame->resize_window(frame_w, frame_h);
868         int nitems = channel_line_items.size();
869         for( int i=0; i<nitems; ++i )
870                 channel_line_items.values[i]->resize_window(frame_w, path_h);
871         channel_scroll->reposition_window(frame_x+frame_w, frame_y, frame_h);
872         channel_scroll->update_length(y1-y0, y_scroll-y0, frame_h,0);
873         flush();
874 }
875
876 int ChannelPanel::button_press_event()
877 {
878         if(get_buttonpress() == 3 && cursor_inside()) {
879                 gui->lock_window("ChannelPanel::button_press_event");
880                 gui->channel_search->start();
881                 gui->unlock_window();
882                 return 1;
883         }
884         return 0;
885 }
886
887 void ChannelPanel::bounding_box(int ix0, int iy0, int ix1, int iy1)
888 {
889         int x_changed = 0, y_changed = 0;
890         if( ix0 < x0 ) { x0 = ix0;  x_changed = 1; }
891         if( iy0 < y0 ) { y0 = iy0;  y_changed = 1; }
892         if( ix1 > x1 ) { x1 = ix1;  x_changed = 1; }
893         if( iy1 > y1 ) { y1 = iy1;  y_changed = 1; }
894         if( x_changed ) {
895                 time_line_update(x0, x1);
896                 time_line_scroll->update_length(x1-x0, x_scroll-x0, frame_w, 0);
897                 if( !x_moved ) time_line_scroll->update_value(-x0);
898         }
899         if( y_changed )
900                 channel_scroll->update_length(y1-y0, y_scroll-y0, frame_h, 0);
901         if( x_changed || y_changed ) flush();
902 }
903
904 void ChannelPanel::set_x_scroll(int v)
905 {
906         x_moved = 1;
907         x_scroll = v + x0;
908         reposition();
909 }
910
911 void ChannelPanel::set_y_scroll(int v)
912 {
913         y_moved = 1;
914         y_scroll = v + y0;
915         reposition();
916 }
917
918 void ChannelPanel::reposition()
919 {
920         int n, i;
921         n = time_line_items.size();
922         for( i=0; i<n; ++i ) {
923                 TimeLineItem *item = time_line_items[i];
924                 if( item->get_x() == item->x0-x_scroll &&
925                     item->get_y() == item->y0-y_scroll) continue;
926                 item->reposition_window(item->x0-x_scroll, item->y0);
927         }
928         n = channel_data_items.size();
929         for( i=0; i<n; ++i ) {
930                 ChannelDataItem *item = channel_data_items[i];
931                 if( item->get_x() == item->x0-x_scroll &&
932                     item->get_y() == item->y0-y_scroll) continue;
933                 item->reposition_window(item->x0, item->y0-y_scroll);
934         }
935         n = channel_line_items.size();
936         for( i=0; i<n; ++i ) {
937                 ChannelEventLine *item = channel_line_items[i];
938                 if( item->get_x() == item->x0-x_scroll &&
939                     item->get_y() == item->y0-y_scroll) continue;
940                 item->reposition_window(item->x0, item->y0-y_scroll);
941         }
942         n = channel_event_items.size();
943         for( i=0; i<n; ++i ) {
944                 ChannelEvent *item = channel_event_items[i];
945                 if( item->get_x() == item->x0-x_scroll &&
946                     item->get_y() == item->y0-y_scroll) continue;
947                 item->reposition_window(item->x0-x_scroll, item->y0);
948         }
949 }
950
951 void ChannelPanel::get_xtime(int x, char *cp)
952 {
953         time_t xt = st_org + (x/hhr_w) * 1800;
954         if( !strcmp(tzname[0],"UTC") ) xt -= tz.tz_minuteswest*60;
955         struct tm xtm;  localtime_r(&xt,&xtm);
956         cp += sprintf(cp,"%02d:%02d",xtm.tm_hour, xtm.tm_min);
957         if( !xtm.tm_hour && !xtm.tm_min ) {
958                 sprintf(cp,"(%3.3s) ",&_("sunmontuewedthufrisat")[xtm.tm_wday*3]);
959         }
960 }
961
962 void ChannelPanel::time_line_update(int ix0, int ix1)
963 {
964         int x;  char text[BCTEXTLEN];
965         for( x=t0; x>=ix0; x-=hhr_w ) {
966                 get_xtime(x, text);  t0 = x;
967                 TimeLineItem *time_line_item = new TimeLineItem(this, t0, 0, text);
968                 time_line->add_subwindow(time_line_item);
969                 time_line_items.insert(time_line_item,0);
970         }
971         for( x=t1; x<ix1; x+=hhr_w ) {
972                 get_xtime(x, text);  t1 = x;
973                 TimeLineItem *time_line_item = new TimeLineItem(this, t1, 0, text);
974                 time_line->add_subwindow(time_line_item);
975                 time_line_items.append(time_line_item);
976         }
977 }
978
979 ChannelInfoCron::
980 ChannelInfoCron(ChannelInfoGUI *gui, int x, int y, int *value)
981  : BC_CheckBox(x, y, value, gui->cron_caption)
982 {
983         this->gui = gui;
984         set_tooltip(_("activate batch record when ok pressed"));
985 }
986
987 ChannelInfoCron::
988 ~ChannelInfoCron()
989 {
990 }
991
992 int ChannelInfoCron::
993 handle_event()
994 {
995         gui->iwindow->cron_enable = get_value();
996         return 1;
997 }
998
999
1000 ChannelInfoPowerOff::ChannelInfoPowerOff(ChannelInfoGUI *gui, int x, int y, int *value)
1001  : BC_CheckBox(x, y , value, gui->power_caption)
1002 {
1003         this->gui = gui;
1004         set_tooltip(_("poweroff system when batch record done"));
1005 }
1006
1007 ChannelInfoPowerOff::~ChannelInfoPowerOff()
1008 {
1009 }
1010
1011 int ChannelInfoPowerOff::handle_event()
1012 {
1013         gui->iwindow->poweroff_enable = get_value();
1014         return 1;
1015 }
1016
1017
1018 ChannelInfoFind::ChannelInfoFind(ChannelInfoGUI *gui, int x, int y)
1019  : BC_GenericButton(x, y, _("Find"))
1020 {
1021         this->gui = gui;
1022         set_tooltip(_("search event titles/info"));
1023 }
1024
1025 ChannelInfoFind::~ChannelInfoFind()
1026 {
1027 }
1028
1029 int ChannelInfoFind::handle_event()
1030 {
1031         gui->lock_window("ChannelInfoFind::handle_event");
1032         gui->channel_search->start();
1033         gui->unlock_window();
1034         return 1;
1035 }
1036
1037 void ChannelThread::start()
1038 {
1039         if( !Thread::running() ) {
1040                 done = 0;
1041                 Thread::start();
1042         }
1043 }
1044
1045 void ChannelThread::stop()
1046 {
1047         if( Thread::running() ) {
1048                 done = 1;
1049                 Thread::cancel();
1050         }
1051         Thread::join();
1052 }
1053
1054 ChannelThread::ChannelThread(ChannelInfoGUI *gui)
1055  : Thread(1, 0, 0)
1056 {
1057         this->gui = gui;
1058         this->iwindow = gui->iwindow;
1059         this->panel = gui->panel;
1060
1061         fd = 0;
1062         done = 0;
1063 }
1064
1065 ChannelThread::~ChannelThread()
1066 {
1067         stop();
1068 }
1069
1070 int ChannelThread::load_ident(int n, int y, char *ident)
1071 {
1072         ChannelDataItem *data_item = new ChannelDataItem(panel, 0, y,
1073                 panel->path_w, !n ? YELLOW : LTYELLOW, ident);
1074         panel->channel_data->add_subwindow(data_item);
1075         panel->channel_data_items.append(data_item);
1076         if( !n ) {
1077                 char info[BCTEXTLEN];
1078                 int i = mpeg3_dvb_get_chan_info(fd, n, -1, 0, info, sizeof(info)-1);
1079                 while( --i >= 0 && (info[i]=='\n' || info[i]==' ') ) info[i] = 0;
1080                 if( info[0] ) data_item->set_tooltip(info);
1081         }
1082         return 0;
1083 }
1084
1085 int ChannelThread::load_info(Channel *channel, ChannelEventLine *channel_line)
1086 {
1087         int n = channel->element;
1088         int ord = 0, i = 0;
1089
1090         while( ord < 0x80 ) {
1091                 char info[65536], *cp = &info[0];
1092                 int len = mpeg3_dvb_get_chan_info(fd,n,ord,i++,cp,sizeof(info)-1);
1093                 if( len < 0 ) { i = 0;  ++ord;  continue; }
1094                 char *bp = cp;  cp += len;
1095                 struct tm stm;  memset(&stm,0,sizeof(stm));
1096                 struct tm etm;  memset(&etm,0,sizeof(etm));
1097                 int k, l = sscanf(bp,"%d:%d:%d-%d:%d:%d %n",
1098                         &stm.tm_hour, &stm.tm_min, &stm.tm_sec,
1099                         &etm.tm_hour, &etm.tm_min, &etm.tm_sec,
1100                         &k);
1101                 if( l != 6 ) {
1102                         printf(_("bad scan time: %s\n"),info);
1103                         continue;
1104                 }
1105                 char *title = (bp += k);
1106                 while( bp<cp && *bp!='\n' ) ++bp;
1107                 char *dp = bp;  *bp++ = 0;
1108                 if( !*title ) {
1109                         printf(_("bad title: %s\n"),info);
1110                         continue;
1111                 }
1112                 char stm_wday[4];  memset(&stm_wday,0,sizeof(stm_wday));
1113                 l = sscanf(bp, "(%3s) %d/%d/%d", &stm_wday[0],
1114                         &stm.tm_year, &stm.tm_mon, &stm.tm_mday);
1115                 if( l != 4 ) {
1116                         printf(_("bad scan date: %s\n"),info);
1117                         continue;
1118                 }
1119                 while( bp<cp && *bp!='\n' ) ++bp;
1120                 *bp = 0;
1121                 etm.tm_year = (stm.tm_year -= 1900);
1122                 etm.tm_mon = --stm.tm_mon;
1123                 etm.tm_mday = stm.tm_mday;
1124                 stm.tm_isdst = etm.tm_isdst = -1;
1125                 time_t st = mktime(&stm);
1126                 time_t et = mktime(&etm);
1127                 if( et < st ) et += 24*3600;
1128                 if( et < st ) {
1129                         printf(_("end before start: %s\n"),info);
1130                         continue;
1131                 }
1132                 if( panel->st_org - et > 24*3600*2) {
1133                         printf(_("end time early: %s\n"),info);
1134                         continue;
1135                 }
1136                 if( st - panel->st_org > 24*3600*12 ) {
1137                         printf(_("start time late: %s\n"),info);
1138                         continue;
1139                 }
1140                 time_t st_min = (st - panel->st_org)/60;
1141                 time_t et_min = (et - panel->st_org)/60;
1142                 int dt = et_min - st_min;
1143                 if( dt <= 0 ) {
1144                         printf(_("zero duration: %s\n"),info);
1145                         continue;
1146                 }
1147
1148                 int w = (dt * panel->hhr_w) / 30;
1149                 int x = (st_min * panel->hhr_w) / 30;
1150                 int y = channel_line->y0;
1151                 panel->bounding_box(x, y, x+w, y+panel->path_h);
1152                 ChannelEvent *channel_event =
1153                         new ChannelEvent(channel_line, channel,
1154                                         st, et, x, 0, w, title);
1155                 channel_line->add_subwindow(channel_event);
1156                 panel->channel_event_items.append(channel_event);
1157
1158                 *dp = '\n';  *bp++ = '\n';
1159                 for( char *lp=bp; bp<cp; ++bp ) {
1160                         if( *bp == '\n' || ((bp-lp)>=60 && *bp==' ') )
1161                                 *(lp=bp) = '\n';
1162                 }
1163                 *cp = 0;
1164                 for( bp=&info[0]; --cp>=bp && (*cp=='\n' || *cp==' '); *cp=0 );
1165                 if( info[0] ) channel_event->set_tooltip(info);
1166         }
1167         return 0;
1168 }
1169
1170 void ChannelThread::run()
1171 {
1172         gui->init_wait();
1173         Channel *channel = 0;
1174         int y = 0;
1175         int nchannels = total_channels();
1176         enable_cancel();
1177
1178         for(int ch=0; !done && ch<nchannels; gui->update_progress(++ch) ) {
1179                 Channel *chan = get_channel(ch);
1180                 if( !chan ) continue;
1181                 disable_cancel();
1182                 iwindow->vdevice_lock->lock("ChannelThread::run");
1183                 DeviceDVBInput *dvb_input = iwindow->dvb_input;
1184                 if( !channel || chan->entry != channel->entry ) {
1185                         int retry = 3;
1186                         while( dvb_input->set_channel(chan) && --retry >= 0 );
1187                         if( retry >= 0 ) {
1188                                 channel = chan;
1189                                 if( y ) {
1190                                         gui->lock_window();
1191                                         y += panel->separator(y);
1192                                         gui->unlock_window();
1193                                 }
1194                         }
1195                         else
1196                                 channel = 0;
1197                 }
1198                 else if( chan->element == channel->element )
1199                         chan = 0;
1200                 else
1201                         channel = chan;
1202                 if( !done && chan && channel && (fd=dvb_input->get_src()) ) {
1203                         gui->lock_window();
1204                         load_ident(chan->element, y, chan->title);
1205                         ChannelEventLine *channel_line = panel->NewChannelLine(y);
1206                         load_info(chan, channel_line);
1207                         channel_line->show_window();
1208                         gui->unlock_window();
1209                         dvb_input->put_src();
1210                         y += panel->path_h;
1211                 }
1212                 iwindow->vdevice_lock->unlock();
1213                 enable_cancel();
1214         }
1215
1216         gui->lock_window("ChannelProgress::run");
1217         gui->channel_status->hide_window();
1218         gui->unlock_window();
1219
1220         disable_cancel();
1221         iwindow->close_vdevice();
1222 }
1223
1224
1225 ChannelInfoOK::ChannelInfoOK(ChannelInfoGUI *gui, int x, int y)
1226  : BC_OKButton(x, y)
1227 {
1228         this->gui = gui;
1229         set_tooltip(_("end channel info, start record"));
1230 }
1231
1232 ChannelInfoOK::~ChannelInfoOK()
1233 {
1234 }
1235
1236 int ChannelInfoOK::button_press_event()
1237 {
1238         if(get_buttonpress() == 1 && is_event_win() && cursor_inside()) {
1239                 gui->stop(0);
1240                 return 1;
1241         }
1242         return 0;
1243 }
1244
1245 int ChannelInfoOK::keypress_event()
1246 {
1247         return context_help_check_and_show();
1248 }
1249
1250
1251 ChannelInfoCancel::ChannelInfoCancel(ChannelInfoGUI *gui, int x, int y)
1252  : BC_CancelButton(x, y)
1253 {
1254         this->gui = gui;
1255 }
1256
1257 ChannelInfoCancel::~ChannelInfoCancel()
1258 {
1259 }
1260
1261 int ChannelInfoCancel::button_press_event()
1262 {
1263         if(get_buttonpress() == 1 && is_event_win() && cursor_inside()) {
1264                 gui->stop(1);
1265                 return 1;
1266         }
1267         return 0;
1268 }
1269
1270
1271 ChannelInfoGUIBatches::ChannelInfoGUIBatches(ChannelInfoGUI *gui,
1272         int x, int y, int w, int h)
1273  : RecordBatchesGUI(gui->iwindow->record_batches, x, y, w, h)
1274 {
1275         this->gui = gui;
1276 }
1277
1278 ChannelInfoGUIBatches::~ChannelInfoGUIBatches()
1279 {
1280 }
1281
1282 int ChannelInfoGUIBatches::selection_changed()
1283 {
1284         return RecordBatchesGUI::selection_changed();
1285 }
1286
1287 int ChannelInfoGUIBatches::handle_event()
1288 {
1289         if( get_double_click() )
1290                 gui->update_channel_tools();
1291         return 1;
1292 }
1293
1294
1295 void ChannelInfoGUI::create_objects()
1296 {
1297         int xs10 = xS(10);
1298         int ys5 = yS(5), ys10 = yS(10);
1299         lock_window("ChannelInfoGUI::create_objects");
1300         panel = new ChannelPanel(this,0,0,panel_w,panel_h);
1301         add_subwindow(panel);
1302         panel->create_objects();
1303         int items = iwindow->channeldb->size();
1304         if( items < 1 ) items = 1;
1305         progress = new ChannelProgress(this, 0, 0, path_w, path_h, items);
1306         add_subwindow(progress);
1307         progress->create_objects();
1308         ok = new ChannelInfoOK(this, ok_x, ok_y);
1309         add_subwindow(ok);
1310         channel_cron = new ChannelInfoCron(this, cron_x, cron_y, &iwindow->cron_enable);
1311         add_subwindow(channel_cron);
1312         channel_poweroff = new ChannelInfoPowerOff(this,
1313                  power_x, power_y, &iwindow->poweroff_enable);
1314         add_subwindow(channel_poweroff);
1315         channel_find = new ChannelInfoFind(this, find_x, find_y);
1316         add_subwindow(channel_find);
1317         cancel = new ChannelInfoCancel(this, cancel_x, cancel_y);
1318         add_subwindow(cancel);
1319         batch_bay = new ChannelInfoGUIBatches(this,bay_x, bay_y, bay_w, bay_h);
1320         add_subwindow(batch_bay);
1321         iwindow->record_batches.gui = batch_bay;
1322         batch_bay->set_current_batch(-1);
1323         batch_bay->update_batches(-1);
1324
1325         pad = BC_TextBox::calculate_h(this, MEDIUMFONT, 0, 1) + ys5;
1326         x0 = bay_x+bay_w + xs10;
1327         y0 = bay_y+ys10;
1328         int ww = 0;
1329         int x = x0;
1330         int y = y0;
1331
1332         add_subwindow(directory_title = new BC_Title(x, y, _("Directory:")));
1333         ww = max(directory_title->get_w(), ww);   y += pad;
1334         add_subwindow(path_title = new BC_Title(x, y, _("Path:")));
1335         ww = max(path_title->get_w(), ww);       y += pad;
1336         add_subwindow(start_title = new BC_Title(x, y, _("Start:")));
1337         ww = max(start_title->get_w(), ww);      y += pad;
1338         add_subwindow(duration_title = new BC_Title(x, y, _("Duration:")));
1339         ww = max(duration_title->get_w(), ww);   y += pad;
1340         add_subwindow(source_title = new BC_Title(x, y, _("Source:")));
1341         ww = max(source_title->get_w(), ww);     y += pad;
1342         title_w = ww;
1343
1344         int x1 = x0 + title_w + pad;
1345         x = x1;  y = y0;  ww = 0;
1346
1347         char *dir = iwindow->record_batches.get_default_directory();
1348         add_subwindow(channel_dir = new ChannelDir(this, dir, x, y));
1349         ww = max(channel_dir->get_w(), ww);     y += pad;
1350         add_subwindow(channel_path = new ChannelPath(this, x, y));
1351         ww = max(channel_path->get_w(), ww);   y += pad;
1352         channel_start = new ChannelStart(this, x, y);
1353         channel_start->create_objects();
1354         ww = max(channel_start->get_w(), ww);   y += pad;
1355         int w = BC_Title::calculate_w(this, (char*)"+00:00:00+", MEDIUMFONT);
1356         channel_duration = new ChannelDuration(this, x, y, w);
1357         channel_duration->create_objects();
1358         int x2 = x + channel_duration->get_w();
1359         double *early_margin = iwindow->record_batches.get_early_margin();
1360         early_time = new ChannelEarlyTime(this, x2, y, early_margin);
1361         early_time->create_objects();
1362         x2 += early_time->get_w();
1363         double *late_margin = iwindow->record_batches.get_late_margin();
1364         late_time = new ChannelLateTime(this, x2, y, late_margin);
1365         late_time->create_objects();
1366         x2 += late_time->get_w();
1367         ww = max(x2-x1, ww);                    y += pad;
1368         channel_source = new ChannelSource(this, x, y);
1369         channel_source->create_objects();
1370         ww = max(channel_source->get_w(), ww);  y += pad;
1371         data_w = x1-x0 + ww;
1372
1373         x = x0 + pad;
1374         add_subwindow(channel_clear_batch = new ChannelClearBatch(this, x, y));
1375         x += channel_clear_batch->get_w() + xs10;
1376         add_subwindow(channel_new_batch = new ChannelNewBatch(this, x, y));
1377         x += channel_new_batch->get_w() + xs10;
1378         add_subwindow(channel_delete_batch = new ChannelDeleteBatch(this, x, y));
1379         x += channel_delete_batch->get_w();
1380         ww = max(x-x0, ww);
1381         data_w = max(ww, data_w);
1382
1383         channel_status = new ChannelStatus(this, x0+data_w, y0);
1384         add_subwindow(channel_status);
1385         channel_status->create_objects();
1386         status_w = channel_status->get_w();
1387
1388         channel_search = new ChanSearch(iwindow);
1389         show_window();
1390         unlock_window();
1391 }
1392
1393 ChannelInfoGUI::ChannelInfoGUI(ChannelInfo *iwindow,
1394         int x, int y, int w, int h)
1395  : BC_Window(_(PROGRAM_NAME ": Channel Info"), x, y,
1396         w, h, xS(600), yS(400), 1, 0, 0 , -1,
1397         iwindow->mwindow->get_cwindow_display())
1398 {
1399         int xs10 = xS(10), xs20 = xS(20);
1400         int ys5 = yS(5), ys10 = yS(10);
1401         this->iwindow = iwindow;
1402         panel = 0;
1403         batch_bay =0;
1404         channel_dir = 0;
1405         channel_path = 0;
1406         channel_start = 0;
1407         channel_duration = 0;
1408         channel_source = 0;
1409         channel_clear_batch = 0;
1410         channel_new_batch = 0;
1411         channel_delete_batch = 0;
1412         early_time = late_time = 0;
1413         directory_title = 0;
1414         path_title = 0;
1415         start_title = 0;
1416         duration_title = 0;
1417         source_title = 0;
1418         cron_caption = _("Start Cron");
1419         power_caption = _("Poweroff");
1420         ok = 0;
1421         cancel = 0;
1422
1423         path_w = 16*BC_Title::calculate_w(this, (char*)"X", MEDIUMFONT);
1424         path_h = BC_TextBox::calculate_h(this, MEDIUMFONT, 0, 1);
1425         x0 = y0 = title_w = data_w = status_w = pad = 0;
1426         ok_w = BC_OKButton::calculate_w();
1427         ok_h = BC_OKButton::calculate_h();
1428         ok_x = xs10;
1429         ok_y = h - ok_h - ys10;
1430         BC_CheckBox::calculate_extents(this, &cron_w, &cron_h, cron_caption);
1431         cron_x = ok_x;
1432         cron_y = ok_y - cron_h - ys10;
1433         BC_CheckBox::calculate_extents(this, &power_w, &power_h, power_caption);
1434         power_x = cron_x;
1435         power_y = cron_y - power_h - ys5;
1436         find_h = BC_GenericButton::calculate_h();
1437         find_x = power_x;
1438         find_y = power_y - find_h - ys10;
1439         cancel_w = BC_CancelButton::calculate_w();
1440         cancel_h = BC_CancelButton::calculate_h();
1441         cancel_x = w - cancel_w - xs10,
1442         cancel_y = h - cancel_h - ys10;
1443         max_bay_w = xS(700);
1444         bay_h = yS(150);
1445         bay_x = ok_w + xs20;
1446         int x1 = cron_x+cron_w + xs10;
1447         if( x1 > bay_x ) bay_x = x1;
1448         x1 = power_x+power_w + xs10;
1449         if( x1 > bay_x ) bay_x = x1;
1450         bay_y = h - bay_h;
1451         // data_w,status_w zero, updated in create_objects
1452         bay_w = (w-bay_x) - (data_w+xs10) - (max(cancel_w, status_w)+xs20);
1453         if( bay_w > max_bay_w ) bay_w = max_bay_w;
1454         panel_w = w;
1455         panel_h = h - bay_h;
1456 // *** CONTEXT_HELP ***
1457         context_help_set_keyword("Capture at some Future Time via Batch");
1458 }
1459
1460 ChannelInfoGUI::~ChannelInfoGUI()
1461 {
1462         progress->stop();
1463         channel_search->stop();
1464         flush();  sync();
1465         delete channel_status;
1466         delete channel_search;
1467         delete channel_start;
1468         delete channel_duration;
1469         delete early_time;
1470         delete late_time;
1471         delete channel_source;
1472 }
1473
1474 void ChannelInfoGUI::stop(int v)
1475 {
1476         if( !iwindow->gui_done ) {
1477                 iwindow->gui_done = 1;
1478                 set_done(v);
1479         }
1480 }
1481
1482 int ChannelInfoGUI::translation_event()
1483 {
1484         iwindow->mwindow->session->cswindow_x = get_x();
1485         iwindow->mwindow->session->cswindow_y = get_y();
1486         return 0;
1487 }
1488
1489 int ChannelInfoGUI::resize_event(int w, int h)
1490 {
1491         int xs10 = xS(10), xs20 = xS(20);
1492         int ys5 = yS(5), ys10 = yS(10);
1493         iwindow->mwindow->session->cswindow_w = w;
1494         iwindow->mwindow->session->cswindow_h = h;
1495         panel_w = w;
1496         panel_h = h - bay_h;
1497         panel->resize(panel_w,panel_h);
1498         panel->reposition_window(0,0,panel_w,panel_h);
1499         ok_x = xs10;
1500         ok_y = h - ok_h - ys10;
1501         ok->reposition_window(ok_x, ok_y);
1502         cron_x = ok_x;
1503         cron_y = ok_y - cron_h - ys10;
1504         channel_cron->reposition_window(cron_x, cron_y);
1505         power_x = cron_x;
1506         power_y = cron_y - power_h - ys5;
1507         channel_poweroff->reposition_window(power_x, power_y);
1508         find_x = power_x;
1509         find_y = power_y - find_h - ys10;
1510         channel_find->reposition_window(find_x, find_y);
1511         cancel_x = w - cancel_w - xs10,
1512         cancel_y = h - cancel_h - ys10;
1513         cancel->reposition_window(cancel_x, cancel_y);
1514         bay_x = ok_w + xs20;
1515         int x1 = cron_x+cron_w + xs10;
1516         if( x1 > bay_x ) bay_x = x1;
1517         x1 = power_x+power_w + xs10;
1518         if( x1 > bay_x ) bay_x = x1;
1519         bay_y = h - bay_h;
1520         bay_w = (w-bay_x) - (data_w+xs10) - (max(cancel_w, status_w)+xs20);
1521         if( bay_w > max_bay_w ) bay_w = max_bay_w;
1522         batch_bay->reposition_window(bay_x, bay_y, bay_w, bay_h);
1523
1524         int x0 = bay_x+bay_w + xs10;
1525         int y0 = bay_y+ys10;
1526         int x = x0;
1527         int y = y0;
1528
1529         directory_title->reposition_window(x, y);  y += pad;
1530         path_title->reposition_window(x, y);       y += pad;
1531         start_title->reposition_window(x, y);      y += pad;
1532         duration_title->reposition_window(x, y);   y += pad;
1533         source_title->reposition_window(x, y);
1534
1535         x = x0 + title_w + pad;
1536         y = y0;
1537
1538         channel_dir->reposition_window(x, y);      y += pad;
1539         channel_path->reposition_window(x, y);     y += pad;
1540         channel_start->reposition_window(x, y);    y += pad;
1541         channel_duration->reposition_window(x, y);
1542         int x2 = x + channel_duration->get_w();
1543         early_time->reposition_window(x2, y);
1544         x2 += early_time->get_w();
1545         late_time->reposition_window(x2, y);       y += pad;
1546         channel_source->reposition_window(x, y);   y += pad;
1547
1548         x = x0 + pad;
1549         channel_clear_batch->reposition_window(x, y);
1550         x += channel_clear_batch->get_w() + xs10;
1551         channel_new_batch->reposition_window(x, y);
1552         x += channel_new_batch->get_w() + xs10;
1553         channel_delete_batch->reposition_window(x, y);
1554
1555         y = y0;
1556         x = x0 + data_w;
1557         channel_status->reposition_window(x, y);
1558         return 1;
1559 }
1560
1561 int ChannelInfoGUI::close_event()
1562 {
1563         stop(1);
1564         return 1;
1565 }
1566
1567 void ChannelInfoGUI::update_channel_tools()
1568 {
1569         Batch *batch = batch_bay->get_editing_batch();
1570         channel_path->update(batch->asset->path);
1571         channel_start->update(&batch->start_day, &batch->start_time);
1572         channel_duration->update(0, &batch->duration);
1573         channel_source->update(batch->get_source_text());
1574         flush();
1575 }
1576
1577 void ChannelInfoGUI::incr_event(int start_time_incr, int duration_incr)
1578 {
1579         Batch *batch = batch_bay->get_editing_batch();
1580         batch->start_time += start_time_incr;
1581         batch->duration += duration_incr;
1582         batch_bay->update_batches();
1583         update_channel_tools();
1584 }
1585
1586
1587 ChannelInfo::ChannelInfo(MWindow *mwindow)
1588  : Thread(1, 0, 0),
1589    record_batches(mwindow)
1590 {
1591         this->mwindow = mwindow;
1592         this->record = mwindow->gui->record;
1593         scan_lock = new Condition(0,"ChannelInfo::scan_lock");
1594         window_lock = new Mutex("ChannelInfo::window_lock");
1595         vdevice_lock = new Mutex("ChannelInfo::vdevice_lock");
1596         progress_lock = new Mutex("ChannelInfo::progress_lock");
1597
1598         vdevice = 0;
1599         dvb_input = 0;
1600         gui = 0;
1601         thread = 0;
1602         channeldb = 0;
1603         cron_enable = 1;
1604         poweroff_enable = 0;
1605         item = 0;
1606         done = 1;
1607         gui_done = 1;
1608
1609         start();
1610 }
1611
1612 ChannelInfo::~ChannelInfo()
1613 {
1614         stop();
1615         delete thread;
1616         delete gui;  gui = 0;
1617         delete vdevice;
1618         record_batches.clear();
1619         delete channeldb;
1620         delete scan_lock;
1621         delete window_lock;
1622         delete vdevice_lock;
1623         delete progress_lock;
1624 }
1625
1626 void ChannelInfo::run_scan()
1627 {
1628         window_lock->lock("ChannelInfo::run_scan");
1629         if( gui ) {
1630                 gui->lock_window("ChannelInfo::run_scan");
1631                 gui->raise_window();
1632                 gui->unlock_window();
1633         }
1634         else
1635                 scan_lock->unlock();
1636         window_lock->unlock();
1637 }
1638
1639 void ChannelInfo::toggle_scan()
1640 {
1641         window_lock->lock("ChannelInfo::toggle_scan");
1642         if( gui )
1643                 gui->stop(1);
1644         else
1645                 scan_lock->unlock();
1646         window_lock->unlock();
1647 }
1648
1649 void ChannelInfo::start()
1650 {
1651         if( !Thread::running() ) {
1652                 done = 0;
1653                 Thread::start();
1654         }
1655 }
1656
1657 void ChannelInfo::stop()
1658 {
1659         if( Thread::running() ) {
1660                 done = 1;
1661                 scan_lock->unlock();
1662                 window_lock->lock("ChannelInfo::stop");
1663                 if( gui ) gui->stop(1);
1664                 window_lock->unlock();
1665                 Thread::cancel();
1666         }
1667         Thread::join();
1668 }
1669
1670 void ChannelInfo::run()
1671 {
1672         int root_w = mwindow->gui->get_root_w(1);
1673         int root_h = mwindow->gui->get_root_h(1);
1674
1675         while( !done ) {
1676                 scan_lock->reset();
1677                 scan_lock->lock();
1678                 if( done ) break;
1679                 if( record->Thread::running() ) {
1680                         char string[BCTEXTLEN];
1681                         sprintf(string,_("Recording in progress\n"));
1682                         MainError::show_error(string);
1683                         continue;
1684                 }
1685                 EDLSession *session = mwindow->edl->session;
1686                 VideoInConfig *vconfig_in = session->vconfig_in;
1687                 if( vconfig_in->driver != CAPTURE_DVB ) {
1688                         char string[BCTEXTLEN];
1689                         sprintf(string,_("capture driver not dvb\n"));
1690                         MainError::show_error(string);
1691                         continue;
1692                 }
1693                 int x = mwindow->session->cswindow_x;
1694                 int y = mwindow->session->cswindow_y;
1695                 int w = mwindow->session->cswindow_w;
1696                 int h = mwindow->session->cswindow_h;
1697                 if( w < xS(600) ) w = xS(600);
1698                 if( h < yS(400) ) h = yS(400);
1699                 int scr_x = mwindow->gui->get_screen_x(1, -1);
1700                 int scr_w = mwindow->gui->get_screen_w(1, -1);
1701                 if( x < scr_x ) x = scr_x;
1702                 if( x > scr_x+scr_w ) x = scr_x+scr_w;
1703                 if( x+w > root_w ) x = root_w - w;
1704                 if( x < 0 ) { x = 0;  w = scr_w; }
1705                 if( y+h > root_h ) y = root_h - h;
1706                 if( y < 0 ) { y = 0;  h = root_h; }
1707                 if( y+h > root_h ) h = root_h-y;
1708                 mwindow->session->cswindow_x = x;
1709                 mwindow->session->cswindow_y = y;
1710                 mwindow->session->cswindow_w = w;
1711                 mwindow->session->cswindow_h = h;
1712                 cron_enable = 1;
1713                 poweroff_enable = 0;
1714                 vdevice = new VideoDevice(mwindow);
1715                 int result = vdevice->open_input(vconfig_in, 0, 0, 1., vconfig_in->in_framerate);
1716                 dvb_input = result ? 0 : (DeviceDVBInput *)vdevice->mpeg_device();
1717                 if( dvb_input ) {
1718                         channeldb = new ChannelDB;
1719                         VideoDevice::load_channeldb(channeldb, vconfig_in);
1720                         record_batches.load_defaults(channeldb);
1721                         item = 0;
1722                         window_lock->lock("ChannelInfo::run 0");
1723                         gui_done = 0;
1724                         gui = new ChannelInfoGUI(this, x, y, w, h);
1725                         gui->lock_window("ChannelInfo::gui_create_objects");
1726                         gui->create_objects();
1727                         gui->set_icon(mwindow->theme->get_image("record_icon"));
1728                         gui->reposition_window(x, y);
1729                         gui->resize_event(w, h);
1730                         dvb_input->set_signal_status(gui->channel_status);
1731                         gui->unlock_window();
1732                         thread = new ChannelThread(gui);
1733                         thread->start();
1734                         window_lock->unlock();
1735                         result = gui->run_window();
1736                         delete thread;  thread = 0;
1737                         close_vdevice();
1738                         gui->lock_window();
1739                         gui->unlock_window();
1740                         window_lock->lock("ChannelInfo::run 1");
1741                         delete gui;  gui = 0;
1742                         window_lock->unlock();
1743                         record_batches.save_defaults(channeldb);
1744                         if( !result ) {
1745                                 record_batches.save_default_channel(channeldb);
1746                                 record->start();
1747                                 record->init_lock->lock();
1748                                 if( cron_enable ) {
1749                                         record->set_power_off(poweroff_enable);
1750                                         record->start_cron_thread();
1751                                 }
1752                         }
1753                         record_batches.clear();
1754                         delete channeldb;  channeldb = 0;
1755                 }
1756                 else {
1757                         close_vdevice();
1758                         char string[BCTEXTLEN];
1759                         sprintf(string,_("cannot open dvb video device\n"));
1760                         MainError::show_error(string);
1761                 }
1762         }
1763 }
1764
1765 void ChannelInfo::close_vdevice()
1766 {
1767         vdevice_lock->lock("ChannelInfo::close_vdevice");
1768         progress_lock->lock("ChannelInfo::close_vdevice");
1769         if( vdevice ) {
1770                 vdevice->close_all();
1771                 delete vdevice;  vdevice = 0;
1772         }
1773         progress_lock->unlock();
1774         vdevice_lock->unlock();
1775 }
1776
1777 Batch *ChannelInfo::new_batch()
1778 {
1779         Batch *batch = new Batch(gui->iwindow->mwindow, 0);
1780         batch->create_objects();
1781         batch->calculate_news();
1782         gui->iwindow->record_batches.append(batch);
1783         return batch;
1784 }
1785
1786 void ChannelInfo::delete_batch()
1787 {
1788 // Abort if one batch left
1789         int edit_batch = editing_batch();
1790         int total_batches = record_batches.total();
1791         if( total_batches > 1 && edit_batch < total_batches ) {
1792                 Batch *batch = record_batches[edit_batch];
1793                 record_batches.remove(batch);  delete batch;
1794         }
1795 }
1796
1797 ChannelScan::ChannelScan(MWindow *mwindow)
1798  : BC_MenuItem(_("Scan..."), _("Ctrl-Alt-s"), 's')
1799 {
1800         set_ctrl();
1801         set_alt();
1802         this->mwindow = mwindow;
1803 }
1804
1805 ChannelScan::~ChannelScan()
1806 {
1807 }
1808
1809 int ChannelScan::handle_event()
1810 {
1811         mwindow->gui->channel_info->run_scan();
1812         return 1;
1813 }
1814
1815
1816 ChannelDir::ChannelDir(ChannelInfoGUI *gui, const char *dir, int x, int y)
1817  : RecordBatchesGUI::Dir(gui->iwindow->record_batches, dir, x, y)
1818 {
1819         this->gui = gui;
1820 }
1821
1822
1823 ChannelPath::ChannelPath(ChannelInfoGUI *gui, int x, int y)
1824  : RecordBatchesGUI::Path(gui->iwindow->record_batches, x, y)
1825 {
1826         this->gui = gui;
1827 }
1828
1829
1830 ChannelStart::ChannelStart(ChannelInfoGUI *gui, int x, int y)
1831  : RecordBatchesGUI::StartTime(gui, gui->iwindow->record_batches, x, y)
1832 {
1833         this->gui = gui;
1834 }
1835
1836
1837 ChannelDuration::ChannelDuration(ChannelInfoGUI *gui, int x, int y, int w)
1838  : RecordBatchesGUI::Duration(gui, gui->iwindow->record_batches, x, y, w)
1839 {
1840         this->gui = gui;
1841 }
1842
1843
1844 ChannelEarlyTime::ChannelEarlyTime(ChannelInfoGUI *gui, int x, int y,
1845         double *output_time)
1846  : TimeEntryTumbler(gui, x, y, 0, output_time, 15, TIME_MS2, xS(75))
1847 {
1848         this->gui = gui;
1849 }
1850
1851 int ChannelEarlyTime::handle_up_event()
1852 {
1853         gui->incr_event(incr,-incr);
1854         return TimeEntryTumbler::handle_up_event();
1855 }
1856
1857 int ChannelEarlyTime::handle_down_event()
1858 {
1859         gui->incr_event(-incr,incr);
1860         return TimeEntryTumbler::handle_down_event();
1861 }
1862
1863
1864 ChannelLateTime::ChannelLateTime(ChannelInfoGUI *gui, int x, int y,
1865         double *output_time)
1866  : TimeEntryTumbler(gui, x, y, 0, output_time, 15, TIME_MS2, xS(75))
1867 {
1868         this->gui = gui;
1869 }
1870
1871 int ChannelLateTime::handle_up_event()
1872 {
1873         gui->incr_event(0,incr);
1874         return TimeEntryTumbler::handle_up_event();
1875 }
1876
1877 int ChannelLateTime::handle_down_event()
1878 {
1879         gui->incr_event(0,-incr);
1880         return TimeEntryTumbler::handle_down_event();
1881 }
1882
1883
1884 ChannelSource::ChannelSource(ChannelInfoGUI *gui, int x, int y)
1885  : RecordBatchesGUI::Source(gui, gui->iwindow->record_batches, x, y)
1886 {
1887         this->gui = gui;
1888 }
1889
1890 void ChannelSource::create_objects()
1891 {
1892         RecordBatchesGUI::Source::create_objects();
1893         ChannelDB *channeldb = gui->iwindow->channeldb;
1894         sources.remove_all_objects();
1895         for(int i = 0; i < channeldb->size(); i++) {
1896                 sources.append(new BC_ListBoxItem(channeldb->get(i)->title));
1897         }
1898         update_list(&sources);
1899 }
1900
1901 int ChannelSource::handle_event()
1902 {
1903         int chan_no = get_number();
1904         Channel *channel = gui->iwindow->channeldb->get(chan_no);
1905         if( channel ) {
1906                 Batch *batch = batches.get_editing_batch();
1907                 if( batch ) batch->channel = channel;
1908                 update(channel->title);
1909         }
1910         return RecordBatchesGUI::Source::handle_event();
1911 }
1912
1913
1914 ChannelClearBatch::ChannelClearBatch(ChannelInfoGUI *gui, int x, int y)
1915  : RecordBatchesGUI::ClearBatch(gui->iwindow->record_batches, x, y)
1916 {
1917         this->gui = gui;
1918         set_tooltip(_("Delete all clips."));
1919 }
1920
1921 int ChannelClearBatch::handle_event()
1922 {
1923         gui->iwindow->record_batches.clear();
1924         gui->iwindow->new_batch();
1925         gui->batch_bay->set_current_batch(-1);
1926         gui->batch_bay->set_editing_batch(0);
1927         gui->batch_bay->update_batches(0);
1928         return RecordBatchesGUI::ClearBatch::handle_event();
1929 }
1930
1931
1932 ChannelNewBatch::ChannelNewBatch(ChannelInfoGUI *gui, int x, int y)
1933  : RecordBatchesGUI::NewBatch(gui->iwindow->record_batches, x, y)
1934 {
1935         this->gui = gui;
1936         set_tooltip(_("Create new clip."));
1937 }
1938 int ChannelNewBatch::handle_event()
1939 {
1940         gui->iwindow->new_batch();
1941         return RecordBatchesGUI::NewBatch::handle_event();
1942 }
1943
1944
1945 ChannelDeleteBatch::ChannelDeleteBatch(ChannelInfoGUI *gui, int x, int y)
1946  : RecordBatchesGUI::DeleteBatch(gui->iwindow->record_batches, x, y)
1947 {
1948         this->gui = gui;
1949         set_tooltip(_("Delete clip."));
1950 }
1951
1952 int ChannelDeleteBatch::handle_event()
1953 {
1954         gui->iwindow->delete_batch();
1955         return RecordBatchesGUI::DeleteBatch::handle_event();
1956 }
1957
1958 #endif
1959