diff --git a/src/Edit.c b/src/Edit.c index 626986220..4dd0cb8ea 100644 --- a/src/Edit.c +++ b/src/Edit.c @@ -189,7 +189,6 @@ HWND EditCreate(HWND hwndParent) SendMessage(hwnd, SCI_INDICSETALPHA, INDIC_NP3_BAD_BRACE, 120); SendMessage(hwnd, SCI_INDICSETOUTLINEALPHA, INDIC_NP3_BAD_BRACE, 120); - // word delimiter handling EditInitWordDelimiter(hwnd); EditSetAccelWordNav(hwnd,bAccelWordNavigation); @@ -5793,6 +5792,57 @@ void EditCompleteWord(HWND hwnd, BOOL autoInsert) { } + +//============================================================================= +// +// EditUpdateUrlHotspots() +// Find and mark all URL hot-spots +// +void EditUpdateUrlHotspots(HWND hwnd, tPos startPos, tPos endPos) +{ + const char* pszUrlRegEx = "\\b(?:(?:https?|ftp|file)://|www\\.|ftp\\.)" + "(?:\\([-A-Z0-9+&@#/%=~_|$?!:,.]*\\)|[-A-Z0-9+&@#/%=~_|$?!:,.])*" + "(?:\\([-A-Z0-9+&@#/%=~_|$?!:,.]*\\)|[A-Z0-9+&@#/%=~_|$])"; + + const int iRegExLen = (int)strlen(pszUrlRegEx); + + tPos posCurr = (tPos)SendMessage(hwnd, SCI_GETCURRENTPOS, 0, 0); + tPos posTextLength = (tPos)SendMessage(hwnd, SCI_GETTEXTLENGTH, 0, 0); + + if ((startPos < 0) || (startPos >= posTextLength)) + { + tPos cln = (tPos)SendMessage(hwnd, SCI_LINEFROMPOSITION, posCurr, 0); + startPos = (tPos)SendMessage(hwnd, SCI_POSITIONFROMLINE, cln-1, 0); + } + if ((endPos < 0) || (endPos >= posTextLength)) { + tPos cln = (tPos)SendMessage(hwnd, SCI_LINEFROMPOSITION, posCurr, 0); + endPos = (tPos)SendMessage(hwnd, SCI_GETLINEENDPOSITION, cln+1, 0); + } + + int start = (int)startPos; + int end = (int)endPos; + int flags = SCFIND_NP3_REGEX; + + while (TRUE) + { + int iPos = EditFindInTarget(hwnd, pszUrlRegEx, iRegExLen, flags, &start, &end, (end == start)); + + if (iPos < 0) + break; // not found + + // mark this match + SendMessage(hwnd, SCI_STARTSTYLING, iPos, 0); + SendMessage(hwnd, SCI_SETSTYLING, (end - start), STYLE_NP3_ID_HOTSPOT); + + start = end; + end = (int)endPos; + + if (start >= end) + break; + } +} + + //============================================================================= // // EditHighlightIfBrace() diff --git a/src/Edit.h b/src/Edit.h index c355a7902..e24791d15 100644 --- a/src/Edit.h +++ b/src/Edit.h @@ -20,6 +20,11 @@ BOOL Scintilla_RegisterClasses(void*); BOOL Scintilla_ReleaseResources(); + +//typedef Sci_Position tPos; +typedef ptrdiff_t tPos; + + #define FNDRPL_BUFFER 512 typedef struct _editfindreplace { @@ -51,6 +56,9 @@ typedef struct _editfindreplace #define INDIC_NP3_MATCH_BRACE 2 #define INDIC_NP3_BAD_BRACE 3 +#define STYLE_NP3_ID_HOTSPOT 222 + + HWND EditCreate(HWND); void EditInitWordDelimiter(HWND); void EditSetNewText(HWND,char*,DWORD); @@ -119,6 +127,7 @@ void EditPrintInit(); void EditMatchBrace(HWND); void EditClearAllMarks(HWND); void EditMarkAll(HWND,char*,int,BOOL,BOOL); +void EditUpdateUrlHotspots(HWND, tPos, tPos); void EditSetAccelWordNav(HWND,BOOL); void EditCompleteWord(HWND,BOOL); void EditGetBookmarkList(HWND,LPWSTR,int); diff --git a/src/Helpers.h b/src/Helpers.h index 651a5ad8c..421516fdb 100644 --- a/src/Helpers.h +++ b/src/Helpers.h @@ -439,7 +439,6 @@ inline HRESULT PathCchCanonicalize(PWSTR p,size_t l,PCWSTR a) { UNUSED(l); re inline HRESULT PathCchRenameExtension(PWSTR p,size_t l,PCWSTR a) { UNUSED(l); return (PathRenameExtension(p,a) ? S_OK : E_FAIL); } inline HRESULT PathCchRemoveFileSpec(PWSTR p,size_t l) { UNUSED(l); return (PathRemoveFileSpec(p) ? S_OK : E_FAIL); } - // special Drag and Drop Handling typedef struct tDROPDATA diff --git a/src/Notepad3.c b/src/Notepad3.c index 9a43d88c7..276960862 100644 --- a/src/Notepad3.c +++ b/src/Notepad3.c @@ -5340,6 +5340,74 @@ LRESULT MsgCommand(HWND hwnd,WPARAM wParam,LPARAM lParam) } +//============================================================================= +// +// OpenHotSpotURL() - Handles WM_NOTIFY +// +// +void OpenHotSpotURL(tPos position) +{ + int iStyle = (int)SendMessage(hwndEdit, SCI_GETSTYLEAT, position, 0); + int iNewStyle = iStyle; + + // get left most position of style + tPos pos = position; + while ((iNewStyle == iStyle) && (--pos > 0)) { + iNewStyle = (int)SendMessage(hwndEdit, SCI_GETSTYLEAT, pos, 0); + } + tPos firstPos = (pos != 0) ? (pos + 1) : 0; + + // get right most position of style + pos = position; + iNewStyle = iStyle; + tPos posTextLength = (tPos)SendMessage(hwndEdit, SCI_GETTEXTLENGTH, 0, 0); + while ((iNewStyle == iStyle) && (++pos < posTextLength)) { + iNewStyle = (int)SendMessage(hwndEdit, SCI_GETSTYLEAT, pos, 0); + } + tPos lastPos = (pos - 1); + + tPos length = lastPos - firstPos; + + if ((length > 0) && (length < HUGE_BUFFER)) + { + char chURL[HUGE_BUFFER] = { '\0' }; + struct Sci_TextRange tr = { { 0, -1 }, NULL }; + tr.chrg.cpMin = (Sci_PositionCR)firstPos; + tr.chrg.cpMax = (Sci_PositionCR)lastPos; + tr.lpstrText = chURL; + + SendMessage(hwndEdit, SCI_GETTEXTRANGE, 0, (LPARAM)&tr); + + StrTrimA(chURL, " \t\n\r"); + + if (StringCchLenA(chURL, COUNTOF(chURL))) + { + WCHAR wchURL[HUGE_BUFFER] = { L'\0' }; + WCHAR wchDirectory[MAX_PATH] = { L'\0' }; + MultiByteToWideCharStrg(Encoding_SciGetCodePage(hwndEdit), chURL, wchURL); + + if (StringCchLenW(szCurFile, COUNTOF(szCurFile))) { + StringCchCopy(wchDirectory, COUNTOF(wchDirectory), szCurFile); + PathRemoveFileSpec(wchDirectory); + } + + SHELLEXECUTEINFO sei; + ZeroMemory(&sei, sizeof(SHELLEXECUTEINFO)); + sei.cbSize = sizeof(SHELLEXECUTEINFO); + sei.fMask = SEE_MASK_NOZONECHECKS; + sei.hwnd = NULL; + sei.lpVerb = NULL; + sei.lpFile = wchURL; + sei.lpParameters = NULL; + sei.lpDirectory = wchDirectory; + sei.nShow = SW_SHOWNORMAL; + ShellExecuteEx(&sei); + } + } +} + + + //============================================================================= // // MsgNotify() - Handles WM_NOTIFY @@ -5357,6 +5425,14 @@ LRESULT MsgNotify(HWND hwnd,WPARAM wParam,LPARAM lParam) switch(pnmh->code) { + case SCN_HOTSPOTCLICK: + { + if (scn->modifiers & SCMOD_CTRL) { + OpenHotSpotURL(scn->position); + } + } + break; + case SCN_UPDATEUI: if (scn->updated & ~(SC_UPDATE_V_SCROLL | SC_UPDATE_H_SCROLL)) { @@ -5513,6 +5589,7 @@ LRESULT MsgNotify(HWND hwnd,WPARAM wParam,LPARAM lParam) } else if (bAutoCompleteWords && !SendMessage(hwndEdit, SCI_AUTOCACTIVE, 0, 0)) EditCompleteWord(hwndEdit, FALSE); + break; case SCN_MODIFIED: @@ -5525,6 +5602,7 @@ LRESULT MsgNotify(HWND hwnd,WPARAM wParam,LPARAM lParam) } } if (scn->linesAdded != 0) { + EditUpdateUrlHotspots(hwndEdit, -1, -1); UpdateLineNumberWidth(); } bModified = TRUE; @@ -7457,7 +7535,7 @@ BOOL FileLoad(BOOL bDontSave,BOOL bNew,BOOL bReload,BOOL bNoEncDetect,LPCWSTR lp InstallFileWatching(szCurFile); // the .LOG feature ... - if (SendMessage(hwndEdit,SCI_GETLENGTH,0,0) >= 4) { + if (SendMessage(hwndEdit,SCI_GETTEXTLENGTH,0,0) >= 4) { char tchLog[5] = { '\0' }; SendMessage(hwndEdit,SCI_GETTEXT,5,(LPARAM)tchLog); if (StringCchCompareXA(tchLog,".LOG") == 0) { @@ -7487,6 +7565,8 @@ BOOL FileLoad(BOOL bDontSave,BOOL bNew,BOOL bReload,BOOL bNoEncDetect,LPCWSTR lp UpdateStatusbar(); UpdateLineNumberWidth(); + EditUpdateUrlHotspots(hwndEdit, 0, (tPos)SendMessage(hwndEdit, SCI_GETTEXTLENGTH, 0, 0) - 1); + // consistent settings file handling (if loaded in editor) bEnableSaveSettings = (StringCchCompareINW(szCurFile, COUNTOF(szCurFile), szIniFile, COUNTOF(szIniFile)) == 0) ? FALSE : TRUE; UpdateSettingsCmds(); diff --git a/src/Styles.c b/src/Styles.c index 248c059a8..a8b807fe2 100644 --- a/src/Styles.c +++ b/src/Styles.c @@ -3559,6 +3559,17 @@ void Style_SetLexer(HWND hwnd, PEDITLEXER pLexNew) { } } + // Hot Spot settings + SendMessage(hwnd, SCI_STYLESETFORE, STYLE_NP3_ID_HOTSPOT, (LPARAM)RGB(0, 0, 200)); + SendMessage(hwnd, SCI_STYLESETITALIC, STYLE_NP3_ID_HOTSPOT, (LPARAM)TRUE); + //SendMessage(hwnd, SCI_STYLESETUNDERLINE, iHotSpotStyle, (LPARAM)TRUE); + + SendMessage(hwnd, SCI_STYLESETHOTSPOT, STYLE_NP3_ID_HOTSPOT, (LPARAM)TRUE); + SendMessage(hwnd, SCI_SETHOTSPOTACTIVEFORE, TRUE, (LPARAM)RGB(0, 0, 255)); + SendMessage(hwnd, SCI_SETHOTSPOTACTIVEUNDERLINE, TRUE, 0); + SendMessage(hwnd, SCI_SETHOTSPOTSINGLELINE, TRUE, 0); + + SendMessage(hwnd,SCI_COLOURISE,0,(LPARAM)-1); // Save current lexer