drag+drop honours labels/plugins/autos, new drag icon, phantom keyframe fix
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / edit.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "asset.h"
23 #include "assets.h"
24 #include "bcsignals.h"
25 #include "clip.h"
26 #include "edit.h"
27 #include "edits.h"
28 #include "edl.h"
29 #include "edlsession.h"
30 #include "filexml.h"
31 #include "filesystem.h"
32 #include "localsession.h"
33 #include "plugin.h"
34 #include "mainsession.h"
35 #include "strack.h"
36 #include "trackcanvas.h"
37 #include "tracks.h"
38 #include "transition.h"
39 #include <string.h>
40
41
42 Edit::Edit()
43 {
44         reset();
45 }
46
47 Edit::Edit(EDL *edl, Track *track)
48 {
49         reset();
50         this->edl = edl;
51         this->track = track;
52         if(track) this->edits = track->edits;
53         id = EDL::next_id();
54 }
55
56 Edit::Edit(EDL *edl, Edits *edits)
57 {
58         reset();
59         this->edl = edl;
60         this->edits = edits;
61         if(edits) this->track = edits->track;
62         id = EDL::next_id();
63 }
64
65 Edit::~Edit()
66 {
67 //printf("Edit::~Edit 1\n");
68         if(transition) delete transition;
69 //printf("Edit::~Edit 2\n");
70 }
71
72 void Edit::reset()
73 {
74         edl = 0;
75         track = 0;
76         edits = 0;
77         startsource = 0;
78         startproject = 0;
79         length = 0;
80         asset = 0;
81         transition = 0;
82         channel = 0;
83         user_title[0] = 0;
84         nested_edl = 0;
85         is_plugin = 0;
86         is_selected = 0;
87         hard_left = 0;
88         hard_right = 0;
89         color = 0;
90         group_id = 0;
91 }
92
93 Indexable* Edit::get_source()
94 {
95         if(asset) return asset;
96         if(nested_edl) return nested_edl;
97         return 0;
98 }
99
100 int Edit::copy(int64_t start,
101         int64_t end,
102         FileXML *file,
103         const char *output_path)
104 {
105 // variables
106 //printf("Edit::copy 1\n");
107
108         int64_t endproject = startproject + length;
109         int result;
110
111         if((startproject >= start && startproject <= end) ||  // startproject in range
112                  (endproject <= end && endproject >= start) ||     // endproject in range
113                  (startproject <= start && endproject >= end))    // range in project
114         {
115 // edit is in range
116                 int64_t startproject_in_selection = startproject; // start of edit in selection in project
117                 int64_t startsource_in_selection = startsource; // start of source in selection in source
118                 //int64_t endsource_in_selection = startsource + length; // end of source in selection
119                 int64_t length_in_selection = length;             // length of edit in selection
120 //printf("Edit::copy 2\n");
121
122                 if(startproject < start)
123                 {         // start is after start of edit in project
124                         int64_t length_difference = start - startproject;
125
126                         startsource_in_selection += length_difference;
127                         startproject_in_selection += length_difference;
128                         length_in_selection -= length_difference;
129                 }
130 //printf("Edit::copy 3\n");
131
132                 if(endproject > end)
133                 {         // end is before end of edit in project
134                         length_in_selection = end - startproject_in_selection;
135                 }
136
137 //printf("Edit::copy 4\n");
138                 if(file)    // only if not counting
139                 {
140                         file->tag.set_title("EDIT");
141                         file->tag.set_property("STARTSOURCE", startsource_in_selection);
142                         file->tag.set_property("CHANNEL", (int64_t)channel);
143                         file->tag.set_property("LENGTH", length_in_selection);
144                         file->tag.set_property("HARD_LEFT", hard_left);
145                         file->tag.set_property("HARD_RIGHT", hard_right);
146                         file->tag.set_property("COLOR", color);
147                         file->tag.set_property("GROUP_ID", group_id);
148                         if(user_title[0]) file->tag.set_property("USER_TITLE", user_title);
149 //printf("Edit::copy 5\n");
150
151                         copy_properties_derived(file, length_in_selection);
152
153                         file->append_tag();
154 //                      file->append_newline();
155 //printf("Edit::copy 6\n");
156
157                         if(nested_edl)
158                         {
159                                 file->tag.set_title("NESTED_EDL");
160                                 file->tag.set_property("SRC", nested_edl->path);
161                                 file->append_tag();
162                                 file->tag.set_title("/NESTED_EDL");
163                                 file->append_tag();
164                                 file->append_newline();
165                         }
166
167                         if(asset)
168                         {
169 //printf("Edit::copy 6 %s\n", asset->path);
170                                 char stored_path[BCTEXTLEN];
171                                 char asset_directory[BCTEXTLEN];
172                                 char output_directory[BCTEXTLEN];
173                                 FileSystem fs;
174
175 //printf("Edit::copy %d %s\n", __LINE__, asset->path);
176                                 fs.extract_dir(asset_directory, asset->path);
177 //printf("Edit::copy %d %s\n", __LINE__, asset->path);
178
179                                 if(output_path)
180                                         fs.extract_dir(output_directory, output_path);
181                                 else
182                                         output_directory[0] = 0;
183 //printf("Edit::copy %s, %s %s, %s\n", asset->path, asset_directory, output_path, output_directory);
184
185                                 if(output_path && !strcmp(asset_directory, output_directory))
186                                         fs.extract_name(stored_path, asset->path);
187                                 else
188                                         strcpy(stored_path, asset->path);
189
190                                 file->tag.set_title("FILE");
191                                 file->tag.set_property("SRC", stored_path);
192                                 file->append_tag();
193                                 file->tag.set_title("/FILE");
194                                 file->append_tag();
195                         }
196
197                         if(transition && startsource_in_selection == startsource)
198                         {
199                                 transition->save_xml(file);
200                         }
201
202 //printf("Edit::copy 7\n");
203                         file->tag.set_title("/EDIT");
204                         file->append_tag();
205                         file->append_newline();
206 //printf("Edit::copy 8\n");
207                 }
208 //printf("Edit::copy 9\n");
209                 result = 1;
210         }
211         else
212         {
213                 result = 0;
214         }
215 //printf("Edit::copy 10\n");
216         return result;
217 }
218
219
220 int64_t Edit::get_source_end(int64_t default_)
221 {
222         return default_;
223 }
224
225 void Edit::insert_transition(char *title)
226 {
227 //printf("Edit::insert_transition this=%p title=%p title=%s\n", this, title, title);
228         delete transition;
229         transition = new Transition(edl, this, title,
230                 track->to_units(edl->session->default_transition_length, 1));
231 }
232
233 void Edit::detach_transition()
234 {
235         if(transition) delete transition;
236         transition = 0;
237 }
238
239 int Edit::silence()
240 {
241         return (track->data_type != TRACK_SUBTITLE ?
242                 asset || nested_edl :
243                 *((SEdit *)this)->get_text()) ? 0 : 1;
244 }
245
246 void Edit::set_selected(int v)
247 {
248         if( group_id )
249                 edl->tracks->set_group_selected(group_id, v);
250         else
251                 is_selected = v >= 0 ? v : !is_selected ? 1 : 0;
252 }
253
254 void Edit::copy_from(Edit *edit)
255 {
256         this->nested_edl = edl->nested_edls.get_nested(edit->nested_edl);
257         this->asset = edl->assets->update(edit->asset);
258         this->startsource = edit->startsource;
259         this->startproject = edit->startproject;
260         this->length = edit->length;
261         this->hard_left = edit->hard_left;
262         this->hard_right = edit->hard_right;
263         this->color = edit->color;
264         this->group_id = edit->group_id;
265         strcpy (this->user_title, edit->user_title);
266
267         if(edit->transition)
268         {
269                 if(!transition) transition = new Transition(edl,
270                         this,
271                         edit->transition->title,
272                         edit->transition->length);
273                 *this->transition = *edit->transition;
274         }
275         this->channel = edit->channel;
276 }
277
278 void Edit::equivalent_output(Edit *edit, int64_t *result)
279 {
280 // End of edit changed
281         if(startproject + length != edit->startproject + edit->length)
282         {
283                 int64_t new_length = MIN(startproject + length,
284                         edit->startproject + edit->length);
285                 if(*result < 0 || new_length < *result)
286                         *result = new_length;
287         }
288
289         if(
290 // Different nested EDLs
291                 (edit->nested_edl && !nested_edl) ||
292                 (!edit->nested_edl && nested_edl) ||
293 // Different assets
294                 (edit->asset == 0 && asset != 0) ||
295                 (edit->asset != 0 && asset == 0) ||
296 // different transitions
297                 (edit->transition == 0 && transition != 0) ||
298                 (edit->transition != 0 && transition == 0) ||
299 // Position changed
300                 (startproject != edit->startproject) ||
301                 (startsource != edit->startsource) ||
302 // Transition changed
303                 (transition && edit->transition &&
304                         !transition->identical(edit->transition)) ||
305 // Asset changed
306                 (asset && edit->asset &&
307                         !asset->equivalent(*edit->asset, 1, 1, edl)) ||
308 // Nested EDL changed
309                 (nested_edl && edit->nested_edl &&
310                         strcmp(nested_edl->path, edit->nested_edl->path))
311                 )
312         {
313 // Start of edit changed
314                 if(*result < 0 || startproject < *result) *result = startproject;
315         }
316 }
317
318
319 Edit& Edit::operator=(Edit& edit)
320 {
321 //printf("Edit::operator= called\n");
322         copy_from(&edit);
323         return *this;
324 }
325
326 void Edit::synchronize_params(Edit *edit)
327 {
328         copy_from(edit);
329 }
330
331
332 // Comparison for ResourcePixmap drawing
333 int Edit::identical(Edit &edit)
334 {
335         int result = (this->nested_edl == edit.nested_edl &&
336                 this->asset == edit.asset &&
337                 this->startsource == edit.startsource &&
338                 this->startproject == edit.startproject &&
339                 this->length == edit.length &&
340                 this->transition == edit.transition &&
341                 this->channel == edit.channel);
342         return result;
343 }
344
345 int Edit::operator==(Edit &edit)
346 {
347         return identical(edit);
348 }
349
350 double Edit::frames_per_picon()
351 {
352         return Units::round(picon_w()) / frame_w();
353 }
354
355 double Edit::frame_w()
356 {
357         return track->from_units(1) *
358                 edl->session->sample_rate /
359                 edl->local_session->zoom_sample;
360 }
361
362 double Edit::picon_w()
363 {
364         int w = 0, h = 0;
365         if(asset) {
366                 w = asset->width;
367                 h = asset->height;
368         }
369         else if(nested_edl) {
370                 w = nested_edl->session->output_w;
371                 h = nested_edl->session->output_h;
372         }
373         return w>0 && h>0 ? ((double)edl->local_session->zoom_track*w)/h : 0;
374 }
375
376 int Edit::picon_h()
377 {
378         return edl->local_session->zoom_track;
379 }
380
381
382 int Edit::dump(FILE *fp)
383 {
384         fprintf(fp,"     EDIT %p\n", this); fflush(fp);
385         fprintf(fp,"      nested_edl=%p %s asset=%p %s\n",
386                 nested_edl,
387                 nested_edl ? nested_edl->path : "",
388                 asset,
389                 asset ? asset->path : "");
390         fflush(fp);
391         fprintf(fp,"      channel %d, color %08x, group_id %d, is_selected %d\n",
392                 channel, color, group_id, is_selected);
393         if(transition)
394         {
395                 fprintf(fp,"      TRANSITION %p\n", transition);
396                 transition->dump(fp);
397         }
398         fprintf(fp,"      startsource %jd startproject %jd hard lt/rt %d/%d length %jd\n",
399                 startsource, startproject, hard_left, hard_right, length); fflush(fp);
400         return 0;
401 }
402
403 int Edit::load_properties(FileXML *file, int64_t &startproject)
404 {
405         startsource = file->tag.get_property("STARTSOURCE", (int64_t)0);
406         length = file->tag.get_property("LENGTH", (int64_t)0);
407         hard_left = file->tag.get_property("HARD_LEFT", (int64_t)0);
408         hard_right = file->tag.get_property("HARD_RIGHT", (int64_t)0);
409         color = file->tag.get_property("COLOR", 0);
410         group_id = file->tag.get_property("GROUP_ID", group_id);
411         user_title[0] = 0;
412         file->tag.get_property("USER_TITLE", user_title);
413         this->startproject = startproject;
414         load_properties_derived(file);
415         return 0;
416 }
417
418 void Edit::shift(int64_t difference)
419 {
420 //printf("Edit::shift 1 %p %jd %jd\n", this, startproject, difference);
421         startproject += difference;
422 //printf("Edit::shift 2 %jd %jd\n", startproject, difference);
423 }
424
425 int Edit::shift_start_in(int edit_mode,
426         int64_t newposition,
427         int64_t oldposition,
428         int edit_edits,
429         int edit_labels,
430         int edit_plugins,
431         int edit_autos,
432         Edits *trim_edits)
433 {
434         int64_t cut_length = newposition - oldposition;
435         int64_t end_previous_source, end_source;
436
437         if(edit_mode == MOVE_ALL_EDITS)
438         {
439                 if(cut_length < length)
440                 {        // clear partial
441                         edits->clear_recursive(oldposition,
442                                 newposition,
443                                 edit_edits,
444                                 edit_labels,
445                                 edit_plugins,
446                                 edit_autos,
447                                 trim_edits);
448                 }
449                 else
450                 {        // clear entire
451                         edits->clear_recursive(oldposition,
452                                 startproject + length,
453                                 edit_edits,
454                                 edit_labels,
455                                 edit_plugins,
456                                 edit_autos,
457                                 trim_edits);
458                 }
459         }
460         else
461         if(edit_mode == MOVE_ONE_EDIT)
462         {
463 // Paste silence and cut
464 //printf("Edit::shift_start_in 1\n");
465                 if(!previous)
466                 {
467                         Edit *new_edit = edits->create_edit();
468                         new_edit->startproject = this->startproject;
469                         new_edit->length = 0;
470                         edits->insert_before(this,
471                                 new_edit);
472                 }
473 //printf("Edit::shift_start_in 2 %p\n", previous);
474
475                 end_previous_source = previous->get_source_end(previous->startsource + previous->length + cut_length);
476                 if(end_previous_source > 0 &&
477                         previous->startsource + previous->length + cut_length > end_previous_source)
478                         cut_length = end_previous_source - previous->startsource - previous->length;
479
480                 if(cut_length < length)
481                 {               // Move in partial
482                         startproject += cut_length;
483                         startsource += cut_length;
484                         length -= cut_length;
485                         previous->length += cut_length;
486 //printf("Edit::shift_start_in 2\n");
487                 }
488                 else
489                 {               // Clear entire edit
490                         cut_length = length;
491                         previous->length += cut_length;
492                         for(Edit* current_edit = this; current_edit; current_edit = current_edit->next)
493                         {
494                                 current_edit->startproject += cut_length;
495                         }
496                         edits->clear_recursive(oldposition + cut_length,
497                                 startproject + cut_length,
498                                 edit_edits,
499                                 edit_labels,
500                                 edit_plugins,
501                                 edit_autos,
502                                 trim_edits);
503                 }
504 //printf("Edit::shift_start_in 3\n");
505         }
506         else
507         if(edit_mode == MOVE_NO_EDITS)
508         {
509                 end_source = get_source_end(startsource + length + cut_length);
510                 if(end_source > 0 && startsource + length + cut_length > end_source)
511                         cut_length = end_source - startsource - length;
512
513                 startsource += cut_length;
514         }
515         return 0;
516 }
517
518 int Edit::shift_start_out(int edit_mode,
519         int64_t newposition,
520         int64_t oldposition,
521         int edit_edits,
522         int edit_labels,
523         int edit_plugins,
524         int edit_autos,
525         Edits *trim_edits)
526 {
527         int64_t cut_length = oldposition - newposition;
528
529
530         if(asset || nested_edl)
531         {
532                 int64_t end_source = get_source_end(1);
533
534 //printf("Edit::shift_start_out 1 %jd %jd\n", startsource, cut_length);
535                 if(end_source > 0 && startsource < cut_length)
536                 {
537                         cut_length = startsource;
538                 }
539         }
540
541         if(edit_mode == MOVE_ALL_EDITS)
542         {
543 //printf("Edit::shift_start_out 10 %jd\n", cut_length);
544                 startsource -= cut_length;
545                 length += cut_length;
546
547                 if(edit_autos)
548                         edits->shift_keyframes_recursive(startproject,
549                                 cut_length);
550                 if(edit_plugins)
551                         edits->shift_effects_recursive(startproject,
552                                 cut_length,
553                                 edit_autos);
554
555                 for(Edit* current_edit = next; current_edit; current_edit = current_edit->next)
556                 {
557                         current_edit->startproject += cut_length;
558                 }
559         }
560         else
561         if(edit_mode == MOVE_ONE_EDIT)
562         {
563                 if(previous)
564                 {
565                         if(cut_length < previous->length)
566                         {   // Cut into previous edit
567                                 previous->length -= cut_length;
568                                 startproject -= cut_length;
569                                 startsource -= cut_length;
570                                 length += cut_length;
571                         }
572                         else
573                         {   // Clear entire previous edit
574                                 cut_length = previous->length;
575                                 previous->length = 0;
576                                 length += cut_length;
577                                 startsource -= cut_length;
578                                 startproject -= cut_length;
579                         }
580                 }
581         }
582         else
583         if(edit_mode == MOVE_NO_EDITS)
584         {
585                 startsource -= cut_length;
586         }
587
588 // Fix infinite length files
589         if(startsource < 0) startsource = 0;
590         return 0;
591 }
592
593 int Edit::shift_end_in(int edit_mode,
594         int64_t newposition,
595         int64_t oldposition,
596         int edit_edits,
597         int edit_labels,
598         int edit_plugins,
599         int edit_autos,
600         Edits *trim_edits)
601 {
602         int64_t cut_length = oldposition - newposition;
603
604         if(edit_mode == MOVE_ALL_EDITS)
605         {
606 //printf("Edit::shift_end_in 1\n");
607                 if(newposition > startproject)
608                 {        // clear partial edit
609 //printf("Edit::shift_end_in %p %p\n", track->edits, edits);
610                         edits->clear_recursive(newposition,
611                                 oldposition,
612                                 edit_edits,
613                                 edit_labels,
614                                 edit_plugins,
615                                 edit_autos,
616                                 trim_edits);
617                 }
618                 else
619                 {        // clear entire edit
620                         edits->clear_recursive(startproject,
621                                 oldposition,
622                                 edit_edits,
623                                 edit_labels,
624                                 edit_plugins,
625                                 edit_autos,
626                                 trim_edits);
627                 }
628         }
629         else
630         if(edit_mode == MOVE_ONE_EDIT)
631         {
632                 if(next)
633                 {
634                         if(next->asset)
635                         {
636                                 int64_t end_source = next->get_source_end(1);
637
638                                 if(end_source > 0 && next->startsource - cut_length < 0)
639                                 {
640                                         cut_length = next->startsource;
641                                 }
642                         }
643
644                         if(cut_length < length)
645                         {
646                                 length -= cut_length;
647                                 next->startproject -= cut_length;
648                                 next->startsource -= cut_length;
649                                 next->length += cut_length;
650 //printf("Edit::shift_end_in 2 %d\n", cut_length);
651                         }
652                         else
653                         {
654                                 cut_length = length;
655                                 next->length += cut_length;
656                                 next->startsource -= cut_length;
657                                 next->startproject -= cut_length;
658                                 length -= cut_length;
659                         }
660                 }
661                 else
662                 {
663                         if(cut_length < length)
664                         {
665                                 length -= cut_length;
666                         }
667                         else
668                         {
669                                 cut_length = length;
670                                 edits->clear_recursive(startproject,
671                                         oldposition,
672                                         edit_edits,
673                                         edit_labels,
674                                         edit_plugins,
675                                         edit_autos,
676                                         trim_edits);
677                         }
678                 }
679         }
680         else
681 // Does nothing for plugins
682         if(edit_mode == MOVE_NO_EDITS)
683         {
684 //printf("Edit::shift_end_in 3\n");
685                 int64_t end_source = get_source_end(1);
686                 if(end_source > 0 && startsource < cut_length)
687                 {
688                         cut_length = startsource;
689                 }
690                 startsource -= cut_length;
691         }
692         return 0;
693 }
694
695 int Edit::shift_end_out(int edit_mode,
696         int64_t newposition,
697         int64_t oldposition,
698         int edit_edits,
699         int edit_labels,
700         int edit_plugins,
701         int edit_autos,
702         Edits *trim_edits)
703 {
704         int64_t cut_length = newposition - oldposition;
705         int64_t endsource = get_source_end(startsource + length + cut_length);
706
707 // check end of edit against end of source file
708         if(endsource > 0 && startsource + length + cut_length > endsource)
709                 cut_length = endsource - startsource - length;
710
711 //printf("Edit::shift_end_out 1 %jd %d %d %d\n", oldposition, newposition, this->length, cut_length);
712         if(edit_mode == MOVE_ALL_EDITS)
713         {
714 // Extend length
715                 this->length += cut_length;
716
717 // Effects are shifted in length extension
718                 if(edit_plugins)
719                         edits->shift_effects_recursive(oldposition /* startproject */,
720                                 cut_length,
721                                 edit_autos);
722                 if(edit_autos)
723                         edits->shift_keyframes_recursive(oldposition /* startproject */,
724                                 cut_length);
725
726                 for(Edit* current_edit = next; current_edit; current_edit = current_edit->next)
727                 {
728                         current_edit->startproject += cut_length;
729                 }
730         }
731         else
732         if(edit_mode == MOVE_ONE_EDIT)
733         {
734                 if(next)
735                 {
736                         if(cut_length < next->length)
737                         {
738                                 length += cut_length;
739                                 next->startproject += cut_length;
740                                 next->startsource += cut_length;
741                                 next->length -= cut_length;
742 //printf("Edit::shift_end_out %d cut_length=%d\n", __LINE__, cut_length);
743                         }
744                         else
745                         {
746 //printf("Edit::shift_end_out %d cut_length=%d next->length=%d\n", __LINE__, cut_length, next->length);
747                                 cut_length = next->length;
748                                 next->startproject += next->length;
749                                 next->startsource += next->length;
750                                 next->length = 0;
751                                 length += cut_length;
752 //track->dump();
753                         }
754                 }
755                 else
756                 {
757                         length += cut_length;
758                 }
759         }
760         else
761         if(edit_mode == MOVE_NO_EDITS)
762         {
763                 startsource += cut_length;
764         }
765         return 0;
766 }
767
768 int Edit::popup_transition(float view_start, float zoom_units, int cursor_x, int cursor_y)
769 {
770         int64_t left, right, left_unit, right_unit;
771         if(!transition) return 0;
772         get_handle_parameters(left, right, left_unit, right_unit, view_start, zoom_units);
773
774         if(cursor_x > left && cursor_x < right)
775         {
776 //              transition->popup_transition(cursor_x, cursor_y);
777                 return 1;
778         }
779         return 0;
780 }
781
782 int Edit::select_handle(float view_start, float zoom_units, int cursor_x, int cursor_y, int64_t &selection)
783 {
784         int64_t left, right, left_unit, right_unit;
785         get_handle_parameters(left, right, left_unit, right_unit, view_start, zoom_units);
786
787         int64_t pixel1, pixel2;
788         pixel1 = left;
789         pixel2 = pixel1 + 10;
790
791 // test left edit
792 // cursor_x is faked in acanvas
793         if(cursor_x >= pixel1 && cursor_x <= pixel2)
794         {
795                 selection = left_unit;
796                 return 1;     // left handle
797         }
798
799         //int64_t endproject = startproject + length;
800         pixel2 = right;
801         pixel1 = pixel2 - 10;
802
803 // test right edit
804         if(cursor_x >= pixel1 && cursor_x <= pixel2)
805         {
806                 selection = right_unit;
807                 return 2;     // right handle
808         }
809         return 0;
810 }
811
812 void Edit::get_title(char *title)
813 {
814         if( user_title[0] ) {
815                 strcpy(title, user_title);
816                 return;
817         }
818         Indexable *idxbl = asset ? (Indexable*)asset : (Indexable*)nested_edl;
819         if( !idxbl ) {
820                 title[0] = 0;
821                 return;
822         }
823         FileSystem fs;
824         fs.extract_name(title, idxbl->path);
825         if( asset || track->data_type == TRACK_AUDIO ) {
826                 char number[BCSTRLEN];
827                 sprintf(number, " #%d", channel + 1);
828                 strcat(title, number);
829         }
830 }
831