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