no longer need ffmpeg patch0 which was for Termux
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / recordbatches.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 #include <stdio.h>
22 #include <stdint.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <fcntl.h>
27
28 #include "asset.h"
29 #include "batch.h"
30 #include "bchash.h"
31 #include "bclistbox.h"
32 #include "channel.h"
33 #include "channeldb.h"
34 #include "filesystem.h"
35 #include "mwindow.h"
36 #include "record.h"
37 #include "recordbatches.h"
38 #include "timeentry.h"
39
40 const char * RecordBatches::
41 default_batch_titles[] = {
42         N_("On"), N_("Path"), N_("News"), N_("Start time"),
43         N_("Duration"), N_("Source"), N_("Mode")
44 };
45
46 const int RecordBatches::
47 default_columnwidth[] = {
48         30, 200, 100, 100, 100, 100, 70
49 };
50
51 void RecordBatches::
52 load_defaults(ChannelDB * channeldb, Record * record)
53 {
54         char string[BCTEXTLEN];
55         BC_Hash *defaults = mwindow->defaults;
56         defaults->get("RECORD_DEFAULT_DIRECTORY", default_directory);
57         early_margin = defaults->get("RECORD_EARLY_MARGIN", early_margin);
58         late_margin = defaults->get("RECORD_LATE_MARGIN", late_margin);
59         for(int i = 0; i < BATCH_COLUMNS; i++) {
60                 batch_titles[i] = _(default_batch_titles[i]);
61                 sprintf(string, "BATCH_COLUMNWIDTH_%d", i);
62                 column_widths[i] = defaults->get(string, xS(default_columnwidth[i]));
63         }
64         int total_batches = defaults->get("TOTAL_BATCHES", 1);
65         if(total_batches < 1) total_batches = 1;
66         for(int i = 0; i < total_batches; ++i) {
67                 Batch *batch = new Batch(mwindow, record);
68                 batch->create_objects();
69                 batches.append(batch);
70                 Asset *asset = batch->asset;
71                 sprintf(string, "RECORD_PATH_%d", i);
72                 defaults->get(string, asset->path);
73                 sprintf(string, "RECORD_CHANNEL_%d", i);
74                 int chan_no = channeldb->number_of(batch->channel);
75                 chan_no = defaults->get(string, chan_no);
76                 batch->channel = channeldb->get(chan_no);
77                 sprintf(string, "RECORD_STARTDAY_%d", i);
78                 batch->start_day = defaults->get(string, batch->start_day);
79                 sprintf(string, "RECORD_STARTTIME_%d", i);
80                 batch->start_time = defaults->get(string, batch->start_time);
81                 sprintf(string, "RECORD_DURATION_%d", i);
82                 batch->duration = defaults->get(string, batch->duration);
83                 sprintf(string, "RECORD_MODE_%d", i);
84                 batch->record_mode = defaults->get(string, batch->record_mode);
85                 sprintf(string, "BATCH_ENABLED_%d", i);
86                 batch->enabled = defaults->get(string, batch->enabled);
87                 batch->update_times();
88         }
89
90         current_item = editing_item = 0;
91 }
92
93 void RecordBatches::
94 save_defaults(ChannelDB * channeldb)
95 {
96         char string[BCTEXTLEN];
97         BC_Hash *defaults = mwindow->defaults;
98         defaults->update("RECORD_DEFAULT_DIRECTORY", default_directory);
99         defaults->update("RECORD_EARLY_MARGIN", early_margin);
100         defaults->update("RECORD_LATE_MARGIN", late_margin);
101         for(int i = 0; i < BATCH_COLUMNS; i++) {
102                 sprintf(string, "BATCH_COLUMNWIDTH_%d", i);
103                 defaults->update(string, column_widths[i]);
104         }
105
106         defaults->update("TOTAL_BATCHES", batches.total);
107         for(int i = 0; i < batches.total; ++i) {
108                 Batch *batch = batches.values[i];
109                 Asset *asset = batch->asset;
110                 sprintf(string, "RECORD_PATH_%d", i);
111                 defaults->update(string, asset->path);
112                 sprintf(string, "RECORD_CHANNEL_%d", i);
113                 int chan_no = channeldb->number_of(batch->channel);
114                 defaults->update(string, chan_no);
115                 sprintf(string, "RECORD_STARTDAY_%d", i);
116                 defaults->update(string, batch->start_day);
117                 sprintf(string, "RECORD_STARTTIME_%d", i);
118                 defaults->update(string, batch->start_time);
119                 sprintf(string, "RECORD_DURATION_%d", i);
120                 defaults->update(string, batch->duration);
121                 sprintf(string, "RECORD_MODE_%d", i);
122                 defaults->update(string, batch->record_mode);
123                 sprintf(string, "BATCH_ENABLED_%d", i);
124                 defaults->update(string, batch->enabled);
125         }
126 }
127
128 void RecordBatches::
129 save_default_channel(ChannelDB * channeldb)
130 {
131         BC_Hash *defaults = mwindow->defaults;
132         Batch *batch = get_editing_batch();
133         if( !batch ) return;
134         int chan_no = channeldb->number_of(batch->channel);
135         if( chan_no < 0 ) return;
136         defaults->update("RECORD_CURRENT_CHANNEL", chan_no);
137 }
138
139 RecordBatches::
140 RecordBatches(MWindow * mwindow)
141 {
142         this->mwindow = mwindow;
143         editing_item = current_item = -1;
144         batch_active = 0;
145         early_margin = late_margin = 0.;
146         default_directory[0] = 0;
147 }
148
149 RecordBatches::
150 ~RecordBatches()
151 {
152 }
153
154 void RecordBatches::
155 remove(Batch *batch)
156 {
157         batches.remove(batch);
158         int total_batches = total();
159         if( current_item == editing_item ) {
160                 if(current_item >= total_batches)
161                         current_item = total_batches - 1;
162                 editing_item = current_item;
163         }
164         else {
165                 if(current_item > editing_item)
166                         --current_item;
167                 if( editing_item >= total_batches )
168                         editing_item = total_batches - 1;
169         }
170 }
171
172 void RecordBatches::
173 clear()
174 {
175         batches.remove_all_objects();
176         for(int j = 0; j < BATCH_COLUMNS; j++) {
177                 data[j].remove_all_objects();
178         }
179         current_item = editing_item = -1;
180 }
181
182 RecordBatchesGUI::
183 RecordBatchesGUI(RecordBatches &batches,
184         int x, int y, int w, int h)
185  : BC_ListBox(x, y, w, h, LISTBOX_TEXT,         // Display text list or icons
186                         batches.data,           // Each column has an ArrayList of BC_ListBoxItems.
187                         batches.batch_titles,   // Titles for columns.  Set to 0 for no titles
188                         batches.column_widths,  // width of each column
189                         BATCH_COLUMNS,          // Total columns.
190                         0,                      // Pixel of top of window.
191                         0,                      // If this listbox is a popup window
192                         LISTBOX_SINGLE,         // Select one item or multiple items
193                         ICON_LEFT,              // Position of icon relative to text of each item
194                         1),                     // Allow dragging
195    batches(batches)
196 {
197         dragging_item = 0;
198 }
199
200 // Do nothing for double clicks to protect active batch
201 int RecordBatchesGUI::
202 handle_event()
203 {
204         return 1;
205 }
206
207 int RecordBatchesGUI::
208 update(int highlighted_item, int recalc_positions)
209 {
210         return BC_ListBox::update(batches.data, batches.batch_titles,
211                         batches.column_widths, BATCH_COLUMNS,
212                         get_xposition(), get_yposition(),
213                         highlighted_item, recalc_positions);
214 }
215
216 int RecordBatchesGUI::
217 column_resize_event()
218 {
219         for(int i = 0; i < BATCH_COLUMNS; i++) {
220                 batches.column_widths[i] = get_column_width(i);
221         }
222         return 1;
223 }
224
225 int RecordBatchesGUI::
226 drag_start_event()
227 {
228         if(!BC_ListBox::drag_start_event()) return 0;
229         dragging_item = 1;
230         return 1;
231 }
232
233 int RecordBatchesGUI::
234 drag_motion_event()
235 {
236         if(!BC_ListBox::drag_motion_event()) return 0;
237         return 1;
238 }
239
240 int RecordBatchesGUI::
241 selection_changed()
242 {
243         int n = get_selection_number(0, 0);
244         if(n >= 0) {
245                 set_editing_batch(n);
246                 if(get_cursor_x() < batches.column_widths[0]) {
247                         Batch *batch = batches[n];
248                         batch->enabled = !batch->enabled;
249                         update_batches(n);
250                 }
251         }
252         return 1;
253 }
254
255 int RecordBatchesGUI::
256 drag_stop_event()
257 {
258         if(dragging_item) {
259                 int total_batches = batches.total();
260                 int src_item = editing_batch();
261                 Batch *src_batch = batches[src_item];
262                 int dst_item = get_highlighted_item();
263                 if(dst_item < 0) dst_item = total_batches;
264                 if(dst_item > src_item) --dst_item;
265
266                 for(int i = src_item; i < total_batches - 1; i++)
267                         batches[i] = batches[i + 1];
268                 for(int i = total_batches - 1; i > dst_item; --i)
269                         batches[i] = batches[i - 1];
270                 batches[dst_item] = src_batch;
271
272                 BC_ListBox::drag_stop_event();
273                 dragging_item = 0;
274                 set_editing_batch(dst_item);
275                 update_batches(dst_item);
276         }
277         return 0;
278 }
279
280 void RecordBatchesGUI::
281 update_batch_news(int item)
282 {
283         Batch *batch = batches[item];
284         batch->calculate_news();
285         batches.data[2].values[item]->set_text(batch->news);
286         update();  flush();
287 }
288
289 void RecordBatchesGUI::
290 update_batches(int selection_item)
291 {
292         time_t t;  time(&t);
293         char string[BCTEXTLEN], string2[BCTEXTLEN];
294
295         for(int j = 0; j < BATCH_COLUMNS; j++) {
296                 batches.data[j].remove_all_objects();
297         }
298         int total_batches = batches.total();
299         for(int i = 0; i < total_batches; i++) {
300                 Batch *batch = batches[i];
301                 batch->update_times();
302                 batch->calculate_news();
303
304                 int color = LTGREEN;
305                 if( i != batches.current_item ) {
306                         if( batch->time_start < t )
307                                 color = RED;
308                         else if( i > 0 && batches[i-1]->time_end > batch->time_start )
309                                 color = RED;
310                         else if( batch->time_start - t > 2*24*3600 )
311                                 color = YELLOW;
312                 }
313                 else
314                         color = LTYELLOW;
315
316                 batches.data[0].append(
317                         new BC_ListBoxItem((char *)(batch->enabled ? "X" : " "), color));
318                 batches.data[1].append(
319                         new BC_ListBoxItem(batch->asset->path, color));
320                 batches.data[2].append(
321                         new BC_ListBoxItem(batch->news, RED));
322                 Units::totext(string2, batch->start_time, TIME_HMS3);
323                 sprintf(string, "%s %s", TimeEntry::day_table[batch->start_day], string2);
324                 batches.data[3].append(
325                         new BC_ListBoxItem(string, color));
326                 Units::totext(string, batch->duration, TIME_HMS3);
327                 batches.data[4].append(
328                         new BC_ListBoxItem(string, color));
329                 sprintf(string, "%s", batch->channel ? batch->channel->title : _("None"));
330                 batches.data[5].append(
331                         new BC_ListBoxItem(string, color));
332                 sprintf(string, "%s", Batch::mode_to_text(batch->record_mode));
333                 batches.data[6].append(
334                         new BC_ListBoxItem(string, color));
335
336                 if(i == selection_item) {
337                         for(int j = 0; j < BATCH_COLUMNS; j++) {
338                                 batches.data[j].values[i]->set_selected(1);
339                         }
340                 }
341         }
342         update(batches.editing_item, 1);
343         flush();
344 }
345
346 void RecordBatchesGUI::
347 set_row_color(int row, int color)
348 {
349         for(int i = 0; i < BATCH_COLUMNS; i++) {
350                 BC_ListBoxItem *batch = batches.data[i].values[row];
351                 batch->set_color(color);
352         }
353 }
354
355
356 RecordBatchesGUI::Dir::
357 Dir(RecordBatches &batches, const char *dir, int x, int y)
358  : BC_TextBox(x, y, xS(200), 1, dir),
359    batches(batches),
360    directory(batches.default_directory)
361 {
362         strncpy(directory, dir, sizeof(directory));
363         dir_entries = new ArrayList<BC_ListBoxItem*>;
364         entries_dir[0] = 0;
365         load_dirs(dir);
366 }
367
368 RecordBatchesGUI::Dir::
369 ~Dir()
370 {
371         dir_entries->remove_all_objects();
372         delete dir_entries;
373 }
374
375 int RecordBatchesGUI::Dir::
376 handle_event()
377 {
378         char *path = FileSystem::basepath(get_text());
379         strcpy(directory, path);
380         load_dirs(path);
381         calculate_suggestions(dir_entries);
382         delete [] path;
383         return 1;
384 }
385
386 void RecordBatchesGUI::Dir::
387 load_dirs(const char *dir)
388 {
389         if( !strncmp(dir, entries_dir, sizeof(entries_dir)) ) return;
390         strncpy(entries_dir, dir, sizeof(entries_dir));
391         dir_entries->remove_all_objects();
392         FileSystem fs;  fs.update(dir);
393         int total_files = fs.total_files();
394         for(int i = 0; i < total_files; i++) {
395                 if( !fs.get_entry(i)->get_is_dir() ) continue;
396                 const char *name = fs.get_entry(i)->get_name();
397                 dir_entries->append(new BC_ListBoxItem(name));
398         }
399 }
400
401 RecordBatchesGUI::Path::
402 Path(RecordBatches &batches, int x, int y)
403  : BC_TextBox(x, y, xS(200), 1, batches.get_editing_batch()->asset->path),
404    batches(batches)
405 {
406 }
407
408 RecordBatchesGUI::Path::
409 ~Path()
410 {
411 }
412
413 int RecordBatchesGUI::Path::
414 handle_event()
415 {
416         calculate_suggestions();
417         Batch *batch = batches.gui->get_editing_batch();
418         strcpy(batch->asset->path, get_text());
419         batches.gui->update_batches();
420         return 1;
421 }
422
423 RecordBatchesGUI::StartTime::
424 StartTime(BC_Window *win, RecordBatches &batches, int x, int y, int w)
425  : TimeEntry(win, x, y,
426                  &batches.get_editing_batch()->start_day,
427                  &batches.get_editing_batch()->start_time, TIME_HMS3, w),
428    batches(batches)
429 {
430 }
431
432 int RecordBatchesGUI::StartTime::
433 handle_event()
434 {
435         batches.gui->update_batches();
436         return 1;
437 }
438
439
440 RecordBatchesGUI::Duration::
441 Duration(BC_Window *win, RecordBatches &batches, int x, int y, int w)
442  : TimeEntry(win, x, y, 0,
443                  &batches.get_editing_batch()->duration, TIME_HMS2, w),
444    batches(batches)
445 {
446 }
447
448 int RecordBatchesGUI::Duration::
449 handle_event()
450 {
451         batches.gui->update_batches();
452         return 1;
453 }
454
455
456 RecordBatchesGUI::Source::
457 Source(BC_Window *win, RecordBatches &batches, int x, int y)
458  : BC_PopupTextBox(win, &sources,
459                  batches.get_editing_batch()->get_source_text(),
460                  x, y, xS(200), yS(200)),
461    batches(batches)
462 {
463 }
464
465 int RecordBatchesGUI::Source::
466 handle_event()
467 {
468         batches.gui->update_batches();
469         return 1;
470 }
471
472
473 RecordBatchesGUI::News::
474 News(RecordBatches &batches, int x, int y)
475  : BC_TextBox(x, y, xS(200), 1, batches.get_editing_batch()->news),
476    batches(batches)
477 {
478 }
479
480 int RecordBatchesGUI::News::
481 handle_event()
482 {
483         return 1;
484 }
485
486
487 RecordBatchesGUI::NewBatch::
488 NewBatch(RecordBatches &batches, int x, int y)
489  : BC_GenericButton(x, y, _("New")),
490    batches(batches)
491 {
492         set_tooltip(_("Create new clip."));
493 }
494
495 int RecordBatchesGUI::NewBatch::
496 handle_event()
497 {
498         int new_item = batches.total()-1;
499         batches.editing_item = new_item;
500         batches.gui->update_batches(new_item);
501         return 1;
502 }
503
504
505 RecordBatchesGUI::DeleteBatch::
506 DeleteBatch(RecordBatches &batches, int x, int y)
507  : BC_GenericButton(x, y, _("Delete")),
508    batches(batches)
509 {
510         set_tooltip(_("Delete clip."));
511 }
512
513 int RecordBatchesGUI::DeleteBatch::
514 handle_event()
515 {
516         batches.gui->update_batches();
517         return 1;
518 }
519
520
521 RecordBatchesGUI::StartBatches::
522 StartBatches(RecordBatches &batches, int x, int y)
523  : BC_GenericButton(x, y, _("Start")), batches(batches)
524 {
525         set_tooltip(_("Start batch recording\nfrom the current position."));
526 }
527
528 int RecordBatchesGUI::StartBatches::
529 handle_event()
530 {
531         batches.batch_active = 1;
532         return 1;
533 }
534
535
536 RecordBatchesGUI::StopBatches::
537 StopBatches(RecordBatches &batches, int x, int y)
538  : BC_GenericButton(x, y, _("Stop")), batches(batches)
539 {
540 }
541
542 int RecordBatchesGUI::StopBatches::
543 handle_event()
544 {
545         batches.batch_active = 0;
546         return 1;
547 }
548
549
550 RecordBatchesGUI::ActivateBatch::
551 ActivateBatch(RecordBatches &batches, int x, int y)
552  : BC_GenericButton(x, y, _("Activate")), batches(batches)
553 {
554         set_tooltip(_("Make the highlighted\nclip active."));
555 }
556
557 int RecordBatchesGUI::ActivateBatch::
558 handle_event()
559 {
560         return 1;
561 }
562
563
564 RecordBatchesGUI::ClearBatch::
565 ClearBatch(RecordBatches &batches, int x, int y)
566  : BC_GenericButton(x, y, _("Clear")), batches(batches)
567 {
568         set_tooltip(_("Delete all clips."));
569 }
570
571 int RecordBatchesGUI::ClearBatch::
572 handle_event()
573 {
574         batches.gui->update_batches();
575         return 1;
576 }
577