full screen vicon view popup
[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         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 && startsource_in_selection == startsource)
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                         }
557                         else
558                         {   // Clear entire previous edit
559                                 cut_length = previous->length;
560                                 previous->length = 0;
561                                 length += cut_length;
562                                 startsource -= cut_length;
563                                 startproject -= cut_length;
564                         }
565                 }
566         }
567         else
568         if(edit_mode == MOVE_NO_EDITS)
569         {
570                 startsource -= cut_length;
571         }
572
573 // Fix infinite length files
574         if(startsource < 0) startsource = 0;
575         return 0;
576 }
577
578 int Edit::shift_end_in(int edit_mode,
579         int64_t newposition,
580         int64_t oldposition,
581         int edit_edits,
582         int edit_labels,
583         int edit_plugins,
584         int edit_autos,
585         Edits *trim_edits)
586 {
587         int64_t cut_length = oldposition - newposition;
588
589         if(edit_mode == MOVE_ALL_EDITS)
590         {
591 //printf("Edit::shift_end_in 1\n");
592                 if(newposition > startproject)
593                 {        // clear partial edit
594 //printf("Edit::shift_end_in %p %p\n", track->edits, edits);
595                         edits->clear_recursive(newposition,
596                                 oldposition,
597                                 edit_edits,
598                                 edit_labels,
599                                 edit_plugins,
600                                 edit_autos,
601                                 trim_edits);
602                 }
603                 else
604                 {        // clear entire edit
605                         edits->clear_recursive(startproject,
606                                 oldposition,
607                                 edit_edits,
608                                 edit_labels,
609                                 edit_plugins,
610                                 edit_autos,
611                                 trim_edits);
612                 }
613         }
614         else
615         if(edit_mode == MOVE_ONE_EDIT)
616         {
617                 if(next)
618                 {
619                         if(next->asset)
620                         {
621                                 int64_t end_source = next->get_source_end(1);
622
623                                 if(end_source > 0 && next->startsource - cut_length < 0)
624                                 {
625                                         cut_length = next->startsource;
626                                 }
627                         }
628
629                         if(cut_length < length)
630                         {
631                                 length -= cut_length;
632                                 next->startproject -= cut_length;
633                                 next->startsource -= cut_length;
634                                 next->length += cut_length;
635 //printf("Edit::shift_end_in 2 %d\n", cut_length);
636                         }
637                         else
638                         {
639                                 cut_length = length;
640                                 next->length += cut_length;
641                                 next->startsource -= cut_length;
642                                 next->startproject -= cut_length;
643                                 length -= cut_length;
644                         }
645                 }
646                 else
647                 {
648                         if(cut_length < length)
649                         {
650                                 length -= cut_length;
651                         }
652                         else
653                         {
654                                 cut_length = length;
655                                 edits->clear_recursive(startproject,
656                                         oldposition,
657                                         edit_edits,
658                                         edit_labels,
659                                         edit_plugins,
660                                         edit_autos,
661                                         trim_edits);
662                         }
663                 }
664         }
665         else
666 // Does nothing for plugins
667         if(edit_mode == MOVE_NO_EDITS)
668         {
669 //printf("Edit::shift_end_in 3\n");
670                 int64_t end_source = get_source_end(1);
671                 if(end_source > 0 && startsource < cut_length)
672                 {
673                         cut_length = startsource;
674                 }
675                 startsource -= cut_length;
676         }
677         return 0;
678 }
679
680 int Edit::shift_end_out(int edit_mode,
681         int64_t newposition,
682         int64_t oldposition,
683         int edit_edits,
684         int edit_labels,
685         int edit_plugins,
686         int edit_autos,
687         Edits *trim_edits)
688 {
689         int64_t cut_length = newposition - oldposition;
690         int64_t endsource = get_source_end(startsource + length + cut_length);
691
692 // check end of edit against end of source file
693         if(endsource > 0 && startsource + length + cut_length > endsource)
694                 cut_length = endsource - startsource - length;
695
696 //printf("Edit::shift_end_out 1 %jd %d %d %d\n", oldposition, newposition, this->length, cut_length);
697         if(edit_mode == MOVE_ALL_EDITS)
698         {
699 // Extend length
700                 this->length += cut_length;
701
702 // Effects are shifted in length extension
703                 if(edit_plugins)
704                         edits->shift_effects_recursive(oldposition /* startproject */,
705                                 cut_length,
706                                 edit_autos);
707                 if(edit_autos)
708                         edits->shift_keyframes_recursive(oldposition /* startproject */,
709                                 cut_length);
710
711                 for(Edit* current_edit = next; current_edit; current_edit = current_edit->next)
712                 {
713                         current_edit->startproject += cut_length;
714                 }
715         }
716         else
717         if(edit_mode == MOVE_ONE_EDIT)
718         {
719                 if(next)
720                 {
721                         if(cut_length < next->length)
722                         {
723                                 length += cut_length;
724                                 next->startproject += cut_length;
725                                 next->startsource += cut_length;
726                                 next->length -= cut_length;
727 //printf("Edit::shift_end_out %d cut_length=%d\n", __LINE__, cut_length);
728                         }
729                         else
730                         {
731 //printf("Edit::shift_end_out %d cut_length=%d next->length=%d\n", __LINE__, cut_length, next->length);
732                                 cut_length = next->length;
733                                 next->startproject += next->length;
734                                 next->startsource += next->length;
735                                 next->length = 0;
736                                 length += cut_length;
737 //track->dump();
738                         }
739                 }
740                 else
741                 {
742                         length += cut_length;
743                 }
744         }
745         else
746         if(edit_mode == MOVE_NO_EDITS)
747         {
748                 startsource += cut_length;
749         }
750         return 0;
751 }
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 int Edit::popup_transition(float view_start, float zoom_units, int cursor_x, int cursor_y)
782 {
783         int64_t left, right, left_unit, right_unit;
784         if(!transition) return 0;
785         get_handle_parameters(left, right, left_unit, right_unit, view_start, zoom_units);
786
787         if(cursor_x > left && cursor_x < right)
788         {
789 //              transition->popup_transition(cursor_x, cursor_y);
790                 return 1;
791         }
792         return 0;
793 }
794
795 int Edit::select_handle(float view_start, float zoom_units, int cursor_x, int cursor_y, int64_t &selection)
796 {
797         int64_t left, right, left_unit, right_unit;
798         get_handle_parameters(left, right, left_unit, right_unit, view_start, zoom_units);
799
800         int64_t pixel1, pixel2;
801         pixel1 = left;
802         pixel2 = pixel1 + 10;
803
804 // test left edit
805 // cursor_x is faked in acanvas
806         if(cursor_x >= pixel1 && cursor_x <= pixel2)
807         {
808                 selection = left_unit;
809                 return 1;     // left handle
810         }
811
812         //int64_t endproject = startproject + length;
813         pixel2 = right;
814         pixel1 = pixel2 - 10;
815
816 // test right edit
817         if(cursor_x >= pixel1 && cursor_x <= pixel2)
818         {
819                 selection = right_unit;
820                 return 2;     // right handle
821         }
822         return 0;
823 }
824
825 void Edit::get_title(char *title)
826 {
827         if( user_title[0] ) {
828                 strcpy(title, user_title);
829                 return;
830         }
831         Indexable *idxbl = asset ? (Indexable*)asset : (Indexable*)nested_edl;
832         if( !idxbl ) {
833                 title[0] = 0;
834                 return;
835         }
836         FileSystem fs;
837         fs.extract_name(title, idxbl->path);
838         if( asset || track->data_type == TRACK_AUDIO ) {
839                 char number[BCSTRLEN];
840                 sprintf(number, " #%d", channel + 1);
841                 strcat(title, number);
842         }
843 }
844