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