void NFOView::OnLeftButtonDown(void) { POINT point = {}; GetCursorPos(&point); ScreenToClient(_handle, &point); POINTS points = { point.x, point.y }; if (IsHyperlink(points)) { std::wstring hyperlink = GetHyperlinkAtPoint(points); ShellExecuteW(_handle, L"open", hyperlink.c_str(), NULL, NULL, SW_SHOWNORMAL); } }
void NFOView::OnMouseMove(POINTS& point) { DWORD selStart; DWORD selEnd; SendMessage(_handle, EM_GETSEL, (WPARAM)&selStart, (LPARAM)&selEnd); bool isSelecting = selEnd > selStart; if (IsHyperlink(point) && !isSelecting) { HCURSOR cursor = LoadCursor(NULL, IDC_HAND); SetCursor(cursor); } else { HCURSOR cursor = LoadCursor(NULL, IDC_IBEAM); SetCursor(cursor); } }
int ParseLine( LPCLASSDATA lpcd, int nInBlock, TCHAR *pcText, int nLength, SYNTAX_COLOR *scColors, int *lpnBlocks, int nNumBlocks, int nLine ) { LPPARSER lpp = Parser; LPKEYHASH lpHash; LPBLOCK lpBlock; COLORREF crText = Parser->crColors[ CARR_TEXT ]; int nIndex = 0, nBlock = 0, nSize, i, nMatchX1 = 0, nMatchX2 = 0; BOOL bContinue = FALSE, bStartOfLine = TRUE, bHasInit = FALSE; int nHyperLength; // Modified by Stephan (2005-06-12) // To allow unquoted paths with space like c:\program files /* * Compute match columns. */ if ( lpcd->ptBracket1.y >= 0 ) nMatchX1 = CaretOffsetLine( lpcd, lpcd->ptBracket1.y, lpcd->ptBracket1.x ); if ( lpcd->ptBracket2.y >= 0 ) nMatchX2 = CaretOffsetLine( lpcd, lpcd->ptBracket2.y, lpcd->ptBracket2.x ); /* * Any text to parse? */ if ( nLength == 0 ) /* * Return the block we are in. */ return nInBlock; /* * Start with a normal text * color block... */ if ( scColors ) { /* * Enough text blocks? */ CHECK_BLOCKS; scColors[ nBlock ].nColumn = nLength; scColors[ nBlock ].crColor = crText; scColors[ nBlock ].crBgColor = CLR_DEFAULT; scColors[ nBlock ].pHash = NULL; scColors[ nBlock ].wFlags = 0; } /* * Parser active? */ if ( lpp == NULL ) { /* * A single text color block. */ *lpnBlocks = 1; return -1; } /* * Are we in a block? */ if ( nInBlock != -1 ) { /* * Get the block. */ lpBlock = ArrayGetAt( lpp->lpaBlocks, nInBlock ); /* * Setup the color. */ if ( scColors ) { /* * Enough text blocks? */ CHECK_BLOCKS; scColors[ nBlock ].nColumn = nLength; scColors[ nBlock ].crColor = lpBlock->crColor; scColors[ nBlock ].crBgColor = lpBlock->crBgColor; scColors[ nBlock ].pHash = NULL; scColors[ nBlock ].wFlags = 0; } /* * End the block with a string? */ if ( lpBlock->nEnd ) { /* * See if the terminator string occures * on the line. */ LPCTSTR pszStr = ( lpp->bCaseOn ? StrStr : StrStrI )( pcText, lpBlock->pszEnd ); /* * Terminator found? */ if( pszStr == NULL ) { /* * Are we parsing hyperlinks? */ if ( Parser->bParseHyperLinks && scColors && lpnBlocks ) { /* * Find all hyperlinks in the line starting * at offset 'nIndex'. */ *lpnBlocks = FindHyperlinksInBlock( lpcd, pcText, nIndex, nLength, 0, nNumBlocks, lpnBlocks, scColors ) + 1; scColors[ *lpnBlocks - 1 ].nColumn = nLength; } else if ( lpnBlocks ) /* * Uses a single block. */ *lpnBlocks = 1; /* * Remain in the text block. */ return nInBlock; } else if ( Parser->bParseHyperLinks && scColors && lpnBlocks ) /* * Find all hyperlinks from offset 'nIndex' * up to the block end marker. */ nBlock = FindHyperlinksInBlock( lpcd, pcText, nIndex, ( int )( pszStr - pcText ), 0, nNumBlocks, lpnBlocks, scColors ); /* * Skip to the index at which * the terminator was found. */ nIndex = ( int )( pszStr - pcText ); } } else { /* * If we are not yet inside a block we determine * if any of the block initiators occure in this * line and, if so, at which offset. */ for ( i = 0; ; i++ ) { /* * Get the block. */ lpBlock = ArrayGetAt( lpp->lpaBlocks, i ); /* * Done? */ if ( lpBlock == NULL ) break; /* * By default this block initiator is not on * this line. */ lpBlock->bInLine = FALSE; /* * Will the initiator fit at all? */ if ( lpBlock->nStart && lpBlock->nStart <= nLength ) { /* * Look up the block initiator on the line. */ LPCTSTR pszStr = ( lpp->bCaseOn ? StrStr : StrStrI )( pcText, lpBlock->pszStart ); /* * Found? */ if ( pszStr != NULL ) { /* * We have a block initializer. */ bHasInit = TRUE; /* * This block initiator is located * on this line. */ lpBlock->bInLine = TRUE; } } } } /* * First we skip the leading blanks... */ while ( _istspace( pcText[ nIndex ] ) && nIndex <= nLength ) nIndex++; /* * Iterate text. */ for ( /*nIndex = 0*/; nIndex <= nLength; nIndex++ ) { /* * Clear continue flag. */ bContinue = FALSE; /* * In a block? */ if ( nInBlock != -1 ) { /* * Get block. */ lpBlock = ArrayGetAt( lpp->lpaBlocks, nInBlock ); /* * Does the block terminate with a string? */ if ( lpBlock->nEnd ) { /* * Does the terminator occure in the text? */ LPCTSTR pszStr = ( lpp->bCaseOn ? StrStr : StrStrI )( &pcText[ nIndex ], lpBlock->pszEnd ); /* * No. Return the block number. */ if ( pszStr == NULL ) { /* * Are we parsing hyperlinks? */ if ( Parser->bParseHyperLinks && scColors && lpnBlocks ) { /* * Find the hyperlinks starting at offset 'nIndex'. */ *lpnBlocks = FindHyperlinksInBlock( lpcd, pcText, nIndex, nLength, nBlock, nNumBlocks, lpnBlocks, scColors ) + 1; scColors[ *lpnBlocks - 1 ].nColumn = nLength; } else if ( lpnBlocks ) /* * Store the block number. */ *lpnBlocks = nBlock + 1; return nInBlock; } else if ( Parser->bParseHyperLinks && scColors && lpnBlocks ) /* * Find all hyperlinks from offset 'nIndex' * up to the block end marker. */ nBlock = FindHyperlinksInBlock( lpcd, pcText, nIndex, ( int )( pszStr - pcText ), nBlock, nNumBlocks, lpnBlocks, scColors ); /* * Skip through to the index at which the * terminator was found. */ nIndex = ( int )( pszStr - pcText ); } /* * Will the terminator fit? */ if ( nLength - nIndex >= lpBlock->nEnd || lpBlock->pszEnd == END_OF_LINE ) { /* * End a block? */ if ( BlockEnd( lpcd, lpBlock, pcText, nIndex, nLength )) { /* * Color array present? */ if ( scColors ) { /* * Enough room? */ CHECK_BLOCKS; /* * Yes. Terminate current color. */ scColors[ nBlock++ ].nColumn = nIndex + lpBlock->nEnd; /* * Start a new color. */ scColors[ nBlock ].nColumn = nLength; scColors[ nBlock ].crColor = crText; scColors[ nBlock ].crBgColor = CLR_DEFAULT; scColors[ nBlock ].pHash = NULL; scColors[ nBlock ].wFlags = 0; } /* * Skip the block terminator. */ if ( lpBlock->pszEnd != END_OF_LINE ) nIndex += lpBlock->nEnd - 1; else nIndex = nLength; /* * No longer in the block. */ nInBlock = -1; /* * Continue parse... */ continue; } } } /* * Keep looking for the terminator if * we are still inside a block. */ if ( nInBlock != -1 ) { if ( scColors && nIndex >= nLength ) scColors[ nBlock ].nColumn = nIndex; continue; } /* * Do we have an initiator? */ if ( bHasInit ) { /* * Look up the block * initiators. */ for ( i = 0; ; i++ ) { /* * Valid block? */ if (( lpBlock = ArrayGetAt( lpp->lpaBlocks, i )) == NULL ) break; /* * Block initiator in this * line? */ if ( ! lpBlock->bInLine ) continue; /* * Block found? */ if ( ! BlockStart( lpcd, lpBlock, pcText, nIndex, nLength, bStartOfLine )) continue; /* * Colors present? */ if ( scColors ) { /* * Enough room? */ CHECK_BLOCKS; /* * Yes. Terminate current color. */ scColors[ nBlock++ ].nColumn = nIndex; /* * Start a new color. */ scColors[ nBlock ].nColumn = nLength; scColors[ nBlock ].crColor = lpBlock->crColor; scColors[ nBlock ].crBgColor = lpBlock->crBgColor; scColors[ nBlock ].pHash = NULL; scColors[ nBlock ].wFlags = 0; } /* * Mark us as being in the block. */ nInBlock = i; /* * Does this block terminate at eol? */ if ( lpBlock->pszEnd == END_OF_LINE ) { /* * Look at the rest of the line * for hyperlinks. */ if ( Parser->bParseHyperLinks && scColors && lpnBlocks ) nBlock = FindHyperlinksInBlock( lpcd, pcText, nIndex, nLength, nBlock, nNumBlocks, lpnBlocks, scColors ); /* * Skip everything except * the last terminator length * plus the escape character. */ nIndex = max( 0, nLength - 2 ); } else /* * Skip the block initiator. */ nIndex += lpBlock->nStart - 1; /* * Break the loop and * continue parsing. */ bContinue = TRUE; break; } } /* * No longer at the start * of the line... */ bStartOfLine = FALSE; /* * Continue? */ if ( bContinue || scColors == NULL ) continue; /* * Skip spaces. */ if ( _istspace( pcText[ nIndex ] )) { /* * Make a new block. */ if ( scColors[ nBlock ].crBgColor != CLR_DEFAULT || scColors[ nBlock ].crColor != crText || scColors[ nBlock ].wFlags ) { /* * Enough room? */ CHECK_BLOCKS; /* * Terminate current color. */ scColors[ nBlock++ ].nColumn = nIndex; /* * Setup new color. */ scColors[ nBlock ].crColor = crText; scColors[ nBlock ].crBgColor = CLR_DEFAULT; scColors[ nBlock ].nColumn = nLength; scColors[ nBlock ].pHash = NULL; scColors[ nBlock ].wFlags = 0; } continue; } if ( Parser->bParseHyperLinks && scColors ) { nHyperLength = IsHyperlink( pcText, nIndex, nLength ); // Modified by Stephan (2005-06-12) if (nHyperLength) { /* * Enough room? */ CHECK_BLOCKS; /* * Yes. Terminate current color. */ scColors[ nBlock++ ].nColumn = nIndex; /* * Start a new color. */ scColors[ nBlock ].nColumn = nLength; scColors[ nBlock ].crColor = Parser->crColors[ CARR_HYPERLINK ]; scColors[ nBlock ].crBgColor = Parser->crColors[ CARR_BACKGROUND_HYPERLINK ]; scColors[ nBlock ].pHash = NULL; scColors[ nBlock ].wFlags = SCF_HYPERLINK; /* * Is it a quoted hyperlink? */ if ( nIndex && (pcText[ nIndex - 1 ] == _T( '"' ) || pcText[ nIndex - 1 ] == _T( '\'' ))) // Modified by Stephan (2005-05-30) { /* * Look up the next quote. */ while ( nIndex < nLength && pcText[ nIndex ] != _T( '"' ) && pcText[ nIndex ] != _T( '\'' )) // Modified by Stephan (2005-05-30) nIndex++; if ( pcText[ nIndex ] == _T( '"' ) || pcText[ nIndex ] == _T( '\'' )) // Modified by Stephan (2005-05-30) nIndex--; } else { /* * Look up the next white space. */ nIndex += nHyperLength; // Modified by Stephan while ( nIndex < nLength && ! _istspace( pcText[ nIndex ] ) && pcText[ nIndex ] != _T('(') && pcText[ nIndex ] != _T(',') && pcText[ nIndex ] != _T(';') && pcText[ nIndex ] != _T(')') && pcText[ nIndex ] != _T('\'')) // Modified by Stephan (2005-05-28) nIndex++; if ( _istspace( pcText[ nIndex ] ) || pcText[ nIndex ] == _T('(') || pcText[ nIndex ] == _T(',') || pcText[ nIndex ] == _T(';') || pcText[ nIndex ] == _T(')') || pcText[ nIndex ] == _T('\'')) // Modified by Stephan (2005-05-28) nIndex--; } continue; } } /* * Delimiter? */ if ( IsDelimiter( lpcd, pcText[ nIndex ] )) { /* * Any change in color? */ if (( scColors[ nBlock ].crColor != Parser->crColors[ CARR_DELIMITER ] ) || ( scColors[ nBlock ].crBgColor != Parser->crColors[ CARR_BACKGROUND_DELIMITER ] ) || ( scColors[ nBlock ].wFlags )) { /* * Enough room? */ CHECK_BLOCKS; /* * Terminate current color. */ scColors[ nBlock++ ].nColumn = nIndex; /* * Setup delimiter color. */ scColors[ nBlock ].crColor = Parser->crColors[ CARR_DELIMITER ]; scColors[ nBlock ].crBgColor = Parser->crColors[ CARR_BACKGROUND_DELIMITER ]; scColors[ nBlock ].nColumn = nLength; scColors[ nBlock ].pHash = NULL; scColors[ nBlock ].wFlags = 0; } /* * Do we have matched brackets? */ if (( nLine == lpcd->ptBracket1.y && nIndex == nMatchX1 ) || ( nLine == lpcd->ptBracket2.y && nIndex == nMatchX2 )) { /* * Enough room? */ CHECK_BLOCKS; /* * Terminate current color. */ scColors[ nBlock++ ].nColumn = nIndex; /* * Setup match color. */ scColors[ nBlock ].crColor = scColors[ nBlock - 1 ].crColor; scColors[ nBlock ].crBgColor = lpp->crColors[ CARR_BRACKET_MATCH ]; scColors[ nBlock ].nColumn = nLength; scColors[ nBlock ].pHash = NULL; scColors[ nBlock ].wFlags = 0; } /* * Continue parsing. */ continue; } else /* * Is it a number? This check is a bit * to simple but it should not pose * any problems... */ if ( _istdigit( pcText[ nIndex ] )) { /* * Color changes? */ if (( scColors[ nBlock ].crColor != Parser->crColors[ CARR_NUMBER ] ) || ( scColors[ nBlock ].crBgColor != Parser->crColors[ CARR_BACKGROUND_NUMBER ] ) || ( scColors[ nBlock ].wFlags )) { /* * Enough room? */ CHECK_BLOCKS; /* * Terminate the current color. */ scColors[ nBlock++ ].nColumn = nIndex; /* * Setup the number colors. */ scColors[ nBlock ].nColumn = nLength; scColors[ nBlock ].crColor = Parser->crColors[ CARR_NUMBER ]; scColors[ nBlock ].crBgColor = Parser->crColors[ CARR_BACKGROUND_NUMBER ]; scColors[ nBlock ].pHash = NULL; scColors[ nBlock ].wFlags = 0; } } else { /* * Do we have bracket matches? */ if (( nLine == lpcd->ptBracket1.y && nIndex == nMatchX1 ) || ( nLine == lpcd->ptBracket2.y && nIndex == nMatchX2 )) { /* * Enough room? */ CHECK_BLOCKS; /* * Terminate current color. */ scColors[ nBlock++ ].nColumn = nIndex; /* * Setup match color. */ scColors[ nBlock ].crColor = crText; scColors[ nBlock ].crBgColor = lpp->crColors[ CARR_BRACKET_MATCH ]; scColors[ nBlock ].nColumn = nLength; scColors[ nBlock ].pHash = NULL; scColors[ nBlock ].wFlags = 0; /* * Continue parsing. */ continue; } else if ( scColors[ nBlock ].crColor != crText || scColors[ nBlock ].crBgColor != CLR_DEFAULT || scColors[ nBlock ].wFlags ) { /* * Enough room? */ CHECK_BLOCKS; /* * Terminate current color. */ scColors[ nBlock++ ].nColumn = nIndex; /* * Setup text color. */ scColors[ nBlock ].crColor = crText; scColors[ nBlock ].crBgColor = CLR_DEFAULT; scColors[ nBlock ].nColumn = nLength; scColors[ nBlock ].pHash = NULL; scColors[ nBlock ].wFlags = 0; } } /* * Count the characters up until * the next space or delimiter. */ nSize = nIndex; while ( nSize < nLength && ! _istspace( pcText[ nSize ] ) && ! IsDelimiter( lpcd, pcText[ nSize ] )) { /* * Do we have bracket matches? */ if (( nLine == lpcd->ptBracket1.y && nSize == nMatchX1 ) || ( nLine == lpcd->ptBracket2.y && nSize == nMatchX2 )) { /* * Enough room? */ CHECK_BLOCKS; /* * Terminate current color. */ scColors[ nBlock++ ].nColumn = nSize; /* * Setup match colors. */ scColors[ nBlock ].crColor = crText; scColors[ nBlock ].crBgColor = lpp->crColors[ CARR_BRACKET_MATCH ]; scColors[ nBlock ].nColumn = nLength; scColors[ nBlock ].pHash = NULL; scColors[ nBlock ].wFlags = 0; /* * Continue parsing. */ break; } nSize++; } /* * Is the previous character a space/delimiter or are * we at the start of the line? */ if ( nIndex == 0 || _istspace( pcText[ nIndex - 1 ] ) || IsDelimiter( lpcd, pcText[ nIndex - 1 ] )) { /* * Is it a keyword */ if (( lpHash = IsKeyword( lpcd, &pcText[ nIndex ], nSize - nIndex )) != NULL ) { /* * Color changes? * NOTE: Removed to accomodate case-fixing. */ /*if ( scColors[ nBlock ].crColor != lpHash->crColor )*/ { /* * Enough room? */ CHECK_BLOCKS; /* * Terminate the current color. */ scColors[ nBlock++ ].nColumn = nIndex; /* * Setup the keyword color and the hash. We use the hash * when a text line is edited to fix the casing when * case-fixing is turned on. */ scColors[ nBlock ].nColumn = nLength; scColors[ nBlock ].crColor = lpHash->crColor; scColors[ nBlock ].crBgColor = lpHash->crBkColor; scColors[ nBlock ].pHash = lpHash; scColors[ nBlock ].wFlags = 0; } } } /* * Are we at the end? */ if ( nSize >= nLength ) break; /* * Adjust the index. */ nIndex = nSize - 1; } /* * Store the number of syntax * color block that are valid. */ if ( lpnBlocks ) *lpnBlocks = nBlock + 1; return nInBlock; }
/* * Look for a hyperlink starting at position * 'nIndex' up to position 'nLength'. */ static int FindHyperlinksInBlock( LPCLASSDATA lpcd, TCHAR *pcText, int nIndex, int nLength, int nBlock, int nNumBlocks, int *lpnBlocks, SYNTAX_COLOR *scColors ) { int nHyperLength; // Modified by Stephan (2005-06-12) // To allow unquoted paths with space like c:\program files /* * Iterate all characters from 'nIndex' * up to 'nLength'. */ for ( ; nIndex < nLength; nIndex++ ) { /* * Is this a hyperlink? */ nHyperLength = IsHyperlink( pcText, nIndex, nLength ); if ( nHyperLength ) { /* * Enough room in the color * blocks array? */ CHECK_BLOCKS; /* * Yes. Terminate current color. */ scColors[ nBlock++ ].nColumn = nIndex; /* * Start a new color. Actually this does not * start a new color but it sets the SCF_HYPERLINK * flag in a block with the same color. * * The flag tell's the rendering engine to * render the text using the underlined font. */ scColors[ nBlock ].nColumn = nLength; scColors[ nBlock ].crColor = scColors[ nBlock - 1 ].crColor; scColors[ nBlock ].crBgColor = scColors[ nBlock - 1 ].crBgColor; scColors[ nBlock ].pHash = NULL; scColors[ nBlock ].wFlags = SCF_HYPERLINK; /* * Is it a quoted hyperlink? */ if ( nIndex && (pcText[ nIndex - 1 ] == _T( '"' ) || pcText[ nIndex - 1 ] == _T( '\'' ))) // Modified by Stephan (2005-05-30) { /* * Look for the next double quote or * the end of the line. */ while ( nIndex < nLength && pcText[ nIndex ] != _T( '"' ) && pcText[ nIndex ] != _T( '\'' )) // Modified by Stephan (2005-05-30) nIndex++; } else if ( nIndex == 0 || ( _istspace( pcText[ nIndex - 1 ] ))) { /* * Look for the next white space or * the end of the line. */ nIndex += nHyperLength; // Modified by Stephan while ( nIndex < nLength && ! _istspace( pcText[ nIndex ] ) && pcText[ nIndex ] != _T('(') && pcText[ nIndex ] != _T(',') && pcText[ nIndex ] != _T(';') && pcText[ nIndex ] != _T(')') && pcText[ nIndex ] != _T('\'') && pcText[ nIndex ] != _T(':')) // Modified by Stephan (2005-05-28) nIndex++; } /* * Terminate this block. */ CHECK_BLOCKS; /* * Yes. Terminate current color. */ scColors[ nBlock++ ].nColumn = nIndex; /* * Erase the SCF_HYPERLINK flag which tells * the rendering engine to switch back to * the normal font. */ scColors[ nBlock ].nColumn = nLength; scColors[ nBlock ].crColor = scColors[ nBlock - 1 ].crColor; scColors[ nBlock ].crBgColor = scColors[ nBlock - 1 ].crBgColor; scColors[ nBlock ].pHash = NULL; scColors[ nBlock ].wFlags = 0; } } return nBlock; }