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