~ubuntu-branches/ubuntu/trusty/aegisub/trusty

« back to all changes in this revision

Viewing changes to src/subtitle_format.cpp

  • Committer: Package Import Robot
  • Author(s): Sebastian Reichel
  • Date: 2012-03-16 22:58:00 UTC
  • Revision ID: package-import@ubuntu.com-20120316225800-yfb8h9e5n04rk46a
Tags: upstream-2.1.9
ImportĀ upstreamĀ versionĀ 2.1.9

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright (c) 2006, Rodrigo Braz Monteiro
 
2
// All rights reserved.
 
3
//
 
4
// Redistribution and use in source and binary forms, with or without
 
5
// modification, are permitted provided that the following conditions are met:
 
6
//
 
7
//   * Redistributions of source code must retain the above copyright notice,
 
8
//     this list of conditions and the following disclaimer.
 
9
//   * Redistributions in binary form must reproduce the above copyright notice,
 
10
//     this list of conditions and the following disclaimer in the documentation
 
11
//     and/or other materials provided with the distribution.
 
12
//   * Neither the name of the Aegisub Group nor the names of its contributors
 
13
//     may be used to endorse or promote products derived from this software
 
14
//     without specific prior written permission.
 
15
//
 
16
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 
17
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 
18
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
19
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 
20
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 
21
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 
22
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 
23
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 
24
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 
25
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 
26
// POSSIBILITY OF SUCH DAMAGE.
 
27
//
 
28
// -----------------------------------------------------------------------------
 
29
//
 
30
// AEGISUB
 
31
//
 
32
// Website: http://aegisub.cellosoft.com
 
33
// Contact: mailto:zeratul@cellosoft.com
 
34
//
 
35
 
 
36
 
 
37
///////////
 
38
// Headers
 
39
#include "config.h"
 
40
 
 
41
#include <wx/intl.h>
 
42
#include <wx/choicdlg.h>
 
43
#include "subtitle_format.h"
 
44
#include "subtitle_format_ass.h"
 
45
#include "subtitle_format_srt.h"
 
46
#include "subtitle_format_txt.h"
 
47
#include "subtitle_format_ttxt.h"
 
48
#include "subtitle_format_mkv.h"
 
49
#include "subtitle_format_microdvd.h"
 
50
#include "subtitle_format_ebu3264.h"
 
51
#include "subtitle_format_encore.h"
 
52
#include "subtitle_format_transtation.h"
 
53
#include "subtitle_format_dvd.h"
 
54
#include "ass_file.h"
 
55
#include "vfr.h"
 
56
 
 
57
 
 
58
///////////////
 
59
// Constructor
 
60
SubtitleFormat::SubtitleFormat() {
 
61
        Line = NULL;
 
62
        Register();
 
63
        isCopy = false;
 
64
}
 
65
 
 
66
 
 
67
//////////////
 
68
// Destructor
 
69
SubtitleFormat::~SubtitleFormat () {
 
70
        Remove();
 
71
}
 
72
 
 
73
 
 
74
////////
 
75
// List
 
76
std::list<SubtitleFormat*> SubtitleFormat::formats;
 
77
bool SubtitleFormat::loaded = false;
 
78
 
 
79
 
 
80
//////////////
 
81
// Set target
 
82
void SubtitleFormat::SetTarget(AssFile *file) {
 
83
        ClearCopy();
 
84
        if (!file) Line = NULL;
 
85
        else Line = &file->Line;
 
86
        assFile = file;
 
87
}
 
88
 
 
89
 
 
90
///////////////
 
91
// Create copy
 
92
void SubtitleFormat::CreateCopy() {
 
93
        SetTarget(new AssFile(*assFile));
 
94
        isCopy = true;
 
95
}
 
96
 
 
97
 
 
98
//////////////
 
99
// Clear copy
 
100
void SubtitleFormat::ClearCopy() {
 
101
        if (isCopy) {
 
102
                delete assFile;
 
103
                assFile = NULL;
 
104
                isCopy = false;
 
105
        }
 
106
}
 
107
 
 
108
 
 
109
///////////////////
 
110
// Clear subtitles
 
111
void SubtitleFormat::Clear() {
 
112
        assFile->Clear();
 
113
}
 
114
 
 
115
 
 
116
////////////////
 
117
// Load default
 
118
void SubtitleFormat::LoadDefault(bool defline) {
 
119
        assFile->LoadDefault(defline);
 
120
}
 
121
 
 
122
 
 
123
////////////
 
124
// Add line
 
125
int SubtitleFormat::AddLine(wxString data,wxString group,int lasttime,int &version,wxString *outgroup) {
 
126
        return assFile->AddLine(data,group,lasttime,version,outgroup);
 
127
}
 
128
 
 
129
 
 
130
///////////////
 
131
// Add formats
 
132
void SubtitleFormat::LoadFormats () {
 
133
        if (!loaded) {
 
134
                new ASSSubtitleFormat();
 
135
                new SRTSubtitleFormat();
 
136
                new TXTSubtitleFormat();
 
137
                new TTXTSubtitleFormat();
 
138
                new MicroDVDSubtitleFormat();
 
139
                new MKVSubtitleFormat();
 
140
                new EncoreSubtitleFormat();
 
141
                new Ebu3264SubtitleFormat();
 
142
                new TranStationSubtitleFormat();
 
143
#ifdef __WXDEBUG__
 
144
                new DVDSubtitleFormat();
 
145
#endif
 
146
        }
 
147
        loaded = true;
 
148
}
 
149
 
 
150
 
 
151
///////////////////
 
152
// Destroy formats
 
153
void SubtitleFormat::DestroyFormats () {
 
154
        std::list<SubtitleFormat*>::iterator cur;
 
155
        for (cur=formats.begin();cur!=formats.end();cur = formats.begin()) {
 
156
                delete *cur;
 
157
        }
 
158
        formats.clear();
 
159
}
 
160
 
 
161
 
 
162
/////////////////////////////
 
163
// Get an appropriate reader
 
164
SubtitleFormat *SubtitleFormat::GetReader(wxString filename) {
 
165
        LoadFormats();
 
166
        std::list<SubtitleFormat*>::iterator cur;
 
167
        SubtitleFormat *reader;
 
168
        for (cur=formats.begin();cur!=formats.end();cur++) {
 
169
                reader = *cur;
 
170
                if (reader->CanReadFile(filename)) return reader;
 
171
        }
 
172
        return NULL;
 
173
}
 
174
 
 
175
 
 
176
/////////////////////////////
 
177
// Get an appropriate writer
 
178
SubtitleFormat *SubtitleFormat::GetWriter(wxString filename) {
 
179
        LoadFormats();
 
180
        std::list<SubtitleFormat*>::iterator cur;
 
181
        SubtitleFormat *writer;
 
182
        for (cur=formats.begin();cur!=formats.end();cur++) {
 
183
                writer = *cur;
 
184
                if (writer->CanWriteFile(filename)) return writer;
 
185
        }
 
186
        return NULL;
 
187
}
 
188
 
 
189
 
 
190
////////////
 
191
// Register
 
192
void SubtitleFormat::Register() {
 
193
        std::list<SubtitleFormat*>::iterator cur;
 
194
        for (cur=formats.begin();cur!=formats.end();cur++) {
 
195
                if (*cur == this) return;
 
196
        }
 
197
        formats.push_back(this);
 
198
}
 
199
 
 
200
 
 
201
//////////
 
202
// Remove
 
203
void SubtitleFormat::Remove() {
 
204
        std::list<SubtitleFormat*>::iterator cur;
 
205
        for (cur=formats.begin();cur!=formats.end();cur++) {
 
206
                if (*cur == this) {
 
207
                        formats.erase(cur);
 
208
                        return;
 
209
                }
 
210
        }
 
211
}
 
212
 
 
213
 
 
214
//////////////////////
 
215
// Get read wildcards
 
216
wxArrayString SubtitleFormat::GetReadWildcards() {
 
217
        return wxArrayString();
 
218
}
 
219
 
 
220
 
 
221
///////////////////////
 
222
// Get write wildcards
 
223
wxArrayString SubtitleFormat::GetWriteWildcards() {
 
224
        return wxArrayString();
 
225
}
 
226
 
 
227
 
 
228
/////////////////////
 
229
// Get wildcard list
 
230
wxString SubtitleFormat::GetWildcards(int mode) {
 
231
        // Ensure it's loaded
 
232
        LoadFormats();
 
233
 
 
234
        // Variables
 
235
        wxArrayString all;
 
236
        wxArrayString cur;
 
237
        wxString wild;
 
238
        wxString final;
 
239
        wxString temp1;
 
240
        wxString temp2;
 
241
 
 
242
        // For each format
 
243
        std::list<SubtitleFormat*>::iterator curIter;
 
244
        SubtitleFormat *format;
 
245
        for (curIter=formats.begin();curIter!=formats.end();curIter++) {
 
246
                // Get list
 
247
                format = *curIter;
 
248
                if (mode == 0) cur = format->GetReadWildcards();
 
249
                else if (mode == 1) cur = format->GetWriteWildcards();
 
250
                temp1.Clear();
 
251
                temp2.Clear();
 
252
 
 
253
                // Has wildcards
 
254
                if (cur.Count()) {
 
255
                        // Process entries
 
256
                        for (unsigned int i=0;i<cur.Count();i++) {
 
257
                                wild = _T("*.") + cur[i];
 
258
                                all.Add(wild);
 
259
                                temp1 += wild + _T(",");
 
260
                                temp2 += wild + _T(";");
 
261
                        }
 
262
 
 
263
                        // Assemble final name
 
264
                        final += format->GetName() + _T(" (") + temp1.Left(temp1.Length()-1) + _T(")|") + temp2.Left(temp2.Length()-1) + _T("|");
 
265
                }
 
266
        }
 
267
 
 
268
        // Add "all formats" list
 
269
        temp1.Clear();
 
270
        temp2.Clear();
 
271
        for (unsigned int i=0;i<all.Count();i++) {
 
272
                temp1 += all[i] + _T(",");
 
273
                temp2 += all[i] + _T(";");
 
274
        }
 
275
        final = wxString(_("All Supported Formats")) + _T(" (") + temp1.Left(temp1.Length()-1) + _T(")|") + temp2.Left(temp2.Length()-1) + _T("|") + final.Left(final.Length()-1);
 
276
 
 
277
        // Return final list
 
278
        return final;
 
279
}
 
280
 
 
281
 
 
282
/////////////////////////////////
 
283
// Ask the user to enter the FPS
 
284
SubtitleFormat::FPSRational SubtitleFormat::AskForFPS(bool showSMPTE) {
 
285
        wxArrayString choices;
 
286
        FPSRational fps_rat;
 
287
        fps_rat.smpte_dropframe = false; // ensure it's false by default
 
288
        
 
289
        // Video FPS
 
290
        bool vidLoaded = VFR_Output.IsLoaded();
 
291
        if (vidLoaded) {
 
292
                wxString vidFPS;
 
293
                if (VFR_Output.GetFrameRateType() == VFR) vidFPS = _T("VFR");
 
294
                else vidFPS = wxString::Format(_T("%.3f"),VFR_Output.GetAverage());
 
295
                choices.Add(wxString::Format(_T("From video (%s)"),vidFPS.c_str()));
 
296
        }
 
297
        
 
298
        // Standard FPS values
 
299
        choices.Add(_("15.000 FPS"));
 
300
        choices.Add(_("23.976 FPS (Decimated NTSC)"));
 
301
        choices.Add(_("24.000 FPS (FILM)"));
 
302
        choices.Add(_("25.000 FPS (PAL)"));
 
303
        choices.Add(_("29.970 FPS (NTSC)"));
 
304
        if (showSMPTE)
 
305
                choices.Add(_("29.970 FPS (NTSC with SMPTE dropframe)"));
 
306
        choices.Add(_("30.000 FPS"));
 
307
        choices.Add(_("50.000 FPS (PAL x2)"));
 
308
        choices.Add(_("59.940 FPS (NTSC x2)"));
 
309
        choices.Add(_("60.000 FPS"));
 
310
        choices.Add(_("119.880 FPS (NTSC x4)"));
 
311
        choices.Add(_("120.000 FPS"));
 
312
 
 
313
        // Ask
 
314
        int choice = wxGetSingleChoiceIndex(_("Please choose the appropriate FPS for the subtitles:"),_("FPS"),choices);
 
315
        if (choice == -1) {
 
316
                fps_rat.num = 0;
 
317
                fps_rat.den = 0;
 
318
 
 
319
                return fps_rat;
 
320
        }
 
321
 
 
322
        // Get FPS from choice
 
323
        if (vidLoaded) choice--;
 
324
        // dropframe was displayed, that means all choices >4 are bumped up by 1
 
325
        if (showSMPTE) {
 
326
                switch (choice) {
 
327
                        case -1: fps_rat.num = -1;              fps_rat.den = 1;        break; // VIDEO
 
328
                        case 0: fps_rat.num = 15;               fps_rat.den = 1;        break;
 
329
                        case 1: fps_rat.num = 24000;    fps_rat.den = 1001;     break;
 
330
                        case 2: fps_rat.num = 24;               fps_rat.den = 1;        break;
 
331
                        case 3: fps_rat.num = 25;               fps_rat.den = 1;        break;
 
332
                        case 4: fps_rat.num = 30000;    fps_rat.den = 1001;     break;
 
333
                        case 5: fps_rat.num = 30000;    fps_rat.den = 1001; fps_rat.smpte_dropframe = true;     break;
 
334
                        case 6: fps_rat.num = 30;               fps_rat.den = 1;        break;
 
335
                        case 7: fps_rat.num = 50;               fps_rat.den = 1;        break;
 
336
                        case 8: fps_rat.num = 60000;    fps_rat.den = 1001;     break;
 
337
                        case 9: fps_rat.num = 60;               fps_rat.den = 1;        break;
 
338
                        case 10: fps_rat.num = 120000;  fps_rat.den = 1001;     break;
 
339
                        case 11: fps_rat.num = 120;             fps_rat.den = 1;        break;
 
340
                }
 
341
                return fps_rat;
 
342
        } else {
 
343
                // dropframe wasn't displayed
 
344
                switch (choice) {
 
345
                        case -1: fps_rat.num = -1;              fps_rat.den = 1;        break; // VIDEO
 
346
                        case 0: fps_rat.num = 15;               fps_rat.den = 1;        break;
 
347
                        case 1: fps_rat.num = 24000;    fps_rat.den = 1001;     break;
 
348
                        case 2: fps_rat.num = 24;               fps_rat.den = 1;        break;
 
349
                        case 3: fps_rat.num = 25;               fps_rat.den = 1;        break;
 
350
                        case 4: fps_rat.num = 30000;    fps_rat.den = 1001;     break;
 
351
                        case 5: fps_rat.num = 30;               fps_rat.den = 1;        break;
 
352
                        case 6: fps_rat.num = 50;               fps_rat.den = 1;        break;
 
353
                        case 7: fps_rat.num = 60000;    fps_rat.den = 1001;     break;
 
354
                        case 8: fps_rat.num = 60;               fps_rat.den = 1;        break;
 
355
                        case 9: fps_rat.num = 120000;   fps_rat.den = 1001;     break;
 
356
                        case 10: fps_rat.num = 120;             fps_rat.den = 1;        break;
 
357
                }
 
358
                return fps_rat;
 
359
        }
 
360
 
 
361
        // fubar
 
362
        fps_rat.num = 0;
 
363
        fps_rat.den = 0;
 
364
 
 
365
        return fps_rat;
 
366
}
 
367
 
 
368
 
 
369
//////////////
 
370
// Sort lines
 
371
void SubtitleFormat::SortLines() {
 
372
        Line->sort(LessByPointedToValue<AssEntry>());
 
373
}
 
374
 
 
375
 
 
376
////////////////
 
377
// Convert tags
 
378
void SubtitleFormat::ConvertTags(int format,const wxString &lineEnd,bool mergeLineBreaks) {
 
379
        using std::list;
 
380
        list<AssEntry*>::iterator next;
 
381
        for (list<AssEntry*>::iterator cur=Line->begin();cur!=Line->end();cur++) {
 
382
                AssDialogue *current = AssEntry::GetAsDialogue(*cur);
 
383
                if (current) {
 
384
                        // Strip tags
 
385
                        if (format == 1) current->StripTags();
 
386
                        else if (format == 2) current->ConvertTagsToSRT();
 
387
 
 
388
                        // Replace line breaks
 
389
                        current->Text.Replace(_T("\\h"),_T(" "),true);
 
390
                        current->Text.Replace(_T("\\n"),lineEnd,true);
 
391
                        current->Text.Replace(_T("\\N"),lineEnd,true);
 
392
                        if (mergeLineBreaks) {
 
393
                                while (current->Text.Replace(lineEnd+lineEnd,lineEnd,true)) {};
 
394
                        }
 
395
                }
 
396
        }
 
397
}
 
398
 
 
399
 
 
400
////////////////////////////
 
401
// Remove all comment lines
 
402
void SubtitleFormat::StripComments() {
 
403
        using std::list;
 
404
        list<AssEntry*>::iterator next;
 
405
        
 
406
        for (list<AssEntry*>::iterator cur = Line->begin(); cur != Line->end(); cur = next) {
 
407
                next = cur;
 
408
                next++;
 
409
                
 
410
                AssDialogue *dlg = AssEntry::GetAsDialogue(*cur);
 
411
                if (dlg && (dlg->Comment || dlg->Text.IsEmpty())) {
 
412
                        delete *cur;
 
413
                        Line->erase(cur);
 
414
                }
 
415
        }
 
416
}
 
417
 
 
418
 
 
419
/////////////////////////////////
 
420
// Remove all non-dialogue lines
 
421
void SubtitleFormat::StripNonDialogue() {
 
422
        using std::list;
 
423
        list<AssEntry*>::iterator next;
 
424
        
 
425
        for (list<AssEntry*>::iterator cur = Line->begin(); cur != Line->end(); cur = next) {
 
426
                next = cur;
 
427
                next++;
 
428
                
 
429
                if (!AssEntry::GetAsDialogue(*cur)) {
 
430
                        delete *cur;
 
431
                        Line->erase(cur);
 
432
                }
 
433
        }
 
434
}
 
435
 
 
436
 
 
437
///////////////////////////////////////////
 
438
// Helper function for RecombineOverlaps()
 
439
static void InsertLineSortedIntoList(std::list<AssEntry*> &list, std::list<AssEntry*>::iterator next, AssDialogue *newdlg) {
 
440
        std::list<AssEntry*>::iterator insertpos = next;
 
441
        bool inserted = false;
 
442
        while (insertpos != list.end()) {
 
443
                AssDialogue *candidate = AssEntry::GetAsDialogue(*insertpos);
 
444
                if (candidate && candidate->Start >= newdlg->Start) {
 
445
                        list.insert(insertpos, newdlg);
 
446
                        inserted = true;
 
447
                        break;
 
448
                }
 
449
                insertpos++;
 
450
        }
 
451
        if (!inserted) {
 
452
                list.push_back(newdlg);
 
453
        }
 
454
}
 
455
 
 
456
///////////////////////////////////////////////////////////
 
457
// Split and merge lines so there are no overlapping lines
 
458
// http://devel.aegisub.org/wiki/Technical/SplitMerge
 
459
void SubtitleFormat::RecombineOverlaps() {
 
460
        using std::list;
 
461
        list<AssEntry*>::iterator next;
 
462
        
 
463
        for (list<AssEntry*>::iterator cur = Line->begin(); cur != Line->end(); cur = next) {
 
464
                next = cur;
 
465
                next++;
 
466
                
 
467
                if (next == Line->end()) break;
 
468
                
 
469
                AssDialogue *prevdlg = AssEntry::GetAsDialogue(*cur);
 
470
                AssDialogue *curdlg = AssEntry::GetAsDialogue(*next);
 
471
                
 
472
                if (curdlg && prevdlg && prevdlg->End > curdlg->Start) {
 
473
                        // Use names like in the algorithm description and prepare for erasing
 
474
                        // old dialogues from the list
 
475
                        list<AssEntry*>::iterator prev = cur;
 
476
                        cur = next;
 
477
                        next++;
 
478
                        
 
479
                        // std::list::insert() inserts items before the given iterator, so
 
480
                        // we need 'next' for inserting. 'prev' and 'cur' can safely be erased
 
481
                        // from the list now.
 
482
                        Line->erase(prev);
 
483
                        Line->erase(cur);
 
484
                        
 
485
                        //Is there an A part before the overlap?
 
486
                        if (curdlg->Start > prevdlg->Start) {
 
487
                                // Produce new entry with correct values
 
488
                                AssDialogue *newdlg = AssEntry::GetAsDialogue(prevdlg->Clone());
 
489
                                newdlg->Start = prevdlg->Start;
 
490
                                newdlg->End = curdlg->Start;
 
491
                                newdlg->Text = prevdlg->Text;
 
492
                                
 
493
                                InsertLineSortedIntoList(*Line, next, newdlg);
 
494
                        }
 
495
                        
 
496
                        // Overlapping A+B part
 
497
                        {
 
498
                                AssDialogue *newdlg = AssEntry::GetAsDialogue(prevdlg->Clone());
 
499
                                newdlg->Start = curdlg->Start;
 
500
                                newdlg->End = (prevdlg->End < curdlg->End) ? prevdlg->End : curdlg->End;
 
501
                                // Put an ASS format hard linewrap between lines
 
502
                                newdlg->Text = curdlg->Text + _T("\\N") + prevdlg->Text;
 
503
                                
 
504
                                InsertLineSortedIntoList(*Line, next, newdlg);
 
505
                        }
 
506
                        
 
507
                        // Is there an A part after the overlap?
 
508
                        if (prevdlg->End > curdlg->End) {
 
509
                                // Produce new entry with correct values
 
510
                                AssDialogue *newdlg = AssEntry::GetAsDialogue(prevdlg->Clone());
 
511
                                newdlg->Start = curdlg->End;
 
512
                                newdlg->End = prevdlg->End;
 
513
                                newdlg->Text = prevdlg->Text;
 
514
                                
 
515
                                InsertLineSortedIntoList(*Line, next, newdlg);
 
516
                        }
 
517
                        
 
518
                        // Is there a B part after the overlap?
 
519
                        if (curdlg->End > prevdlg->End) {
 
520
                                // Produce new entry with correct values
 
521
                                AssDialogue *newdlg = AssEntry::GetAsDialogue(prevdlg->Clone());
 
522
                                newdlg->Start = prevdlg->End;
 
523
                                newdlg->End = curdlg->End;
 
524
                                newdlg->Text = curdlg->Text;
 
525
                                
 
526
                                InsertLineSortedIntoList(*Line, next, newdlg);
 
527
                        }
 
528
                        
 
529
                        next--;
 
530
                }
 
531
        }
 
532
}
 
533
 
 
534
 
 
535
////////////////////////////////////////////////
 
536
// Merge identical lines that follow each other
 
537
void SubtitleFormat::MergeIdentical() {
 
538
        using std::list;
 
539
        list<AssEntry*>::iterator next;
 
540
        
 
541
        for (list<AssEntry*>::iterator cur = Line->begin(); cur != Line->end(); cur = next) {
 
542
                next = cur;
 
543
                next++;
 
544
                
 
545
                if (next == Line->end()) break;
 
546
                
 
547
                AssDialogue *curdlg = AssEntry::GetAsDialogue(*cur);
 
548
                AssDialogue *nextdlg = AssEntry::GetAsDialogue(*next);
 
549
                
 
550
                if (curdlg && nextdlg && curdlg->End == nextdlg->Start && curdlg->Text == nextdlg->Text) {
 
551
                        // Merge timing
 
552
                        nextdlg->Start = (nextdlg->Start < curdlg->Start ? nextdlg->Start : curdlg->Start);
 
553
                        nextdlg->End = (nextdlg->End > curdlg->End ? nextdlg->End : curdlg->End);
 
554
                        
 
555
                        // Remove duplicate line
 
556
                        delete *cur;
 
557
                        Line->erase(cur);
 
558
                }
 
559
        }
 
560
}
 
561