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