#To edit and compare internal_properties, use WINDEV integrated tools.
#Internal properties refer to the properties of controls in windows, reports, etc.
info :
 name : CSubtitle
 major_version : 30
 minor_version : 0
 type : 4
 description : ""
 subtype : 0
 options : 256
class :
 identifier : 0x181e7a1f02f43ce8
 internal_properties : HwAAAB4AAAD+1kTo6KJy2WQU0Y4fbFD6aQ7NWfD4KKkN7Ml67nIqLXIRxJ1vjfJ1
 code_elements :
  type_code : 10
  p_codes :
   -
     code : |1+
      /* Copyright 2025 Alexandre Leclerc. MPL 2.0. See https://mozilla.org/MPL/2.0/. */
      
      // Allow to read and write SRT files...
      
      CSubtitle est une Classe
      public
      	m_sFilename			est une chaine
      	m_tabContent		est un tableau local de CSubtitleEntry	// CAUTION: "local" and not "dynamic": When the class is copied, each record must be copied and unique (due to some destructive processes).
      	m_nMaxCharPerLine	est un entier	= 60					// When splitting sentences, use this a max number of characters there should be per line
      fin
      
      TextFormat est une énumération
      	tfContinuousText			= 1
      	tfParagraphOnTimestampBreak	= 2
      	tfParagraphOnSentence		= 3
      fin
      
      ETimecodeRangeAction est une énumération
      	traKeepEntry	// Will keep the whole entry
      	traRemoveEntry	// Will remove the whole entry
      	traTrimEntry	// Will trim the entry, if it is at least 1000ms long in duration
      fin
     type : 131072
  procedures :
   -
     name : Constructeur
     procedure_id : 1737960779823791336
     type_code : 27
     code : |1+
      procédure Constructeur()
      
     type : 589824
   -
     name : Destructeur
     procedure_id : 1737960779823856872
     type_code : 28
     code : |1+
      procédure Destructeur()
      
     type : 655360
   -
     name : LoadFromFile
     procedure_id : 1737960779823922408
     type_code : 12
     code : |1+
      // Résumé : <indiquez ici ce que fait la procédure>
      // Paramètres :
      //	sFilename (chaîne ANSI) : <indiquez ici le rôle de sFilename>
      // Valeur de retour :
      // 	booléen : <indiquez ici le rôle de la valeur de retour>
      //
      procédure LoadFromFile(sFilename est chaine)
      
      si pas fFichierExiste(sFilename) ALORS
      	ErreurDéclenche(101,"File does not exist.")
      	renvoyer faux
      FIN
      
      // Validate file format
      sFormat est une chaine = minuscule(fExtraitChemin(sFilename,fExtension))
      si pas sFormat dans (".srt", ".sbv") alors
      	ErreurDéclenche(102,"This is not an supported format (.SRT or .SBV files).")
      	RENVOYER Faux
      FIN
      
      // Open file content
      sContent est une chaine = UTF8VersChaîne(fChargeTexte(sFilename))
      si sContent = "" _ET_ ErreurDétectée ALORS
      	erreur(erreurinfo(errComplet))
      	renvoyer faux
      FIN
      
      m_sFilename = sFilename
      tableausupprimetout(m_tabContent)
      
      // Create entries
      selon sFormat
      	CAS ".srt" : _LoadDecodeSRT(sContent)
      	cas ".sbv" : _LoadDecodeSBV(sContent)
      FIN
      
      renvoyer Vrai
     type : 458752
   -
     name : ExportToSRT
     procedure_id : 1737960779823987944
     type_code : 12
     code : |1+
      // Résumé : <indiquez ici ce que fait la procédure>
      // Paramètres :
      //	sFilename (chaîne ANSI) : <indiquez ici le rôle de sFilename>
      // Valeur de retour :
      // 	booléen : <indiquez ici le rôle de la valeur de retour>
      //
      procédure ExportToSRT(sFilename est chaîne)
      
      si m_tabContent..Occurrence = 0 alors
      	ErreurDéclenche(103,"There is no content to save.")
      	renvoyer faux
      FIN
      
      // Build content to save
      sContent est une chaine
      pour tout e de m_tabContent
      	sContent += [rc+rc] + e.m_nID + rc + e.TimecodeToString(CSubtitleEntry.tfSRT) + rc + e.m_sText
      FIN
      
      renvoyer fSauveTexte(sFilename,ChaîneVersUTF8(sContent))
     type : 458752
   -
     name : ContentToNumberedText
     procedure_id : 1737960779824053480
     type_code : 12
     code : |1+
      // Résumé : Will return all the content in the form of a text with numbers matching the IDs of the timestamps.
      // Paramètres :
      //	eParagraphFormat (CSRTFile.TextFormat) : Optional paragraph formatting.
      // Valeur de retour :
      // 	chaîne ANSI : <indiquez ici le rôle de la valeur de retour>
      //
      procédure ContentToNumberedText(eParagraphFormat est un TextFormat = tfContinuousText) : chaine
      
      sContent est une chaîne
      
      SI eParagraphFormat = tfParagraphOnTimestampBreak ALORS
      	POUR TOUT e,i DE m_tabContent
      		// Is this clip consecutive with the next one? If no, we insert a paragraph break
      		sContent += [" "] + "{" + e.m_nID + "}" + [" "] + e.TextAsSingleLine()
      		SI i+1 <= m_tabContent..Occurrence ALORS
      			SI PAS m_tabContent[i].m_duTimecodeEnd = m_tabContent[i+1].m_duTimecodeStart ALORS
      				sContent += RC
      			FIN
      		FIN
      	FIN
      
      	sContent = remplace(sContent,rc+" ",rc)
      SINON
      	POUR TOUT e DE m_tabContent
      		sContent += [" "] + "{" + e.m_nID + "}" + [" "] + e.TextAsSingleLine()
      	FIN
      
      	SI eParagraphFormat = tfParagraphOnSentence ALORS
      		sContent	= Remplace(sContent, ". ", "."+CRLF)
      		sContent	= Remplace(sContent, "! ", "!"+CRLF)
      		sContent	= Remplace(sContent, "? ", "?"+CRLF)
      	FIN
      FIN
      
      // Delete all unbreakable spaces that might be in the file. This messes things up for nothing
      // We also cleanup all double spaces
      sContent	= Remplace(sContent," "," ")	// Delete unbreakable spaces
      sContent	= Remplace(sContent,"  "," ")	// Delete all double spaces
      sContent	= Remplace(sContent,RC+" ",RC)	// Delete all cases where a new line starts with a space
      
      renvoyer sContent
     type : 458752
   -
     name : ContentFromNumberedText
     procedure_id : 1737960779824119016
     type_code : 12
     code : |1+
      // Résumé : Will take a numbered text with nested IDs and put back the text as the next SRT content.
      // Paramètres :
      //	sTextBlock (chaîne ANSI) : Text block to use
      //	bDeleteEntriesNotFound (booléen - valeur par défaut=0) : If an entry is not found in the text block, it will be deleted from the SRT file. Otherwise it will stay there.
      //	bRemoveCommentsAndCRLF (booléen - valeur par défaut=1) : Considers a ligne begining with a "#" as a comment. Removes all CRLF in the text to have it as a valid TextBlock. This feature is useful if the textblock has been reviewed and formated during revision.
      // Valeur de retour :
      // 	Aucune
      //
      procédure ContentFromNumberedText(local sTextBlock est une chaine, bDeleteEntriesNotFound est un booléen = Faux, bRemoveCommentsAndCRLF est un booléen = Vrai)
      
      si bRemoveCommentsAndCRLF ALORS
      	sNewText est une chaine
      	s est une chaine
      
      	// All # are considered as comment, even if they are MD titles. (This is on purpose).
      	// An easy way to go around this is to simply add one space before #, and it will be treated as text part of the subtitles.
      	POUR TOUTE CHAÎNE s DE sTextBlock SÉPARÉE PAR rc
      		si s [= "#" alors
      			continue
      		FIN
      		sNewText += s + rc
      	FIN
      
      	// Remove all markdown from the text
      	sNewText = MarkdownVersTexte(sNewText)
      
      	// We could also remove all HTML code from the text... but have to test that later.
      	sNewText = HTMLVersTexte(sNewText)
      
      	// Remove double spaces that might have been created...
      	sNewText = Remplace(sNewText, "  ", " ")
      
      	// We are done with removing special formating
      	sTextBlock = sNewText
      FIN
      
      // Find each entry in the text block to get it's updated value
      pour tout e de m_tabContent
      	sID est une chaine = "{"+e.m_nID+"}"
      
      	// Find the entry point
      	i est un entier = position(sTextBlock, sID)
      	si i = 0 ALORS
      		si bDeleteEntriesNotFound ALORS
      			TableauSupprime(m_tabContent, ElémentCourant)
      		FIN
      		continue
      	FIN
      
      	// Find the end of the entry point
      	o est un entier = position(sTextBlock, "{", i+1)
      	si o = 0 alors
      		o = taille(sTextBlock)+1
      	FIN
      
      	sSubtitle est une chaine = sansespace(sTextBlock[[i+Taille(sID) À o-1]])
      
      	// Update the content
      	e.p_sText = sSubtitle
      FIN
     type : 458752
   -
     name : ClipMergeOptimization
     procedure_id : 1737960779824250088
     type_code : 12
     code : |1+
      // Résumé : Will try to merge clips in an optimized way, respecting a maximum length duration.
      // Paramètres :
      //	nMaxClipDuration (entier) : Maximum clip duration (in sec) that cannot be exceded.
      //	TranslationSRT (CSRTFile dynamique - valeur par défaut=0) : <indiquez ici le rôle de TranslationSRT>
      // 	bIgnoreSentenceOptimization (booléen - valeur par défaut=0) : <indiquez ici le rôle de bIgnoreSentenceOptimization>
      // Valeur de retour :
      // 	Aucune
      //
      procédure ClipMergeOptimization(nMaxClipDuration est une entier, TranslationSRT est un CSubtitle dynamique = Null, bIgnoreSentenceOptimization est un booléen = Faux)
      
      // - We can try to optimize clip length for Dubbing in ElevenLabs. This will merge clips together to have a better length and a better rendering.
      // - Logic we will try to accomplish:
      //   - Make a clip "stop" if there is a "end" punctuation to the clip (.!?) [option to disable if desired?].
      //   - Make a clip stop before the maximum clip duration is reached (important).
      //   - Do not merge clips that are not immediately consecutive.
      
      // This is not our responsability to check if both SRT files are compatible, but we want to avoid a crash with different index count.
      si TranslationSRT <> Null _ET_ TranslationSRT.m_tabContent..Occurrence <> m_tabContent..Occurrence ALORS
      	retour
      FIN
      
      e		est un CSubtitleEntry dynamique
      bMerged	est un booléen
      
      BOUCLE
      	SI m_tabContent..Occurrence <= 1 ALORS
      		SORTIR
      	FIN
      	i		est un entier	= 1
      	bMerged	= Faux
      
      	BOUCLE
      		// Have we reached the end?
      		si i+1 > m_tabContent..Occurrence ALORS
      			sortir
      		FIN
      
      		// Is this clip consecutive with the next one? If no, we cannot merge.
      		si pas m_tabContent[i].m_duTimecodeEnd = m_tabContent[i+1].m_duTimecodeStart alors
      			i++
      			continuer
      		fin
      
      		// Can both clip be merge length-wise? If it is too long, we cannot merge.
      		d est une durée = m_tabContent[i].TimecodeDuration() + m_tabContent[i+1].TimecodeDuration()
      		si d..EnSecondes > nMaxClipDuration alors
      			i++
      			CONTINUER
      		FIN
      
      		// Avoid a merge? — Is clip punctuation too good for a merge? (Check the next one if it is also good.)
      		si bIgnoreSentenceOptimization = Faux alors
      			// Optimize strings for verification. We eliminate quotations are they are considered “transparent” punctuation. (And non-breaking, narrow non-breaking and thin space.)
      			s1 est une chaine = sansespace( remplace(m_tabContent[i].m_sText,	["""", "«", "»", "“", "”", CaractUnicode(0x00A0), CaractUnicode(0x202F), CaractUnicode(0x2009)],"") )
      			s2 est une chaîne = SansEspace( Remplace(m_tabContent[i+1].m_sText,	["""", "«", "»", "“", "”", CaractUnicode(0x00A0), CaractUnicode(0x202F), CaractUnicode(0x2009)],"") )
      			si droite(s1,1) dans (".","!","?") ALORS
      				// Unless the other sentence ends very well, we do not merge.
      				SI pas droite(s2,1) DANS (".","!","?") ALORS
      					i++
      					CONTINUER
      				FIN
      			sinon
      				// If this is nevertheless a not too bad spot to end, and there is nothing better after, we stop here too
      				SI droite(s1,1) DANS (";",",",":","—","–") ALORS
      					// So if we have a punctuation here, but nothing in the next one, we do not merge. We keep this ending
      					SI PAS droite(s2,1) DANS (".","!","?",";",",",":","—","–") ALORS
      						i++
      						CONTINUER
      					FIN
      				FIN
      			FIN
      		FIN
      
      		// Merge
      		// At this point me made all the tests we desired and we are good to merge
      		m_tabContent[i].MergeWith(m_tabContent[i+1])
      		tableausupprime(m_tabContent,i+1)
      
      		// If we have a translation SRT, we have to sync all the merges there too. We hope it works for it too.
      		si TranslationSRT <> Null alors
      			TranslationSRT.m_tabContent[i].MergeWith(TranslationSRT.m_tabContent[i+1])
      			TableauSupprime(TranslationSRT.m_tabContent,i+1)
      		FIN
      
      		// We do not go to the next element as this element is still active for another potential merge
      		bMerged = Vrai
      
      	FIN
      
      À FAIRE TANTQUE bMerged
      
      // We change the numbering of the IDs for the SRT file (in case the user wants to save this as an SRT file)
      
      pour tout e,i de m_tabContent
      	e.m_nID = i
      FIN
     type : 458752
   -
     name : ExportToSBV
     procedure_id : 1737960779824315624
     type_code : 12
     code : |1+
      // Résumé : Export to SubViewer format
      // Paramètres :
      //	sFilename (chaîne ANSI) : <indiquez ici le rôle de sFilename>
      // Valeur de retour :
      // 	booléen : <indiquez ici le rôle de la valeur de retour>
      //
      procédure ExportToSBV(sFilename est chaîne)
      
      SI m_tabContent..Occurrence = 0 ALORS
      	ErreurDéclenche(103,"There is no content to save.")
      	RENVOYER Faux
      FIN
      
      // Build content to save
      // https://wiki.videolan.org/SubViewer/
      //
      sContent est une chaîne
      POUR TOUT e DE m_tabContent
      	sContent += [RC+RC] + e.TimecodeToString(CSubtitleEntry.tfSVB) + RC + e.m_sText
      FIN
      
      // To write in version 2 of subviewer, replace CRLF with "[br]".
      
      RENVOYER fSauveTexte(sFilename,ChaîneVersUTF8(sContent))
     type : 458752
   -
     name : _LoadDecodeSRT
     procedure_id : 1737960779824381160
     type_code : 12
     code : |1+
      // Résumé : <indiquez ici ce que fait la procédure>
      // Paramètres :
      //	sContent (chaîne ANSI) : <indiquez ici le rôle de sContent>
      // Valeur de retour :
      // 	Aucune
      //
      procédure protégée _LoadDecodeSRT(sContent est une chaine)
      
      e est un CSubtitleEntry dynamique
      
      // Load each lines on the SRT file and fill the content.
      POUR TOUTE CHAÎNE sLine DE sContent SÉPARÉE PAR RC
      	// New srt entry (his is a integer number)
      	SI Val(sLine) > 0 _ET_ Val(sLine) = sLine ALORS
      		// Add any previous entry
      		SI e <> Null _ET_ e.m_nID > 0 ALORS
      			TableauAjoute(m_tabContent,e)
      		FIN
      
      		// Create new entry
      		e		= allouer un CSubtitleEntry
      		e.m_nID	= sLine
      		CONTINUE
      	FIN
      
      	// Is this the timecode line?
      	SI sLine [=] " --> " ALORS
      		e.TimecodeFromString(CSubtitleEntry.tfSRT,sLine)
      		CONTINUE
      	FIN
      
      	// This is obviously text line
      	SI sLine <> "" ALORS
      		e.m_sText += [RC] + sLine
      	FIN
      FIN
      
      // Just in case the last SRT entry was not written (because the last line has no empty line after)
      SI e <> Null _ET_ e.m_nID > 0 ALORS
      	TableauAjoute(m_tabContent,e)
      	e = Null
      FIN
      
     type : 458752
   -
     name : _LoadDecodeSBV
     procedure_id : 1737960779824446696
     type_code : 12
     code : |1+
      // Résumé : <indiquez ici ce que fait la procédure>
      // Paramètres :
      //	sContent (chaîne ANSI) : <indiquez ici le rôle de sContent>
      // Valeur de retour :
      // 	Aucune
      //
      procédure protégée _LoadDecodeSBV(sContent est une chaine)
      
      e		est un CSubtitleEntry dynamique = allouer un CSubtitleEntry
      
      // SBV format is expected to have:
      // - first line as timecode
      // - other lines as text
      // - empty line as end of timecode indicator
      
      // Load each lines on the SRT file and fill the content.
      bSkip	est un booléen
      POUR TOUTE CHAÎNE sLine DE sContent SÉPARÉE PAR RC
      	// Skipping potential "additionnal information" section and control codes
      	si sLine = "[INFORMATION]" alors
      		bSkip = Vrai
      		continue
      	FIN
      	si bSkip alors
      		si sLine = "[END INFORMATION]" alors
      			bSkip = Faux
      		FIN
      		continue
      	FIN
      	si sLine [= "[" alors
      		continue
      	FIN
      
      	// Empty line (new entry?)
      	SI sLine = "" ALORS
      		si e <> Null _ET_ e.m_duTimecodeEnd..EnMillisecondes <> 0 alors
      			// Add any previous entry
      			e.m_nID = TableauOccurrence(m_tabContent) + 1	// SBV files do not have an ID. We create one because we need it internally for different processses.
      			TableauAjoute(m_tabContent,e)
      			e = Null
      		fin
      
      		// Create new entry
      		si e = Null alors
      			e = allouer un CSubtitleEntry
      		fin
      
      		CONTINUE
      	FIN
      
      	// The first non-empty line is always the timecode
      	si e.m_duTimecodeEnd..EnMillisecondes = 0 alors
      		// Transform the sbv timecode to SRT timecode
      		e.TimecodeFromString(CSubtitleEntry.tfSVB,sLine)
      		continue
      	FIN
      
      	// This is a text line
      	e.m_sText += [RC] + replace(sLine,"[br]",crlf)	// SBV version 2 allows CRLF to be replaced with [br] placeholder. We want the text as SRT.
      FIN
      
      // Just in case the last SRT entry was not written (because the last line has no empty line after)
      SI e <> Null _ET_ e.m_duTimecodeEnd..EnMillisecondes <> 0 ALORS
      	e.m_nID = TableauOccurrence(m_tabContent) + 1	// SBV files do not have an ID. We create one because we need it internally for different processses.
      	TableauAjoute(m_tabContent,e)
      	e = Null
      FIN
     type : 458752
   -
     name : CalculateTimecodeContinuity
     procedure_id : 1737960779824512232
     type_code : 12
     code : |1+
      // Résumé : Calculate timecode continuity.
      // Paramètres :
      //	Aucun
      // Valeur de retour :
      // 	Aucune
      //
      procédure CalculateTimecodeContinuity()
      
      pour i = 2 _à_ m_tabContent..Occurrence
      	m_tabContent[i].m__bTimecodeNotContinuous = m_tabContent[i-1].m_duTimecodeEnd <> m_tabContent[i].m_duTimecodeStart
      FIN
     type : 458752
   -
     name : GetLastTimecodeEnd
     procedure_id : 1737960779824577768
     type_code : 12
     code : |1+
      // Résumé : Get the last timecode ending time.
      // Paramètres :
      //	Aucun
      // Valeur de retour :
      // 	durée : <indiquez ici le rôle de la valeur de retour>
      //
      procédure GetLastTimecodeEnd() : durée
      
      si m_tabContent..Occurrence > 0 alors
      	renvoyer m_tabContent[m_tabContent..Occurrence].m_duTimecodeEnd
      FIN
      
      renvoyer ""
     type : 458752
   -
     name : TimecodesIdenticalWith
     procedure_id : 1737960779824643304
     type_code : 12
     code : |1+
      // Résumé : Check if the timecodes are identical with another file.
      // Paramètres :
      //	c (CSRTFile dynamique) : Other file to check against.
      // Valeur de retour :
      // 	booléen : True if both files have identical timecodes.
      //
      procédure TimecodesIdenticalWith(c est un CSubtitle dynamique) : booléen
      
      si c = Null alors
      	renvoyer vrai
      FIN
      
      SI m_tabContent..Occurrence <> c.m_tabContent..Occurrence ALORS
      	renvoyer Faux
      FIN
      
      e est un CSubtitleEntry dynamique
      POUR TOUT e,i DANS m_tabContent
      	SI e.m_duTimecodeStart <> c.m_tabContent[i].m_duTimecodeStart _OU_ e.m_duTimecodeEnd <> c.m_tabContent[i].m_duTimecodeEnd ALORS
      		renvoyer Faux
      	FIN
      FIN
      
      renvoyer vrai
     type : 458752
   -
     name : ExportToCSV
     procedure_id : 1737960779824708840
     type_code : 12
     code : |1+
      // Résumé : Will export the file to CSV compatible with ElevenLabs Dubbing project.
      // Paramètres :
      //	sFilename (chaîne ANSI) : Filename to write to.
      //	nOptimizeClips (entier - valeur par défaut=-1) : -1 if clips should not be opimized, all other values will trigger ClipMergeOptimization.
      //	cTranslation (CSRTFile dynamique - valeur par défaut=0) : Translation file to add to the export. Both files must have identical timecodes.
      // Valeur de retour :
      // 	booléen : <indiquez ici le rôle de la valeur de retour>
      //
      procédure ExportToCSV(sFilename est une chaîne, nOptimizeClips est un entier = -1, cTranslation est un CSubtitle dynamique = Null) : booléen
      
      si pas TimecodesIdenticalWith(cTranslation) alors
      	ErreurDéclenche(1,"Both subtitle files must have identical timecodes. The selected files do not match.")
      	renvoyer faux
      FIN
      
      si nOptimizeClips > -1 alors
      	ClipMergeOptimization(nOptimizeClips, cTranslation)
      FIN
      
      // Now we create the CSV file
      // speaker,start_time,end_time,transcription,translation
      sCSV est une chaîne = `speaker,start_time,end_time,transcription,translation`
      POUR TOUT e,i DANS m_tabContent
      	s est une chaine = e.m__sSpeaker = "" ? "Speaker 1" sinon e.m__sSpeaker
      	sCSV += [RC] + ChaîneConstruit(`"%1","%2","%3","%4","%5"`, ...
      	s, ...
      	e.TimecodeToString(CSubtitleEntry.tfSRT,CSubtitleEntry.tpStart), ...
      	e.TimecodeToString(CSubtitleEntry.tfSRT,CSubtitleEntry.tpEnd), ...
      	Remplace(e.TextAsSingleLine(),`"`,`""`), ...
      	cTranslation <> Null ? Remplace(cTranslation.m_tabContent[i].TextAsSingleLine(),`"`,`""`) sinon "" )
      FIN
      
      // Save the CSV file
      renvoyer fSauveTexte(sFilename,ChaîneVersUTF8(sCSV))
     type : 458752
   -
     name : RemoveTimecodesOutOfRange
     procedure_id : 1737960779824774376
     type_code : 12
     code : |1+
      // Résumé : Will delete all timecode entries that are not withing the desired range. This is a destructive process, so work on a copy.
      // Paramètres :
      //	duStart (durée) : Start point (inclusive). Before this point, everything will be deleted.
      //	duEnd (durée) : End point (inclusive). After this point, everything will be deleted.
      //	duTimecodeOffest (durée - valeur par défaut=0) : If timecodes should be corrected, specify the offset. It will be deducted from the timecodes.
      //	eActionOnStraddlingTimecodes (CSRTFile.ETimecodeRangeAction) : Action to be taken if a timecode is stranddling on the range. Default action is to trim (adjust) the timecode so to fit.
      // Valeur de retour :
      // 	Aucune
      //
      procédure RemoveTimecodesOutOfRange(duStart est une durée, duEnd est une durée, duTimecodeOffest est une durée = 0, eActionOnStraddlingTimecodes est une ETimecodeRangeAction = traTrimEntry)
      
      // Prune out timecodes
      pour i = m_tabContent..Occurrence _A_ 1 pas -1
      	e est un CSubtitleEntry dynamique <- m_tabContent[i]
      
      	// Entry is completely outside of the range
      	si e.m_duTimecodeEnd < duStart _OU_ e.m_duTimecodeStart > duEnd ALORS
      		// Remove
      		TableauSupprime(m_tabContent,i)
      		continue
      	fin
      
      	// Starting point is outside of the starting range
      	si e.m_duTimecodeStart < duStart alors
      		selon eActionOnStraddlingTimecodes
      			CAS traRemoveEntry:
      				TableauSupprime(m_tabContent,i)
      
      			CAS traTrimEntry:
      				e.m_duTimecodeStart = duStart
      				si (e.m_duTimecodeEnd - e.m_duTimecodeStart) < 1000 alors
      					TableauSupprime(m_tabContent,i)
      				FIN
      		FIN
      		continue
      	fin
      
      	// Ending point is outside of the ending range
      	SI e.m_duTimecodeEnd > duEnd ALORS
      		SELON eActionOnStraddlingTimecodes
      			CAS traRemoveEntry:
      				TableauSupprime(m_tabContent,i)
      
      			CAS traTrimEntry:
      				e.m_duTimecodeEnd = duEnd
      				SI e.m_duTimecodeEnd - e.m_duTimecodeStart < 1000 ALORS
      					TableauSupprime(m_tabContent,i)
      				FIN
      		FIN
      		continue
      	FIN
      
      FIN
      
      // Correct timecode
      si duTimecodeOffest..EnMillisecondes > 0 alors
      
      	pour tout e de m_tabContent
      		e.m_duTimecodeStart	= e.m_duTimecodeStart - duTimecodeOffest
      		e.m_duTimecodeEnd	= e.m_duTimecodeEnd - duTimecodeOffest
      	FIN
      
      FIN
     type : 458752
   -
     name : CalculateSpeakerChange
     procedure_id : 1738311005448731556
     type_code : 12
     code : |1+
      // Résumé : Calculate speaker switching.
      // Paramètres :
      // 	sSpeaker1 (chaîne ANSI - valeur par défaut="Speaker 1") : Name of Speaker 1
      // 	sSpeaker2 (chaîne ANSI - valeur par défaut="Speaker 2") : Name of Speaker 2
      // Valeur de retour :
      // 	Aucune
      //
      procédure CalculateSpeakerChange(sSpeaker1 est une chaine = "Speaker 1", sSpeaker2 est une chaine = "Speaker 2")
      
      bSwitch est un booléen
      n est un entier = m_tabContent..Occurrence
      
      POUR i = 1 _À_ n
      	m_tabContent[i].m__sSpeaker = bSwitch = Faux ? sSpeaker1 SINON sSpeaker2
      	si i+1 <= n _ET_ m_tabContent[i].m_duTimecodeEnd <> m_tabContent[i+1].m_duTimecodeStart ALORS
      		bSwitch = pas bSwitch
      	FIN
      FIN
     type : 458752
   -
     name : TimecodesToYAMLSequenceOfCompactMappings
     procedure_id : 1740092690090315318
     type_code : 12
     code : |1+
      // Résumé : Exports the timecode information to YAML document
      // Paramètres :
      //	Aucun
      // Valeur de retour :
      // 	chaîne ANSI : <indiquez ici le rôle de la valeur de retour>
      //
      //	y (YAML) : Yaml variable to fill with the timecodes (direct filling, considered as an array)
      procédure TimecodesToYAMLSequenceOfCompactMappings() : chaine
      
      LF est un chaine = Caract(10)
      s est une chaine
      
      pour tout e,i de m_tabContent
      	s += [LF] + "- " + "{" + " id: " + e.m_nID + ", i: " + DuréeVersChaîne(e.m_duTimecodeStart,"HH:MM:SS.CCC") + ", o: " + DuréeVersChaîne(e.m_duTimecodeEnd,"HH:MM:SS.CCC") + " }"
      FIN
      
      renvoyer s
     type : 458752
   -
     name : TimecodesFromYAML
     procedure_id : 1740112968090671115
     type_code : 12
     code : |1+
      // Résumé : Import timecodes from YAML document.
      // Paramètres :
      //	y (YAML) : YAML node containing a sequence of mappings (id, i, o)
      // Valeur de retour :
      // 	booléen : <indiquez ici le rôle de la valeur de retour>
      //
      procédure TimecodesFromYAML(y est un yaml)
      
      // We allow overriding existing timecodes, only if the count matches
      bNewEntries est un booléen = Vrai
      
      si m_tabContent..Occurrence > 0 alors
      	si m_tabContent..Occurrence <> y..Occurrence alors
      		ErreurDéclenche(100, "The number of timecodes does not match. Unable to import the new timecodes.")
      		renvoyer faux
      	FIN
      	bNewEntries = Faux
      FIN
      
      pour i = 1 _a_ y..Occurrence
      	si bNewEntries alors
      		ajoute(m_tabContent)
      	FIN
      
      	m_tabContent[i].m_nid				= y[i].id
      	m_tabContent[i].m_duTimecodeStart	= ChaîneVersDurée(y[i].i..Valeur, y[i].i..Type = 25 ? "HHMMSSLLL" SINON "HH:MM:SS.LLL")
      	m_tabContent[i].m_duTimecodeEnd		= ChaîneVersDurée(y[i].o..Valeur, y[i].i..Type = 25 ? "HHMMSSLLL" SINON "HH:MM:SS.LLL")
      FIN
      
      renvoyer vrai
     type : 458752
  procedure_templates : []
  property_templates : []
 code_parameters :
  internal_properties : HwAAAB4AAAB7MB8NZB5rGUbyk77+IjQnJ74vm430Ar3yq0zmP05sGBBw0ur17uG6ZWry
  original_name : Classe1
resources :
 string_res :
  identifier : 0x17e5b16407d0f1fb
  internal_properties : HwAAAB4AAAA809Qj/IAi+r8QXyrnW7sarQeYORCUjKBkmMeTFexSj5AuvTfTUpN0Eg==
custom_note :
 internal_properties : HwAAAB4AAADnl3uxgA6ylw4vtqUKEOJQD3VAAOKeNUmhPNojcRFoDpHEcUyYAw==
