⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 phonemeeditor_closecaption.cpp

📁 hl2 source code. Do not use it illegal.
💻 CPP
📖 第 1 页 / 共 3 页
字号:
#include <stdio.h>
#include <math.h>
#include "hlfaceposer.h"
#include "PhonemeEditor.h"
#include "PhonemeEditorColors.h"
#include "snd_audio_source.h"
#include "snd_wave_source.h"
#include "ifaceposersound.h"
#include "choreowidgetdrawhelper.h"
#include "mxBitmapButton.h"
#include "phonemeproperties.h"
#include "riff.h"
#include "cmdlib.h"
#include "scriplib.h"
#include "StudioModel.h"
#include "expressions.h"
#include "expclass.h"
#include "InputProperties.h"
#include "PhonemeExtractor.h"
#include "PhonemeConverter.h"
#include "choreoevent.h"
#include "choreoscene.h"
#include "ChoreoView.h"
#include "FileSystem.h"
#include "UtlBuffer.h"
#include "AudioWaveOutput.h"
#include "StudioModel.h"
#include "viewerSettings.h"
#include "ControlPanel.h"
#include "faceposer_models.h"
#include "vstdlib/strtools.h"
#include "tabwindow.h"
#include "MatSysWin.h"
#include "iclosecaptionmanager.h"
#include "EditPhrase.h"

#define PLENTY_OF_TIME 99999.9
#define MINIMUM_PHRASE_GAP 0.02f
#define DEFAULT_PHRASE_LENGTH 1.0f

// Close captioning
void PhonemeEditor::CloseCaption_EditInsertFirstPhrase( void )
{
	if ( GetMode() != MODE_CLOSECAPTION )
		return;

	if ( m_Tags.GetCloseCaptionPhraseCount( CC_ENGLISH ) != 0 )
	{
		Con_Printf( "Can't insert first phrase into sentence, already has data\n" );
		return;
	}

	CEditPhraseParams params;
	memset( &params, 0, sizeof( params ) );
	strcpy( params.m_szDialogTitle, "Edit Phrase" );
	strcpy( params.m_szPrompt, "Current Phrase:" );
	ConvertANSIToUnicode( m_Tags.GetText(), params.m_szInputText, sizeof( params.m_szInputText ) );

	params.m_nLeft = -1;
	params.m_nTop = -1;

	params.m_bPositionDialog = false;

	if ( !EditPhrase( &params ) )
	{
		SetFocus( (HWND)getHandle() );
		return;
	}

	if ( wcslen( params.m_szInputText ) <= 0 )
	{
		return;
	}

	float start, end;
	m_Tags.GetEstimatedTimes( start, end );

	SetDirty( true );

	PushUndo();

	CCloseCaptionPhrase *phrase = new CCloseCaptionPhrase( params.m_szInputText );
	phrase->SetSelected( true );
	phrase->SetStartTime( start );
	phrase->SetEndTime( end );

	m_Tags.AddCloseCaptionPhrase( CC_ENGLISH, phrase );

	PushRedo();

	// Add it
	redraw();
}

void PhonemeEditor::CloseCaption_EditPhrase( CCloseCaptionPhrase *phrase )
{
	CEditPhraseParams params;
	memset( &params, 0, sizeof( params ) );
	strcpy( params.m_szDialogTitle, "Edit Phrase" );
	strcpy( params.m_szPrompt, "Current Phrase:" );
	wcscpy( params.m_szInputText, phrase->GetStream() );

	params.m_nLeft = -1;
	params.m_nTop = -1;

	params.m_bPositionDialog = true;

	if ( params.m_bPositionDialog )
	{
		RECT rcPhrase;
		CloseCaption_GetPhraseRect( phrase, rcPhrase );

		// Convert to screen coords
		POINT pt;
		pt.x = rcPhrase.left;
		pt.y = rcPhrase.top;

		ClientToScreen( (HWND)getHandle(), &pt );

		params.m_nLeft	= pt.x;
		params.m_nTop	= pt.y;
	}

	if ( !EditPhrase( &params ) )
	{
		SetFocus( (HWND)getHandle() );
		return;
	}

	SetFocus( (HWND)getHandle() );

	SetDirty( true );

	PushUndo();

	phrase->SetStream( params.m_szInputText );

	PushRedo();

	redraw();
}

void PhonemeEditor::CloseCaption_EditPhrase( void )
{
	if ( GetMode() != MODE_CLOSECAPTION )
		return;

	CCloseCaptionPhrase *pPhrase = CloseCaption_GetClickedPhrase();
	if ( !pPhrase )
		return;

	CloseCaption_EditPhrase( pPhrase );
}

void PhonemeEditor::CloseCaption_EditInsertBefore( void )
{
	if ( GetMode() != MODE_CLOSECAPTION )
		return;

	CCloseCaptionPhrase *phrase = CloseCaption_GetSelectedPhrase();
	if ( !phrase )
		return;

	float gap = CloseCaption_GetTimeGapToNextPhrase( false, phrase );
	if ( gap < MINIMUM_PHRASE_GAP )
	{
		Con_Printf( "Can't insert before, gap of %.2f ms is too small\n", 1000.0f * gap );
		return;
	}

	// Don't have really long phrases
	gap = min( gap, DEFAULT_PHRASE_LENGTH );

	int clicked = CloseCaption_IndexOfPhrase( phrase );
	Assert( clicked >= 0 );

	CEditPhraseParams params;
	memset( &params, 0, sizeof( params ) );
	strcpy( params.m_szDialogTitle, "Insert Phrase" );
	strcpy( params.m_szPrompt, "Phrase:" );
	wcscpy( params.m_szInputText, L"" );

	params.m_nLeft = -1;
	params.m_nTop = -1;

	params.m_bPositionDialog = true;
	if ( params.m_bPositionDialog )
	{
		RECT rcPhrase;
		CloseCaption_GetPhraseRect( phrase, rcPhrase );

		// Convert to screen coords
		POINT pt;
		pt.x = rcPhrase.left;
		pt.y = rcPhrase.top;

		ClientToScreen( (HWND)getHandle(), &pt );

		params.m_nLeft	= pt.x;
		params.m_nTop	= pt.y;
	}

	int iret = EditPhrase( &params );
	SetFocus( (HWND)getHandle() );
	if ( !iret )
	{
		return;
	}

	if ( wcslen( params.m_szInputText ) <= 0 )
	{
		return;
	}

	SetDirty( true );

	PushUndo();

	CCloseCaptionPhrase *newphrase = new CCloseCaptionPhrase( params.m_szInputText );
	newphrase->SetEndTime( phrase->GetStartTime() );
	newphrase->SetStartTime( phrase->GetStartTime() - gap );
	newphrase->SetSelected( true );
	phrase->SetSelected( false );

	m_Tags.m_CloseCaption[ CC_ENGLISH ].InsertBefore( clicked, newphrase );

	PushRedo();

	// Add it
	redraw();
}

void PhonemeEditor::CloseCaption_EditInsertAfter( void )
{
	if ( GetMode() != MODE_CLOSECAPTION )
		return;

	CCloseCaptionPhrase *phrase = CloseCaption_GetSelectedPhrase();
	if ( !phrase )
		return;

	float gap = CloseCaption_GetTimeGapToNextPhrase( true, phrase );
	if ( gap < MINIMUM_PHRASE_GAP )
	{
		Con_Printf( "Can't insert after, gap of %.2f ms is too small\n", 1000.0f * gap );
		return;
	}

	// Don't have really long phrases
	gap = min( gap, DEFAULT_PHRASE_LENGTH );

	int clicked = CloseCaption_IndexOfPhrase( phrase );
	Assert( clicked >= 0 );

	CEditPhraseParams params;
	memset( &params, 0, sizeof( params ) );
	strcpy( params.m_szDialogTitle, "Insert Phrase" );
	strcpy( params.m_szPrompt, "Phrase:" );
	wcscpy( params.m_szInputText, L"" );

	params.m_nLeft = -1;
	params.m_nTop = -1;

	params.m_bPositionDialog = true;
	if ( params.m_bPositionDialog )
	{
		RECT rcPhrase;
		CloseCaption_GetPhraseRect( phrase, rcPhrase );

		// Convert to screen coords
		POINT pt;
		pt.x = rcPhrase.left;
		pt.y = rcPhrase.top;

		ClientToScreen( (HWND)getHandle(), &pt );

		params.m_nLeft	= pt.x;
		params.m_nTop	= pt.y;
	}

	int iret = EditPhrase( &params );
	SetFocus( (HWND)getHandle() );
	if ( !iret )
	{
		return;
	}

	if ( wcslen( params.m_szInputText ) <= 0 )
	{
		return;
	}

	SetDirty( true );

	PushUndo();

	CCloseCaptionPhrase *newphrase = new CCloseCaptionPhrase( params.m_szInputText );
	newphrase->SetEndTime( phrase->GetEndTime() + gap );
	newphrase->SetStartTime( phrase->GetEndTime() );
	newphrase->SetSelected( true );
	phrase->SetSelected( false );

	m_Tags.m_CloseCaption[ CC_ENGLISH ].InsertAfter( clicked, newphrase );

	PushRedo();

	// Add it
	redraw();
}

void PhonemeEditor::CloseCaption_EditDelete( void )
{
	if ( GetMode() != MODE_CLOSECAPTION )
		return;

	CountSelected();

	if ( m_nSelectedPhraseCount < 1 )
		return;

	SetDirty( true );

	PushUndo();

	for ( int i = m_Tags.GetCloseCaptionPhraseCount( CC_ENGLISH )- 1; i >= 0; i-- )
	{
		CCloseCaptionPhrase *phrase = m_Tags.GetCloseCaptionPhrase( CC_ENGLISH, i );
		if ( !phrase || !phrase->GetSelected() )
			continue;

		m_Tags.RemoveCloseCaptionPhrase( CC_ENGLISH, i );
	}

	PushRedo();

	redraw();
}

void PhonemeEditor::CloseCaption_Select( bool forward )
{
	if ( GetMode() != MODE_CLOSECAPTION )
		return;

	CountSelected();

	if ( m_nSelectedPhraseCount != 1 )
		return;

	// Figure out it's phrase and index
	CCloseCaptionPhrase *phrase = CloseCaption_GetSelectedPhrase();
	if ( !phrase )
		return;

	int phraseNum = CloseCaption_IndexOfPhrase( phrase );
	if ( phraseNum == -1 )
		return;

	if ( forward )
	{
		phraseNum++;

		for ( ; phraseNum < m_Tags.GetCloseCaptionPhraseCount( CC_ENGLISH ); phraseNum++ )
		{
			phrase = m_Tags.GetCloseCaptionPhrase( CC_ENGLISH, phraseNum );
			phrase->SetSelected( true );
		}
	}
	else
	{
		phraseNum--;

		for ( ; phraseNum >= 0; phraseNum-- )
		{
			phrase = m_Tags.GetCloseCaptionPhrase( CC_ENGLISH, phraseNum );
			phrase->SetSelected( true );
		}

	}

	redraw();
}

char const *PhonemeEditor::CloseCaption_StreamToShortName( CCloseCaptionPhrase *phrase )
{
	static char shortname[ 32 ];

	char converted[ 1024 ];
	ConvertUnicodeToANSI( phrase->GetStream(), converted, sizeof( converted ) );

	Q_strncpy( shortname, converted, sizeof( shortname ) );

	if ( strlen( shortname ) > 16 )
	{
		shortname[ 16 ] = 0;
		strcat( shortname, "..." );
	}
	return shortname;
}

void PhonemeEditor::CloseCaption_ShowMenu( CCloseCaptionPhrase *phrase, int mx, int my, mxPopupMenu *pop )
{
	CountSelected();

	bool showmenu = false;
	if ( !pop )
	{
		pop = new mxPopupMenu();
		showmenu = true;
	}
	Assert( pop );

	if ( m_nSelectedPhraseCount <= 0 )
	{
		if ( m_Tags.GetCloseCaptionPhraseCount( CC_ENGLISH ) == 0 )
		{
			pop->add( va( "Add phrase" ), IDC_EDIT_CC_ADDFIRSTPHRASE );
			pop->add( "Create default", IDC_EDIT_CC_DEFAULT_PHRASE );
		}
	}
	else
	{
		pop->add( va( "Delete %s", m_nSelectedPhraseCount > 1 ? "phrases" : va( "'%s'", CloseCaption_StreamToShortName( phrase ) ) ), IDC_EDIT_CC_DELETEPHRASE );

		if ( m_nSelectedPhraseCount == 1 )
		{
			int index = CloseCaption_IndexOfPhrase( phrase );
			bool valid = false;
			if ( index != -1 )
			{
				int token = CloseCaption_GetPhraseTokenUnderMouse( phrase, mx );

				CloseCaption_SetClickedPhrase( index, token );
				valid = true;
			}

			if ( valid )
			{
				pop->add( va( "Edit phrase '%s'...", CloseCaption_StreamToShortName( phrase ) ), IDC_EDIT_CC_PHRASE );

				float nextGap = CloseCaption_GetTimeGapToNextPhrase( true, phrase );
				float prevGap = CloseCaption_GetTimeGapToNextPhrase( false, phrase );

				if ( nextGap > MINIMUM_PHRASE_GAP ||
					 prevGap > MINIMUM_PHRASE_GAP )
				{
					pop->addSeparator();
					if ( prevGap > MINIMUM_PHRASE_GAP )
					{
						pop->add( va( "Insert phrase before '%s'...", CloseCaption_StreamToShortName( phrase ) ), IDC_EDIT_CC_INSERTPHRASEBEFORE );
					}
					if ( nextGap > MINIMUM_PHRASE_GAP )
					{
						pop->add( va( "Insert phrase after '%s'...", CloseCaption_StreamToShortName( phrase ) ), IDC_EDIT_CC_INSERTPHRASEAFTER );
					}
				}

				pop->addSeparator();
				pop->add( va( "Select all phrases after '%s'", CloseCaption_StreamToShortName( phrase ) ), IDC_SELECT_CC_PHRASESRIGHT );
				pop->add( va( "Select all phrases before '%s'", CloseCaption_StreamToShortName( phrase ) ), IDC_SELECT_CC_PHRASESLEFT );
				int count = phrase->CountTokens();
				int clicked = CloseCaption_GetClickedPhraseToken();
				if ( clicked >= 0 && clicked < count - 1 )
				{
					pop->add( va( "Split phrase '%s' after '%s'", 
						CloseCaption_StreamToShortName( phrase ),
						phrase->GetToken( CloseCaption_GetClickedPhraseToken() ) ), IDC_EDIT_CC_SPLIT_PHRASE );
				}
			}
		}
	}

	if ( CloseCaption_AreSelectedPhrasesContiguous() && m_nSelectedPhraseCount > 1 )
	{
		pop->addSeparator();
		pop->add( va( "Merge phrases" ), IDC_EDIT_CC_MERGE_PHRASES );
		pop->add( va( "Snap phrases" ), IDC_CC_SNAPPHRASES );
		if ( m_nSelectedPhraseCount == 2 )
		{
			pop->add( va( "Separate phrases" ), IDC_CC_SEPARATEPHRASES );
		}
	}

	if ( m_nSelectedPhraseCount > 0 )
	{
		pop->addSeparator();

		pop->add( va( "Deselect all" ), IDC_DESELECT_CC_PHRASES );
	}

	if ( m_Tags.GetCloseCaptionPhraseCount( CC_ENGLISH )> 0 )
	{
		pop->addSeparator();
		pop->add( va( "Select all" ), IDC_SELECT_CC_ALLPHRASES );
		pop->add( va( "Cleanup phrases" ), IDC_CC_CLEANUP );
	}

	if ( showmenu )
	{
		pop->popup( this, mx, my );
	}
}

void PhonemeEditor::ShowContextMenu_CloseCaption( int mx, int my )
{
	CountSelected();

	// Construct main
	mxPopupMenu *pop = new mxPopupMenu();

	if ( m_nSelectedPhraseCount > 0 )
	{
		CCloseCaptionPhrase *phrase = CloseCaption_GetSelectedPhrase();
		if ( !phrase )
		{
			// find first one...
			int c = m_Tags.GetCloseCaptionPhraseCount( CC_ENGLISH );
			for ( int i = 0; i < c; i++ )
			{
				phrase = m_Tags.GetCloseCaptionPhrase( CC_ENGLISH, i );
				if ( phrase->GetSelected() )
					break;
			}

			if ( i >= c )
			{
				phrase = NULL;
			}
		}

		if ( phrase )
		{
			CloseCaption_ShowMenu( phrase, mx, my, pop );
		}
	}
	else
	{
		if ( m_Tags.GetCloseCaptionPhraseCount( CC_ENGLISH ) > 0 )
		{
			pop->add( va( "Select all" ), IDC_SELECT_CC_ALLPHRASES );
		}
	}

	if ( m_nUndoLevel != 0 || m_nUndoLevel != m_UndoStack.Size()  )
	{
		pop->addSeparator();

		if ( m_nUndoLevel != 0 )
		{
			pop->add( va( "Undo" ), IDC_UNDO );
		}
		if ( m_nUndoLevel != m_UndoStack.Size() )
		{
			pop->add( va( "Redo" ), IDC_REDO );
		}
		pop->add( va( "Clear Undo Info" ), IDC_CLEARUNDO );
	}
	pop->popup( this, mx, my );
}


bool PhonemeEditor::CloseCaption_IsMouseOverRow( int my )
{
	if ( GetMode() != MODE_CLOSECAPTION )
		return false;

	RECT rc;

	CloseCaption_GetTrayTopBottom( rc );

	if ( my < rc.top )
		return false;
	
	if ( my > rc.bottom )
		return false;
	
	return true;
}

void PhonemeEditor::CloseCaption_FinishMove( int startx, int endx )
{
	float clicktime	= GetTimeForPixel( startx );
	float endtime	= GetTimeForPixel( endx );

	// Find the phonemes who have the closest start/endtime to the starting click time
	CCloseCaptionPhrase *current, *next;

	if ( !CloseCaption_FindSpanningPhrases( clicktime, &current, &next ) )
	{
		return;
	}

	SetDirty( true );

	PushUndo();

	if ( current && !next )
	{
		// cap movement
		current->SetEndTime( current->GetEndTime() + ( endtime - clicktime ) );
	}
	else if ( !current && next )
	{
		// cap movement
		next->SetStartTime( next->GetStartTime() + ( endtime - clicktime ) );
	}
	else
	{
		// cap movement
		endtime = min( endtime, next->GetEndTime() - 1.0f / GetPixelsPerSecond() );
		endtime = max( endtime, current->GetStartTime() + 1.0f / GetPixelsPerSecond() );

		current->SetEndTime( endtime );
		next->SetStartTime( endtime );
	}

	CloseCaption_CleanupPhrases( false );

	PushRedo();

	redraw();
}

void PhonemeEditor::CloseCaption_FinishDrag( int startx, int endx )
{
	float clicktime	= GetTimeForPixel( startx );
	float endtime	= GetTimeForPixel( endx );

	float dt = endtime - clicktime;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -