richedit-win32.cpp (3076B)
1 /* ReaPack: Package manager for REAPER 2 * Copyright (C) 2015-2025 Christian Fillion 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU Lesser General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #include "richedit.hpp" 19 20 // This is the Win32 implementation of RichEdit 21 // The macOS implementation is in richedit.mm, Linux is in richedit-generic.cpp 22 23 #include "dllimport.hpp" 24 #include "win32.hpp" 25 26 #include <memory> 27 #include <richedit.h> 28 #include <sstream> 29 30 static void HandleLink(ENLINK *info, HWND handle) 31 { 32 const CHARRANGE &range = info->chrg; 33 34 std::wstring url(range.cpMax - range.cpMin, 0); 35 36 TEXTRANGE tr{range, url.data()}; 37 SendMessage(handle, EM_GETTEXTRANGE, 0, (LPARAM)&tr); 38 39 if(info->msg == WM_LBUTTONUP) 40 ShellExecute(nullptr, L"open", url.c_str(), nullptr, nullptr, SW_SHOW); 41 } 42 43 void RichEdit::Init() 44 { 45 LoadLibrary(L"Msftedit.dll"); 46 } 47 48 RichEdit::RichEdit(HWND handle) 49 : Control(handle) 50 { 51 SendMessage(handle, EM_AUTOURLDETECT, true, 0); 52 SendMessage(handle, EM_SETEVENTMASK, 0, ENM_LINK); 53 SendMessage(handle, EM_SETEDITSTYLE, 54 SES_HYPERLINKTOOLTIPS, SES_HYPERLINKTOOLTIPS); 55 56 // available since Windows 10 version 1703 57 static DllImport<decltype(SetDialogControlDpiChangeBehavior)> 58 _SetDialogControlDpiChangeBehavior 59 {L"user32.dll", "SetDialogControlDpiChangeBehavior"}; 60 61 if(_SetDialogControlDpiChangeBehavior) { 62 _SetDialogControlDpiChangeBehavior(handle, 63 DCDC_DISABLE_FONT_UPDATE, DCDC_DISABLE_FONT_UPDATE); 64 } 65 } 66 67 RichEdit::~RichEdit() = default; 68 69 void RichEdit::onNotify(LPNMHDR info, LPARAM lParam) 70 { 71 switch(info->code) { 72 case EN_LINK: 73 HandleLink((ENLINK *)lParam, handle()); 74 break; 75 }; 76 } 77 78 void RichEdit::setPlainText(const std::string &text) 79 { 80 Win32::setWindowText(handle(), text.c_str()); 81 } 82 83 bool RichEdit::setRichText(const std::string &rtf) 84 { 85 std::stringstream stream(rtf); 86 87 EDITSTREAM es{}; 88 es.dwCookie = (DWORD_PTR)&stream; 89 es.pfnCallback = [](DWORD_PTR cookie, LPBYTE buf, LONG size, LONG *pcb) -> DWORD { 90 std::stringstream *stream = reinterpret_cast<std::stringstream *>(cookie); 91 *pcb = (LONG)stream->readsome((char *)buf, size); 92 return 0; 93 }; 94 95 SendMessage(handle(), EM_STREAMIN, SF_RTF, (LPARAM)&es); 96 97 if(es.dwError) 98 return false; 99 100 GETTEXTLENGTHEX tl{}; 101 LONG length = (LONG)SendMessage(handle(), EM_GETTEXTLENGTHEX, (WPARAM)&tl, 0); 102 103 if(!length) 104 return false; 105 106 // scale down a little bit, by default everything is way to big 107 SendMessage(handle(), EM_SETZOOM, 3, 4); 108 109 return true; 110 }