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