├── .gitignore ├── .gitmodules ├── README.md ├── litehtml-tips.sln ├── src ├── tooltips.cpp └── tooltips.h └── test ├── MainWnd.cpp ├── MainWnd.h ├── TxWnd.cpp ├── TxWnd.h ├── globals.h ├── main.cpp ├── resource.h ├── tooltipstest.rc ├── tooltipstest.vcproj └── utils.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | [Dd]ebug/ 11 | [Dd]ebugPublic/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | bld/ 16 | [Bb]in/ 17 | [Oo]bj/ 18 | [Ww]in32/ 19 | [Ll]ib/ 20 | 21 | # MSTest test Results 22 | [Tt]est[Rr]esult*/ 23 | [Bb]uild[Ll]og.* 24 | 25 | #NUNIT 26 | *.VisualState.xml 27 | TestResult.xml 28 | 29 | # Build Results of an ATL Project 30 | [Dd]ebugPS/ 31 | [Rr]eleasePS/ 32 | dlldata.c 33 | 34 | *_i.c 35 | *_p.c 36 | *_i.h 37 | *.ilk 38 | *.meta 39 | *.obj 40 | *.pch 41 | *.pdb 42 | *.pgc 43 | *.pgd 44 | *.rsp 45 | *.sbr 46 | *.tlb 47 | *.tli 48 | *.tlh 49 | *.tmp 50 | *.tmp_proj 51 | *.log 52 | *.vspscc 53 | *.vssscc 54 | .builds 55 | *.pidb 56 | *.svclog 57 | *.scc 58 | 59 | # Chutzpah Test files 60 | _Chutzpah* 61 | 62 | # Visual C++ cache files 63 | ipch/ 64 | *.aps 65 | *.ncb 66 | *.opensdf 67 | *.sdf 68 | *.cachefile 69 | 70 | # Visual Studio profiler 71 | *.psess 72 | *.vsp 73 | *.vspx 74 | 75 | # TFS 2012 Local Workspace 76 | $tf/ 77 | 78 | # Guidance Automation Toolkit 79 | *.gpState 80 | 81 | # ReSharper is a .NET coding add-in 82 | _ReSharper*/ 83 | *.[Rr]e[Ss]harper 84 | *.DotSettings.user 85 | 86 | # JustCode is a .NET coding addin-in 87 | .JustCode 88 | 89 | # TeamCity is a build add-in 90 | _TeamCity* 91 | 92 | # DotCover is a Code Coverage Tool 93 | *.dotCover 94 | 95 | # NCrunch 96 | *.ncrunch* 97 | _NCrunch_* 98 | .*crunch*.local.xml 99 | 100 | # MightyMoose 101 | *.mm.* 102 | AutoTest.Net/ 103 | 104 | # Web workbench (sass) 105 | .sass-cache/ 106 | 107 | # Installshield output folder 108 | [Ee]xpress/ 109 | 110 | # DocProject is a documentation generator add-in 111 | DocProject/buildhelp/ 112 | DocProject/Help/*.HxT 113 | DocProject/Help/*.HxC 114 | DocProject/Help/*.hhc 115 | DocProject/Help/*.hhk 116 | DocProject/Help/*.hhp 117 | DocProject/Help/Html2 118 | DocProject/Help/html 119 | 120 | # Click-Once directory 121 | publish/ 122 | 123 | # Publish Web Output 124 | *.[Pp]ublish.xml 125 | *.azurePubxml 126 | 127 | # NuGet Packages Directory 128 | packages/ 129 | ## TODO: If the tool you use requires repositories.config uncomment the next line 130 | #!packages/repositories.config 131 | 132 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 133 | # This line needs to be after the ignore of the build folder (and the packages folder if the line above has been uncommented) 134 | !packages/build/ 135 | 136 | # Windows Azure Build Output 137 | csx/ 138 | *.build.csdef 139 | 140 | # Windows Store app package directory 141 | AppPackages/ 142 | 143 | # Others 144 | sql/ 145 | *.Cache 146 | ClientBin/ 147 | [Ss]tyle[Cc]op.* 148 | ~$* 149 | *~ 150 | *.dbmdl 151 | *.dbproj.schemaview 152 | *.pfx 153 | *.publishsettings 154 | node_modules/ 155 | 156 | # RIA/Silverlight projects 157 | Generated_Code/ 158 | 159 | # Backup & report files from converting an old project file to a newer 160 | # Visual Studio version. Backup files are not needed, because we have git ;-) 161 | _UpgradeReport_Files/ 162 | Backup*/ 163 | UpgradeLog*.XML 164 | UpgradeLog*.htm 165 | 166 | # SQL Server files 167 | *.mdf 168 | *.ldf 169 | 170 | # Business Intelligence projects 171 | *.rdl.data 172 | *.bim.layout 173 | *.bim_*.settings 174 | 175 | # Microsoft Fakes 176 | FakesAssemblies/ 177 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "litehtml"] 2 | path = litehtml 3 | url = git@github.com:tordex/litehtml.git 4 | [submodule "libs/cairo"] 5 | path = libs/cairo 6 | url = git@github.com:tordex/cairo.git 7 | [submodule "libs/freeimage"] 8 | path = libs/freeimage 9 | url = git@github.com:tordex/freeimage.git 10 | [submodule "libs/simpledib"] 11 | path = libs/simpledib 12 | url = git@github.com:tordex/simpledib.git 13 | [submodule "libs/txdib"] 14 | path = libs/txdib 15 | url = git@github.com:tordex/txdib.git 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Tooltips with litehtml engine 2 | 3 | This project was created to show how to show HTML powered tooltips using the [litehtml rendering engine](https://github.com/tordex/litehtml). -------------------------------------------------------------------------------- /litehtml-tips.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 10.00 3 | # Visual Studio 2008 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tooltipstest", "test\tooltipstest.vcproj", "{E9B3BCB1-F224-470C-8DC0-FCBD22C8B364}" 5 | ProjectSection(ProjectDependencies) = postProject 6 | {23740D61-AB4D-4F6B-8E5B-64D42A19173C} = {23740D61-AB4D-4F6B-8E5B-64D42A19173C} 7 | {5EEE68E7-3CB0-4ADB-AF72-4C0BB01F0B18} = {5EEE68E7-3CB0-4ADB-AF72-4C0BB01F0B18} 8 | {E94EC1FA-DBB9-4D02-B190-F549BD3E1EC9} = {E94EC1FA-DBB9-4D02-B190-F549BD3E1EC9} 9 | EndProjectSection 10 | EndProject 11 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "litehtml", "litehtml\src\litehtml.vcproj", "{5EEE68E7-3CB0-4ADB-AF72-4C0BB01F0B18}" 12 | EndProject 13 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TxDIB", "libs\txdib\TxDIBlib.vcproj", "{E94EC1FA-DBB9-4D02-B190-F549BD3E1EC9}" 14 | EndProject 15 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simpledib", "libs\simpledib\simpledib.vcproj", "{23740D61-AB4D-4F6B-8E5B-64D42A19173C}" 16 | EndProject 17 | Global 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | Debug|Win32 = Debug|Win32 20 | Debug|x64 = Debug|x64 21 | Release|Win32 = Release|Win32 22 | Release|x64 = Release|x64 23 | EndGlobalSection 24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 25 | {E9B3BCB1-F224-470C-8DC0-FCBD22C8B364}.Debug|Win32.ActiveCfg = Debug|Win32 26 | {E9B3BCB1-F224-470C-8DC0-FCBD22C8B364}.Debug|Win32.Build.0 = Debug|Win32 27 | {E9B3BCB1-F224-470C-8DC0-FCBD22C8B364}.Debug|x64.ActiveCfg = Debug|x64 28 | {E9B3BCB1-F224-470C-8DC0-FCBD22C8B364}.Debug|x64.Build.0 = Debug|x64 29 | {E9B3BCB1-F224-470C-8DC0-FCBD22C8B364}.Release|Win32.ActiveCfg = Release|Win32 30 | {E9B3BCB1-F224-470C-8DC0-FCBD22C8B364}.Release|Win32.Build.0 = Release|Win32 31 | {E9B3BCB1-F224-470C-8DC0-FCBD22C8B364}.Release|x64.ActiveCfg = Release|x64 32 | {E9B3BCB1-F224-470C-8DC0-FCBD22C8B364}.Release|x64.Build.0 = Release|x64 33 | {5EEE68E7-3CB0-4ADB-AF72-4C0BB01F0B18}.Debug|Win32.ActiveCfg = Debug|Win32 34 | {5EEE68E7-3CB0-4ADB-AF72-4C0BB01F0B18}.Debug|Win32.Build.0 = Debug|Win32 35 | {5EEE68E7-3CB0-4ADB-AF72-4C0BB01F0B18}.Debug|x64.ActiveCfg = Debug|x64 36 | {5EEE68E7-3CB0-4ADB-AF72-4C0BB01F0B18}.Debug|x64.Build.0 = Debug|x64 37 | {5EEE68E7-3CB0-4ADB-AF72-4C0BB01F0B18}.Release|Win32.ActiveCfg = Release|Win32 38 | {5EEE68E7-3CB0-4ADB-AF72-4C0BB01F0B18}.Release|Win32.Build.0 = Release|Win32 39 | {5EEE68E7-3CB0-4ADB-AF72-4C0BB01F0B18}.Release|x64.ActiveCfg = Release|x64 40 | {5EEE68E7-3CB0-4ADB-AF72-4C0BB01F0B18}.Release|x64.Build.0 = Release|x64 41 | {E94EC1FA-DBB9-4D02-B190-F549BD3E1EC9}.Debug|Win32.ActiveCfg = Debug|Win32 42 | {E94EC1FA-DBB9-4D02-B190-F549BD3E1EC9}.Debug|Win32.Build.0 = Debug|Win32 43 | {E94EC1FA-DBB9-4D02-B190-F549BD3E1EC9}.Debug|x64.ActiveCfg = Debug|x64 44 | {E94EC1FA-DBB9-4D02-B190-F549BD3E1EC9}.Debug|x64.Build.0 = Debug|x64 45 | {E94EC1FA-DBB9-4D02-B190-F549BD3E1EC9}.Release|Win32.ActiveCfg = Release|Win32 46 | {E94EC1FA-DBB9-4D02-B190-F549BD3E1EC9}.Release|Win32.Build.0 = Release|Win32 47 | {E94EC1FA-DBB9-4D02-B190-F549BD3E1EC9}.Release|x64.ActiveCfg = Release|x64 48 | {E94EC1FA-DBB9-4D02-B190-F549BD3E1EC9}.Release|x64.Build.0 = Release|x64 49 | {23740D61-AB4D-4F6B-8E5B-64D42A19173C}.Debug|Win32.ActiveCfg = Debug|Win32 50 | {23740D61-AB4D-4F6B-8E5B-64D42A19173C}.Debug|Win32.Build.0 = Debug|Win32 51 | {23740D61-AB4D-4F6B-8E5B-64D42A19173C}.Debug|x64.ActiveCfg = Debug|x64 52 | {23740D61-AB4D-4F6B-8E5B-64D42A19173C}.Debug|x64.Build.0 = Debug|x64 53 | {23740D61-AB4D-4F6B-8E5B-64D42A19173C}.Release|Win32.ActiveCfg = Release|Win32 54 | {23740D61-AB4D-4F6B-8E5B-64D42A19173C}.Release|Win32.Build.0 = Release|Win32 55 | {23740D61-AB4D-4F6B-8E5B-64D42A19173C}.Release|x64.ActiveCfg = Release|x64 56 | {23740D61-AB4D-4F6B-8E5B-64D42A19173C}.Release|x64.Build.0 = Release|x64 57 | EndGlobalSection 58 | GlobalSection(SolutionProperties) = preSolution 59 | HideSolutionNode = FALSE 60 | EndGlobalSection 61 | EndGlobal 62 | -------------------------------------------------------------------------------- /src/tooltips.cpp: -------------------------------------------------------------------------------- 1 | #define ISOLATION_AWARE_ENABLED 1 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "tooltips.h" 11 | #define _USE_MATH_DEFINES 12 | #include 13 | #include 14 | #include 15 | 16 | //defines 17 | #define LITEHTML_TOOLTIPS_WINDOW L"LITEHTML_TOOLTIPS_WINDOW" 18 | #define TIMER_HIDE_TIP 2 19 | 20 | 21 | litehtml::tooltips::tooltips(HINSTANCE hInst, litehtml::context* html_context, int radius) 22 | { 23 | registerClass(hInst); 24 | m_hWnd = NULL; 25 | m_html_context = html_context; 26 | m_max_width = 300; 27 | m_show_time = 500; 28 | m_hide_time = 0; 29 | m_callback = NULL; 30 | m_style = tips_style_square; 31 | m_over_tool = 0; 32 | m_disabled = false; 33 | m_alpha = 255; 34 | m_cr = NULL; 35 | m_surface = NULL; 36 | m_last_shown_tool = 0; 37 | m_cached_tool = 0; 38 | m_mouse_hover_on = false; 39 | m_radius = radius; 40 | m_hide_time_int = 2000; 41 | m_hide_timer_active = false; 42 | m_max_height = 0; 43 | 44 | init_def_font(); 45 | } 46 | 47 | litehtml::tooltips::~tooltips(void) 48 | { 49 | DestroyWindow(m_hWnd); 50 | RemoveWindowSubclass(m_hWndParent, SubclassProc, (DWORD_PTR) this); 51 | if(m_surface) cairo_surface_destroy(m_surface); 52 | if(m_cr) cairo_destroy(m_cr); 53 | } 54 | 55 | void litehtml::tooltips::make_url( LPCWSTR url, LPCWSTR basepath, std::wstring& out ) 56 | { 57 | out.clear(); 58 | if(basepath) 59 | { 60 | out = basepath; 61 | } 62 | out += url; 63 | } 64 | 65 | CTxDIB* litehtml::tooltips::get_image( LPCWSTR url, bool redraw_on_ready ) 66 | { 67 | if(m_callback) 68 | { 69 | return m_callback->ttcb_get_image(m_show_tool, url, redraw_on_ready); 70 | } 71 | return NULL; 72 | } 73 | 74 | void litehtml::tooltips::set_caption( const wchar_t* caption ) 75 | { 76 | 77 | } 78 | 79 | void litehtml::tooltips::set_base_url( const wchar_t* base_url ) 80 | { 81 | 82 | } 83 | 84 | void litehtml::tooltips::link( litehtml::document* doc, litehtml::element::ptr el ) 85 | { 86 | 87 | } 88 | 89 | void litehtml::tooltips::import_css( std::wstring& text, const std::wstring& url, std::wstring& baseurl ) 90 | { 91 | 92 | } 93 | 94 | void litehtml::tooltips::on_anchor_click( const wchar_t* url, litehtml::element::ptr el ) 95 | { 96 | if(url) 97 | { 98 | m_anchor = url; 99 | } 100 | } 101 | 102 | void litehtml::tooltips::set_cursor( const wchar_t* cursor ) 103 | { 104 | m_cursor = cursor ? cursor : L""; 105 | } 106 | 107 | LRESULT CALLBACK litehtml::tooltips::WndProc( HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam ) 108 | { 109 | tooltips* pThis = NULL; 110 | if(IsWindow(hWnd)) 111 | { 112 | pThis = (tooltips*)GetProp(hWnd, TEXT("tooltips_this")); 113 | if(pThis && pThis->m_hWnd != hWnd) 114 | { 115 | pThis = NULL; 116 | } 117 | } 118 | if(pThis || uMessage == WM_CREATE) 119 | { 120 | switch (uMessage) 121 | { 122 | case WM_CREATE: 123 | { 124 | LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam; 125 | pThis = (tooltips*)(lpcs->lpCreateParams); 126 | SetProp(hWnd, TEXT("tooltips_this"), (HANDLE) pThis); 127 | pThis->m_hWnd = hWnd; 128 | } 129 | break; 130 | case WM_DESTROY: 131 | { 132 | LRESULT ret = pThis->OnMessage(hWnd, uMessage, wParam, lParam); 133 | RemoveProp(hWnd, TEXT("tooltips_this")); 134 | pThis->m_hWnd = NULL; 135 | return ret; 136 | } 137 | break; 138 | } 139 | return pThis->OnMessage(hWnd, uMessage, wParam, lParam); 140 | } 141 | return DefWindowProc(hWnd, uMessage, wParam, lParam); 142 | } 143 | 144 | void litehtml::tooltips::registerClass( HINSTANCE hInstance ) 145 | { 146 | m_hInst = hInstance; 147 | 148 | WNDCLASSEX wc; 149 | if(!GetClassInfoEx(hInstance, LITEHTML_TOOLTIPS_WINDOW, &wc)) 150 | { 151 | ZeroMemory(&wc, sizeof(wc)); 152 | wc.cbSize = sizeof(wc); 153 | wc.lpfnWndProc = (WNDPROC) tooltips::WndProc; 154 | wc.hInstance = hInstance; 155 | wc.cbClsExtra = 0; 156 | wc.cbWndExtra = 0; 157 | wc.hIcon = NULL; 158 | wc.hCursor = NULL; 159 | wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); 160 | wc.lpszMenuName = NULL; 161 | wc.lpszClassName = LITEHTML_TOOLTIPS_WINDOW; 162 | 163 | RegisterClassEx(&wc); 164 | } 165 | } 166 | 167 | LRESULT litehtml::tooltips::OnMessage( HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam ) 168 | { 169 | switch(uMessage) 170 | { 171 | case WM_MOUSEACTIVATE: 172 | return MA_NOACTIVATE; 173 | case WM_TIMER: 174 | switch(wParam) 175 | { 176 | case TIMER_HIDE_TIP: 177 | stop_timers(); 178 | hide(); 179 | break; 180 | } 181 | break; 182 | case WM_UPDATE_TIP: 183 | update((unsigned int) wParam, lParam ? true : false); 184 | return 0; 185 | case WM_REDRAW_TIP: 186 | if((unsigned int) wParam == m_show_tool && IsWindowVisible(m_hWnd)) 187 | { 188 | draw_window(NULL); 189 | } 190 | return 0; 191 | case WM_MOUSELEAVE: 192 | if(is_tool_interactive(m_show_tool)) 193 | { 194 | TRACKMOUSEEVENT tm = {0}; 195 | tm.cbSize = sizeof(TRACKMOUSEEVENT); 196 | tm.dwFlags = TME_QUERY; 197 | TrackMouseEvent(&tm); 198 | 199 | if(tm.dwFlags & TME_LEAVE) 200 | { 201 | tm.dwFlags = TME_LEAVE | TME_CANCEL; 202 | tm.hwndTrack = m_hWnd; 203 | TrackMouseEvent(&tm); 204 | } 205 | 206 | if(m_html) 207 | { 208 | litehtml::position::vector redraw_boxes; 209 | if(m_html->on_mouse_leave(redraw_boxes)) 210 | { 211 | draw_window(TRUE); 212 | } 213 | } 214 | hide_current_tool(); 215 | } 216 | break; 217 | case WM_SETCURSOR: 218 | update_cursor(); 219 | break; 220 | case WM_MOUSEMOVE: 221 | if(!is_tool_interactive(m_show_tool)) 222 | { 223 | hide(); 224 | } else if(m_html) 225 | { 226 | if(m_hide_timer_active) 227 | { 228 | KillTimer(m_hWnd, TIMER_HIDE_TIP); 229 | m_hide_timer_active = false; 230 | } 231 | 232 | TRACKMOUSEEVENT tm = {0}; 233 | tm.cbSize = sizeof(TRACKMOUSEEVENT); 234 | tm.dwFlags = TME_QUERY; 235 | TrackMouseEvent(&tm); 236 | 237 | if(!(tm.dwFlags & TME_LEAVE)) 238 | { 239 | tm.dwFlags = TME_LEAVE; 240 | tm.hwndTrack = m_hWnd; 241 | TrackMouseEvent(&tm); 242 | } 243 | 244 | if(m_html) 245 | { 246 | litehtml::position::vector redraw_boxes; 247 | if(m_html->on_mouse_over( GET_X_LPARAM(lParam) - m_layout.content_x, 248 | GET_Y_LPARAM(lParam) - m_layout.content_y + m_top, 249 | GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 250 | redraw_boxes) ) 251 | { 252 | draw_window(TRUE); 253 | } 254 | } 255 | update_cursor(); 256 | } 257 | break; 258 | case WM_LBUTTONDOWN: 259 | if(is_tool_interactive(m_show_tool)) 260 | { 261 | if(m_html) 262 | { 263 | litehtml::position::vector redraw_boxes; 264 | if(m_html->on_lbutton_down( GET_X_LPARAM(lParam) - m_layout.content_x, 265 | GET_Y_LPARAM(lParam) - m_layout.content_y + m_top, 266 | GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 267 | redraw_boxes) ) 268 | { 269 | draw_window(TRUE); 270 | } 271 | } 272 | update_cursor(); 273 | } 274 | break; 275 | case WM_LBUTTONUP: 276 | if(is_tool_interactive(m_show_tool)) 277 | { 278 | if(m_html) 279 | { 280 | m_anchor = L""; 281 | litehtml::position::vector redraw_boxes; 282 | if(m_html->on_lbutton_up( GET_X_LPARAM(lParam) - m_layout.content_x, 283 | GET_Y_LPARAM(lParam) - m_layout.content_y + m_top, 284 | GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 285 | redraw_boxes) ) 286 | { 287 | draw_window(TRUE); 288 | } 289 | } 290 | update_cursor(); 291 | 292 | if(!m_anchor.empty()) 293 | { 294 | if(m_callback) 295 | { 296 | if(m_callback->ttcb_on_anchor_click(m_show_tool, m_anchor)) 297 | { 298 | hide(); 299 | } 300 | } 301 | } 302 | } 303 | break; 304 | case WM_MOUSEWHEEL: 305 | if(can_scroll()) 306 | { 307 | if(scroll(-GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA * m_def_font_size * 5)) 308 | { 309 | draw_window(TRUE); 310 | } 311 | return 0; 312 | } 313 | break; 314 | } 315 | return DefWindowProc(hWnd, uMessage, wParam, lParam); 316 | } 317 | 318 | void litehtml::tooltips::add_tool( unsigned int id, const wchar_t* text, HWND ctl, LPCRECT rc_tool, UINT options ) 319 | { 320 | m_tools[id] = tool(text, ctl, rc_tool, options); 321 | } 322 | 323 | void litehtml::tooltips::clear() 324 | { 325 | m_tools.clear(); 326 | } 327 | 328 | void litehtml::tooltips::get_client_rect( litehtml::position& client ) 329 | { 330 | client.x = m_layout.content_x; 331 | client.y = m_layout.content_y; 332 | client.width = m_layout.content_width; 333 | client.height = m_layout.content_height; 334 | } 335 | 336 | void litehtml::tooltips::create( HWND parent ) 337 | { 338 | m_hWnd = CreateWindowEx(WS_EX_LAYERED | WS_EX_TOOLWINDOW | WS_EX_TOPMOST | WS_EX_NOACTIVATE /*WS_EX_TRANSPARENT*/, LITEHTML_TOOLTIPS_WINDOW, L"", WS_POPUP, 0, 0, 0, 0, NULL, NULL, m_hInst, (LPVOID) this); 339 | if(parent) 340 | { 341 | m_hWndParent = parent; 342 | SetWindowSubclass(parent, SubclassProc, (UINT_PTR) this, (DWORD_PTR) this); 343 | } 344 | } 345 | 346 | void litehtml::tooltips::show( unsigned int id, int top, bool is_update, bool re_render ) 347 | { 348 | tool::map::iterator ti = m_tools.find(id); 349 | if(ti != m_tools.end()) 350 | { 351 | if(!is_update && m_cached_tool != id) 352 | { 353 | clear_images(); 354 | } 355 | m_show_tool = id; 356 | m_last_shown_tool = id; 357 | if(!re_render && m_html || !m_html) 358 | { 359 | if(m_html) 360 | { 361 | m_html = NULL; 362 | } 363 | if(ti->second.options & tool_opt_ask_text) 364 | { 365 | if(m_callback) 366 | { 367 | std::wstring text; 368 | std::wstring css_text; 369 | m_callback->ttcb_get_text(ti->first, text, css_text); 370 | litehtml::css user_style; 371 | if(!css_text.empty()) 372 | { 373 | user_style.parse_stylesheet(css_text.c_str(), L"{tip-style}", 0, litehtml::media_query_list::ptr()); 374 | } 375 | if(!text.empty()) 376 | { 377 | m_html = litehtml::document::createFromString(text.c_str(), this, m_html_context, &user_style); 378 | } 379 | } else 380 | { 381 | return; 382 | } 383 | } else 384 | { 385 | if(!ti->second.text.empty()) 386 | { 387 | m_html = litehtml::document::createFromString(ti->second.text.c_str(), this, m_html_context); 388 | } 389 | } 390 | } 391 | 392 | if(!m_html) 393 | { 394 | return; 395 | } 396 | m_cached_tool = id; 397 | 398 | int w = m_html->render(m_max_width, litehtml::render_no_fixed); 399 | if(w < m_max_width) 400 | { 401 | m_html->render(w, litehtml::render_no_fixed); 402 | } 403 | 404 | calc_layout(&ti->second, &m_layout); 405 | create_dib(m_layout.width, m_layout.height); 406 | 407 | m_html->render(w, litehtml::render_fixed_only); 408 | 409 | m_top = top; 410 | if(m_top) 411 | { 412 | scroll(0); 413 | } 414 | 415 | draw_window(); 416 | ShowWindow(m_hWnd, SW_SHOWNA); 417 | 418 | if(ti->second.options) 419 | 420 | if(m_hide_time) 421 | { 422 | SetTimer(m_hWnd, TIMER_HIDE_TIP, m_hide_time, NULL); 423 | } 424 | } 425 | } 426 | 427 | void litehtml::tooltips::hide() 428 | { 429 | if(m_show_tool) 430 | { 431 | m_show_tool = 0; 432 | ShowWindow(m_hWnd, SW_HIDE); 433 | } 434 | stop_timers(); 435 | } 436 | 437 | LRESULT CALLBACK litehtml::tooltips::SubclassProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData ) 438 | { 439 | tooltips* pThis = (tooltips*) dwRefData; 440 | 441 | switch(uMsg) 442 | { 443 | case WM_MOUSEWHEEL: 444 | if(pThis->can_scroll()) 445 | { 446 | if(pThis->scroll(-GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA * pThis->m_def_font_size * 5)) 447 | { 448 | pThis->draw_window(TRUE); 449 | } 450 | return 0; 451 | } 452 | break; 453 | case WM_MOUSELEAVE: 454 | pThis->m_last_shown_tool = 0; 455 | pThis->hide_current_tool(); 456 | pThis->start_hover_tracking(false); 457 | break; 458 | case WM_LBUTTONDOWN: 459 | case WM_LBUTTONDBLCLK: 460 | case WM_RBUTTONDOWN: 461 | case WM_RBUTTONDBLCLK: 462 | case WM_MBUTTONDOWN: 463 | case WM_MBUTTONDBLCLK: 464 | case WM_NCLBUTTONDOWN: 465 | case WM_NCLBUTTONDBLCLK: 466 | case WM_NCRBUTTONDOWN: 467 | case WM_NCRBUTTONDBLCLK: 468 | case WM_NCMBUTTONDOWN: 469 | case WM_NCMBUTTONDBLCLK: 470 | case WM_KEYDOWN: 471 | case WM_SYSKEYDOWN: 472 | case WM_KILLFOCUS: 473 | case WM_CLOSE: 474 | pThis->hide(); 475 | break; 476 | case WM_MOUSEHOVER: 477 | pThis->on_mouse_hover_tool(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); 478 | break; 479 | case WM_MOUSEMOVE: 480 | pThis->on_mouse_over_tool(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); 481 | break; 482 | } 483 | 484 | return DefSubclassProc(hWnd, uMsg, wParam, lParam); 485 | } 486 | 487 | void litehtml::tooltips::draw_background( litehtml::uint_ptr hdc, const litehtml::background_paint& bg ) 488 | { 489 | if(bg.is_root) 490 | { 491 | cairo_t* cr = (cairo_t*) hdc; 492 | 493 | cairo_save(cr); 494 | cairo_reset_clip(cr); 495 | 496 | m_bg_cache.create_tip_path(cr, 0); 497 | cairo_clip(cr); 498 | 499 | litehtml::background_paint new_bg(bg); 500 | new_bg.clip_box.x = 0; 501 | new_bg.clip_box.y = 0; 502 | new_bg.clip_box.width = m_dib.width(); 503 | new_bg.clip_box.height = m_dib.height(); 504 | new_bg.border_box = new_bg.clip_box; 505 | new_bg.border_radius = litehtml::css_border_radius(); 506 | cairo_container::draw_background(hdc, new_bg); 507 | 508 | cairo_restore(cr); 509 | } else 510 | { 511 | cairo_container::draw_background(hdc, bg); 512 | } 513 | } 514 | 515 | int litehtml::tooltips::tip_width() 516 | { 517 | int width = m_html->width(); 518 | switch(m_style) 519 | { 520 | case tips_style_square: 521 | width += 8; 522 | break; 523 | case tips_style_rounded: 524 | width += 16; 525 | break; 526 | case tips_style_baloon: 527 | width += 16; 528 | break; 529 | } 530 | return width; 531 | } 532 | 533 | int litehtml::tooltips::tip_height() 534 | { 535 | int height = m_html->height(); 536 | switch(m_style) 537 | { 538 | case tips_style_square: 539 | height += 4; 540 | break; 541 | case tips_style_rounded: 542 | height += 24; 543 | break; 544 | case tips_style_baloon: 545 | height += 24; 546 | break; 547 | } 548 | return height; 549 | } 550 | 551 | void litehtml::tooltips::content_point( LPPOINT pt ) 552 | { 553 | pt->x = 0; 554 | pt->y = 0; 555 | switch(m_style) 556 | { 557 | case tips_style_square: 558 | pt->x = 4; 559 | pt->y = 2; 560 | break; 561 | case tips_style_rounded: 562 | pt->x = 12; 563 | pt->y = 12; 564 | break; 565 | case tips_style_baloon: 566 | pt->x = 12; 567 | pt->y = 12; 568 | break; 569 | } 570 | } 571 | 572 | void litehtml::tooltips::rounded_rect( cairo_t* cr, int x, int y, int width, int height, int radius, int line_width ) 573 | { 574 | double xx = x; 575 | double yy = y; 576 | double w = width; 577 | double h = height; 578 | double r = radius; 579 | 580 | if(line_width != 0) 581 | { 582 | xx += line_width / 2.0; 583 | yy += line_width / 2.0; 584 | w -= line_width; 585 | h -= line_width; 586 | } 587 | 588 | cairo_new_path(cr); 589 | 590 | cairo_move_to (cr, xx + r, yy); 591 | cairo_arc (cr, xx + w - r, yy + r, r, M_PI + M_PI / 2, M_PI * 2 ); 592 | cairo_arc (cr, xx + w - r, yy + h - r, r, 0, M_PI / 2 ); 593 | cairo_arc (cr, xx + r, yy + h - r, r, M_PI/2, M_PI ); 594 | cairo_arc (cr, xx + r, yy + r, r, M_PI, 270 * M_PI / 180); 595 | cairo_close_path(cr); 596 | } 597 | 598 | void litehtml::tooltips::stop_timers() 599 | { 600 | KillTimer(m_hWnd, TIMER_HIDE_TIP); 601 | m_hide_timer_active = false; 602 | } 603 | 604 | unsigned int litehtml::tooltips::find_tool( int x, int y ) 605 | { 606 | POINT pt = {x, y}; 607 | for(tool::map::iterator t = m_tools.begin(); t != m_tools.end(); t++) 608 | { 609 | if(PtInRect(&t->second.rc_tool, pt)) 610 | { 611 | return t->first; 612 | } 613 | } 614 | return 0; 615 | } 616 | 617 | void litehtml::tooltips::calc_layout( tool* t, tip_layout* layout ) 618 | { 619 | layout->style = m_style; 620 | layout->width = m_html->width(); 621 | layout->height = m_html->height(); 622 | layout->content_width = m_html->width(); 623 | layout->content_height = m_html->height(); 624 | layout->radius = m_radius; 625 | 626 | switch(m_style) 627 | { 628 | case tips_style_square: 629 | layout->width += 8; 630 | layout->height += 4; 631 | layout->content_x = 4; 632 | layout->content_y = 2; 633 | break; 634 | case tips_style_rounded: 635 | if(layout->radius > 4) 636 | { 637 | layout->width += layout->radius * 2; 638 | layout->height += layout->radius * 2; 639 | layout->content_x = layout->radius; 640 | layout->content_y = layout->radius; 641 | } else 642 | { 643 | layout->width += 8; 644 | layout->height += max(2, layout->radius) * 2; 645 | layout->content_x = 4; 646 | layout->content_y = max(2, layout->radius); 647 | } 648 | break; 649 | case tips_style_baloon: 650 | layout->width += 24; 651 | if(layout->width < 32) layout->width = 32; 652 | layout->height += 24; 653 | if(layout->height < 32) layout->height = 32; 654 | break; 655 | } 656 | 657 | RECT rcDesktop; 658 | GetDesktopRect(&rcDesktop, m_hWndParent); 659 | 660 | int max_height = (rcDesktop.bottom - rcDesktop.top); 661 | if(m_max_height >= 300) 662 | { 663 | max_height = min(rcDesktop.bottom - rcDesktop.top, m_max_height); 664 | } 665 | 666 | if(layout->height > max_height) 667 | { 668 | layout->width += 7; 669 | } 670 | 671 | RECT rc_tool = t->rc_tool; 672 | MapWindowPoints(m_hWndParent, NULL, (LPPOINT) &rc_tool, 2); 673 | 674 | calc_position(t->options & tool_opt_align_mask, &rc_tool, layout); 675 | 676 | if(layout->y + layout->height > rcDesktop.bottom) 677 | { 678 | int margin = layout->height - m_html->height(); 679 | layout->height = rcDesktop.bottom - layout->y; 680 | layout->content_height = layout->height - margin; 681 | } 682 | 683 | switch(layout->align) 684 | { 685 | case tool_opt_align_top: 686 | layout->anchor_x = rc_tool.left + (rc_tool.right - rc_tool.left) / 2; 687 | layout->anchor_y = rc_tool.top; 688 | if(layout->y + layout->height > rc_tool.top) 689 | { 690 | layout->content_height -= (layout->y + layout->height) - rc_tool.top; 691 | layout->height -= layout->y + layout->height - rc_tool.top; 692 | layout->y = rc_tool.top - layout->height; 693 | } 694 | break; 695 | case tool_opt_align_bottom: 696 | layout->anchor_x = rc_tool.left + (rc_tool.right - rc_tool.left) / 2; 697 | layout->anchor_y = rc_tool.bottom; 698 | if(rc_tool.bottom > layout->y) 699 | { 700 | layout->content_height -= rc_tool.bottom - layout->y; 701 | layout->height -= rc_tool.bottom - layout->y; 702 | layout->y = rc_tool.bottom; 703 | } 704 | break; 705 | case tool_opt_align_left: 706 | layout->anchor_y = rc_tool.top + (rc_tool.bottom - rc_tool.top) / 2; 707 | layout->anchor_x = rc_tool.left; 708 | break; 709 | case tool_opt_align_right: 710 | layout->anchor_y = rc_tool.top + (rc_tool.bottom - rc_tool.top) / 2; 711 | layout->anchor_x = rc_tool.right; 712 | break; 713 | } 714 | 715 | if(m_style == tips_style_baloon) 716 | { 717 | switch(layout->align) 718 | { 719 | case tool_opt_align_top: 720 | layout->content_x = 8; 721 | layout->content_y = 8; 722 | if(layout->width - 8 >= 32) 723 | { 724 | layout->width -= 8; 725 | } else 726 | { 727 | layout->width = 32; 728 | } 729 | break; 730 | case tool_opt_align_left: 731 | layout->content_x = 8; 732 | layout->content_y = 8; 733 | if(layout->height - 8 >= 32) 734 | { 735 | layout->height -= 8; 736 | } else 737 | { 738 | layout->height = 32; 739 | } 740 | break; 741 | case tool_opt_align_bottom: 742 | layout->content_x = 8; 743 | layout->content_y = 16; 744 | if(layout->width - 8 >= 32) 745 | { 746 | layout->width -= 8; 747 | } else 748 | { 749 | layout->width = 32; 750 | } 751 | break; 752 | case tool_opt_align_right: 753 | layout->content_x = 16; 754 | layout->content_y = 8; 755 | if(layout->height - 8 >= 32) 756 | { 757 | layout->height -= 8; 758 | } else 759 | { 760 | layout->height = 32; 761 | } 762 | break; 763 | } 764 | } 765 | // add 5px shadow 766 | layout->width += 5; 767 | layout->height += 5; 768 | } 769 | 770 | void litehtml::tooltips::GetDesktopRect(RECT* rcDsk, HWND hWnd) 771 | { 772 | int nMonitors = GetSystemMetrics(80); 773 | if(!nMonitors) nMonitors = 1; 774 | if(nMonitors == 1) 775 | { 776 | oneMonitor: 777 | HWND dskWnd = GetDesktopWindow(); 778 | GetClientRect(dskWnd, rcDsk); 779 | } else 780 | { 781 | HMONITOR hMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); 782 | if(!hMonitor) 783 | { 784 | goto oneMonitor; 785 | } 786 | MONITORINFO mInf; 787 | mInf.cbSize = sizeof(MONITORINFO); 788 | GetMonitorInfo(hMonitor, &mInf); 789 | *rcDsk = mInf.rcMonitor; 790 | } 791 | } 792 | 793 | void litehtml::tooltips::calc_position( UINT align, LPRECT rc_tool, tip_layout* layout, bool second ) 794 | { 795 | RECT rcDesktop; 796 | GetDesktopRect(&rcDesktop, m_hWndParent); 797 | 798 | int shift = 0; 799 | if(m_style != tips_style_baloon) 800 | { 801 | shift = 8; 802 | } 803 | 804 | switch(align) 805 | { 806 | case tool_opt_align_top: 807 | layout->align = tool_opt_align_top; 808 | layout->x = rc_tool->left + (rc_tool->right - rc_tool->left) / 2 - layout->width /2; 809 | layout->y = rc_tool->top - layout->height - shift; 810 | if(layout->x + layout->width > rcDesktop.right) 811 | { 812 | layout->x = rcDesktop.right - layout->width; 813 | } 814 | if(layout->x < rcDesktop.left) 815 | { 816 | layout->x = rcDesktop.left; 817 | } 818 | if(layout->y < rcDesktop.top) 819 | { 820 | if(!second) 821 | { 822 | if(rcDesktop.bottom - rc_tool->bottom >= layout->height + shift) 823 | { 824 | calc_position(tool_opt_align_bottom, rc_tool, layout, true); 825 | } else 826 | { 827 | if(rc_tool->left - rcDesktop.left > rcDesktop.right - rc_tool->right) 828 | { 829 | calc_position(tool_opt_align_left, rc_tool, layout, true); 830 | } else 831 | { 832 | calc_position(tool_opt_align_right, rc_tool, layout, true); 833 | } 834 | } 835 | } else 836 | { 837 | layout->y = rcDesktop.top; 838 | } 839 | } 840 | break; 841 | case tool_opt_align_bottom: 842 | layout->align = tool_opt_align_bottom; 843 | layout->x = rc_tool->left + (rc_tool->right - rc_tool->left) / 2 - layout->width /2; 844 | layout->y = rc_tool->bottom + shift; 845 | if(layout->x + layout->width > rcDesktop.right) 846 | { 847 | layout->x = rcDesktop.right - layout->width; 848 | } 849 | if(layout->x < rcDesktop.left) 850 | { 851 | layout->x = rcDesktop.left; 852 | } 853 | if(layout->y + layout->height > rcDesktop.bottom) 854 | { 855 | if(!second) 856 | { 857 | if(rc_tool->top - rcDesktop.top >= layout->height + shift) 858 | { 859 | calc_position(tool_opt_align_top, rc_tool, layout, true); 860 | } else 861 | { 862 | if(rc_tool->left - rcDesktop.left > rcDesktop.right - rc_tool->right) 863 | { 864 | calc_position(tool_opt_align_left, rc_tool, layout, true); 865 | } else 866 | { 867 | calc_position(tool_opt_align_right, rc_tool, layout, true); 868 | } 869 | } 870 | } else 871 | { 872 | layout->y = rcDesktop.top; 873 | } 874 | } 875 | break; 876 | case tool_opt_align_left: 877 | layout->align = tool_opt_align_left; 878 | layout->y = rc_tool->top + (rc_tool->bottom - rc_tool->top) / 2 - layout->height /2; 879 | layout->x = rc_tool->left - layout->width - shift; 880 | if(layout->y + layout->height > rcDesktop.bottom) 881 | { 882 | layout->y = rcDesktop.bottom - layout->height; 883 | } 884 | if(layout->y < rcDesktop.top) 885 | { 886 | layout->y = rcDesktop.top; 887 | } 888 | if(layout->x < rcDesktop.left) 889 | { 890 | if(!second) 891 | { 892 | if(rcDesktop.right - rc_tool->right >= layout->width + shift) 893 | { 894 | calc_position(tool_opt_align_right, rc_tool, layout, true); 895 | } else 896 | { 897 | if(rc_tool->top - rcDesktop.top > rcDesktop.bottom - rc_tool->bottom) 898 | { 899 | calc_position(tool_opt_align_top, rc_tool, layout, true); 900 | } else 901 | { 902 | calc_position(tool_opt_align_bottom, rc_tool, layout, true); 903 | } 904 | } 905 | } else 906 | { 907 | layout->x = rcDesktop.left; 908 | } 909 | } 910 | break; 911 | case tool_opt_align_right: 912 | layout->align = tool_opt_align_right; 913 | layout->y = rc_tool->top + (rc_tool->bottom - rc_tool->top) / 2 - layout->height /2; 914 | layout->x = rc_tool->right + shift; 915 | if(layout->y + layout->height > rcDesktop.bottom) 916 | { 917 | layout->y = rcDesktop.bottom - layout->height; 918 | } 919 | if(layout->y < rcDesktop.top) 920 | { 921 | layout->y = rcDesktop.top; 922 | } 923 | if(layout->x + layout->width > rcDesktop.right) 924 | { 925 | if(!second) 926 | { 927 | if(rc_tool->left - rcDesktop.left >= layout->width + shift) 928 | { 929 | calc_position(tool_opt_align_left, rc_tool, layout, true); 930 | } else 931 | { 932 | if(rc_tool->top - rcDesktop.top > rcDesktop.bottom - rc_tool->bottom) 933 | { 934 | calc_position(tool_opt_align_top, rc_tool, layout, true); 935 | } else 936 | { 937 | calc_position(tool_opt_align_bottom, rc_tool, layout, true); 938 | } 939 | } 940 | } else 941 | { 942 | layout->x = rcDesktop.left; 943 | } 944 | } 945 | break; 946 | } 947 | } 948 | 949 | void litehtml::tooltips::baloon( cairo_t* cr, int x, int y, int width, int height, int ax, int ay, UINT align, int radius, int line_width ) 950 | { 951 | double xx = x; 952 | double yy = y; 953 | double w = width; 954 | double h = height; 955 | double r = radius; 956 | double axx = ax; 957 | double ayy = ay; 958 | 959 | if(line_width != 0) 960 | { 961 | xx += line_width / 2.0; 962 | yy += line_width / 2.0; 963 | w -= line_width; 964 | h -= line_width; 965 | } 966 | 967 | cairo_new_path(cr); 968 | 969 | switch(align) 970 | { 971 | case tool_opt_align_top: 972 | h -= 8; 973 | if(axx - 8 < xx + 8) axx = xx + 16; 974 | if(axx + 8 > xx + w - 8) axx = xx + w - 16; 975 | cairo_move_to (cr, xx + r, yy); 976 | cairo_arc (cr, xx + w - r, yy + r, r, M_PI + M_PI / 2, M_PI * 2 ); 977 | cairo_arc (cr, xx + w - r, yy + h - r, r, 0, M_PI / 2 ); 978 | cairo_line_to (cr, axx + 8.0, yy + h); 979 | cairo_line_to (cr, axx, ayy); 980 | cairo_line_to (cr, axx - 8.0, yy + h); 981 | cairo_arc (cr, xx + r, yy + h - r, r, M_PI/2, M_PI ); 982 | cairo_arc (cr, xx + r, yy + r, r, M_PI, 270 * M_PI / 180); 983 | break; 984 | case tool_opt_align_bottom: 985 | h -= 8; 986 | yy += 8; 987 | if(axx - 8 < xx + 8) axx = xx + 16; 988 | if(axx + 8 > xx + w - 8) axx = xx + w - 16; 989 | cairo_move_to (cr, xx + r, yy); 990 | cairo_line_to (cr, axx - 8.0, yy); 991 | cairo_line_to (cr, axx, ayy); 992 | cairo_line_to (cr, axx + 8.0, yy); 993 | cairo_arc (cr, xx + w - r, yy + r, r, M_PI + M_PI / 2, M_PI * 2 ); 994 | cairo_arc (cr, xx + w - r, yy + h - r, r, 0, M_PI / 2 ); 995 | cairo_arc (cr, xx + r, yy + h - r, r, M_PI/2, M_PI ); 996 | cairo_arc (cr, xx + r, yy + r, r, M_PI, 270 * M_PI / 180); 997 | break; 998 | case tool_opt_align_left: 999 | w -= 8; 1000 | if(ayy - 8 < yy + 8) ayy = yy + 16; 1001 | if(ayy + 8 > yy + h - 8) ayy = yy + h - 16; 1002 | cairo_move_to (cr, xx + r, yy); 1003 | cairo_arc (cr, xx + w - r, yy + r, r, M_PI + M_PI / 2, M_PI * 2 ); 1004 | cairo_line_to (cr, xx + w, ayy - 8); 1005 | cairo_line_to (cr, axx, ayy); 1006 | cairo_line_to (cr, xx + w, ayy + 8); 1007 | cairo_arc (cr, xx + w - r, yy + h - r, r, 0, M_PI / 2 ); 1008 | cairo_arc (cr, xx + r, yy + h - r, r, M_PI/2, M_PI ); 1009 | cairo_arc (cr, xx + r, yy + r, r, M_PI, 270 * M_PI / 180); 1010 | break; 1011 | case tool_opt_align_right: 1012 | w -= 8; 1013 | xx += 8; 1014 | if(ayy - 8 < yy + 8) ayy = yy + 16; 1015 | if(ayy + 8 > yy + h - 8) ayy = yy + h - 16; 1016 | cairo_move_to (cr, xx + r, yy); 1017 | cairo_arc (cr, xx + w - r, yy + r, r, M_PI + M_PI / 2, M_PI * 2 ); 1018 | cairo_arc (cr, xx + w - r, yy + h - r, r, 0, M_PI / 2 ); 1019 | cairo_arc (cr, xx + r, yy + h - r, r, M_PI/2, M_PI ); 1020 | cairo_line_to (cr, xx, ayy + 8); 1021 | cairo_line_to (cr, axx, ayy); 1022 | cairo_line_to (cr, xx, ayy - 8); 1023 | cairo_arc (cr, xx + r, yy + r, r, M_PI, 270 * M_PI / 180); 1024 | break; 1025 | default: 1026 | cairo_move_to (cr, xx + r, yy); 1027 | cairo_arc (cr, xx + w - r, yy + r, r, M_PI + M_PI / 2, M_PI * 2 ); 1028 | cairo_arc (cr, xx + w - r, yy + h - r, r, 0, M_PI / 2 ); 1029 | cairo_arc (cr, xx + r, yy + h - r, r, M_PI/2, M_PI ); 1030 | cairo_arc (cr, xx + r, yy + r, r, M_PI, 270 * M_PI / 180); 1031 | } 1032 | 1033 | cairo_close_path(cr); 1034 | } 1035 | 1036 | int litehtml::tooltips::get_default_font_size() 1037 | { 1038 | return m_def_font_size; 1039 | } 1040 | 1041 | const wchar_t* litehtml::tooltips::get_default_font_name() 1042 | { 1043 | return m_def_font_name.c_str(); 1044 | } 1045 | 1046 | void litehtml::tooltips::init_def_font() 1047 | { 1048 | LOGFONT lf; 1049 | GetObject(GetStockObject(DEFAULT_GUI_FONT), sizeof(LOGFONT), (LPVOID) &lf); 1050 | 1051 | m_def_font_name = lf.lfFaceName; 1052 | 1053 | HDC hdc = GetDC(NULL); 1054 | HFONT old_font = (HFONT) SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT)); 1055 | TEXTMETRIC tm; 1056 | GetTextMetrics(hdc, &tm); 1057 | SelectObject(hdc, old_font); 1058 | ReleaseDC(NULL, hdc); 1059 | 1060 | m_def_font_size = tm.tmHeight; 1061 | } 1062 | 1063 | void litehtml::tooltips::disable( bool val ) 1064 | { 1065 | m_disabled = val; 1066 | if(val) 1067 | { 1068 | hide(); 1069 | } 1070 | } 1071 | 1072 | void litehtml::tooltips::update( unsigned int id, bool re_render ) 1073 | { 1074 | if(id == m_show_tool && IsWindowVisible(m_hWnd)) 1075 | { 1076 | show(id, m_top, true, re_render); 1077 | } 1078 | } 1079 | 1080 | void litehtml::tooltips::create_dib( int width, int height ) 1081 | { 1082 | if(!m_cr || !m_dib.hdc() || m_dib.width() != width || m_dib.height() != height) 1083 | { 1084 | if(m_surface) cairo_surface_destroy(m_surface); 1085 | if(m_cr) cairo_destroy(m_cr); 1086 | 1087 | m_dib.clear(); 1088 | m_dib.create(width, height, true); 1089 | 1090 | m_surface = cairo_image_surface_create_for_data((unsigned char*) m_dib.bits(), CAIRO_FORMAT_ARGB32, m_dib.width(), m_dib.height(), m_dib.width() * 4); 1091 | m_cr = cairo_create(m_surface); 1092 | } else 1093 | { 1094 | m_dib.clear(); 1095 | } 1096 | 1097 | } 1098 | 1099 | void litehtml::tooltips::draw_window(BOOL clr) 1100 | { 1101 | if(clr) 1102 | { 1103 | cairo_save(m_cr); 1104 | 1105 | cairo_set_operator(m_cr, CAIRO_OPERATOR_SOURCE); 1106 | cairo_set_source_rgba(m_cr, 0, 0, 0, 0); 1107 | cairo_paint(m_cr); 1108 | 1109 | cairo_restore(m_cr); 1110 | } 1111 | 1112 | m_bg_cache.draw(m_cr, &m_layout, m_hWnd, m_alpha); 1113 | 1114 | litehtml::position clip(m_layout.content_x, m_layout.content_y, m_layout.content_width, m_layout.content_height); 1115 | cairo_save(m_cr); 1116 | cairo_rectangle(m_cr, clip.x, clip.y, clip.width, clip.height); 1117 | cairo_clip(m_cr); 1118 | m_html->draw((litehtml::uint_ptr) m_cr, m_layout.content_x, m_layout.content_y - m_top, &clip); 1119 | cairo_restore(m_cr); 1120 | 1121 | // draw scrollbar 1122 | if(m_html->height() > m_layout.content_height) 1123 | { 1124 | double sb_ratio = (double) m_layout.content_height / (double) m_html->height(); 1125 | double sb_height = m_layout.content_height * sb_ratio; 1126 | double sb_top = m_layout.content_y + m_top * sb_ratio; 1127 | 1128 | rounded_rect(m_cr, m_layout.content_x + m_layout.content_width + 3, (int) sb_top, 4, (int) sb_height, 2, 0); 1129 | cairo_set_source_rgb(m_cr, (double) GetRValue(m_bg_cache.m_clr_border) / 255.0, (double) GetGValue(m_bg_cache.m_clr_border) / 255.0, (double) GetBValue(m_bg_cache.m_clr_border) / 255.0); 1130 | cairo_fill(m_cr); 1131 | } 1132 | 1133 | POINT ptDst; 1134 | POINT ptSrc = {0, 0}; 1135 | SIZE size; 1136 | 1137 | BLENDFUNCTION bf; 1138 | bf.BlendOp = AC_SRC_OVER; 1139 | bf.BlendFlags = 0; 1140 | bf.AlphaFormat = AC_SRC_ALPHA; 1141 | bf.SourceConstantAlpha = 255; 1142 | 1143 | ptDst.x = m_layout.x; 1144 | ptDst.y = m_layout.y; 1145 | 1146 | size.cx = m_dib.width(); 1147 | size.cy = m_dib.height(); 1148 | 1149 | UpdateLayeredWindow(m_hWnd, NULL, &ptDst, &size, m_dib.hdc(), &ptSrc, 0, &bf, ULW_ALPHA); 1150 | } 1151 | 1152 | BOOL litehtml::tooltips::scroll( int dx ) 1153 | { 1154 | if(IsWindow(m_hWnd) && IsWindowVisible(m_hWnd)) 1155 | { 1156 | int new_top = m_top + dx; 1157 | if(new_top < 0) new_top = 0; 1158 | if(new_top > m_html->height() - m_layout.content_height) 1159 | { 1160 | new_top = m_html->height() - m_layout.content_height; 1161 | } 1162 | if(new_top != m_top) 1163 | { 1164 | m_top = new_top; 1165 | return TRUE; 1166 | } 1167 | } 1168 | return FALSE; 1169 | } 1170 | 1171 | BOOL litehtml::tooltips::can_scroll() 1172 | { 1173 | if(IsWindow(m_hWnd) && IsWindowVisible(m_hWnd)) 1174 | { 1175 | if(m_html->height() > m_layout.content_height) 1176 | { 1177 | return TRUE; 1178 | } 1179 | } 1180 | return FALSE; 1181 | } 1182 | 1183 | void litehtml::tooltips::set_def_font( const wchar_t* font_name, int font_size ) 1184 | { 1185 | if(!font_name) 1186 | { 1187 | init_def_font(); 1188 | } else 1189 | { 1190 | m_def_font_name = font_name; 1191 | m_def_font_size = font_size; 1192 | } 1193 | } 1194 | 1195 | void litehtml::tooltips::start_hover_tracking( bool start ) 1196 | { 1197 | TRACKMOUSEEVENT tm = {0}; 1198 | tm.cbSize = sizeof(TRACKMOUSEEVENT); 1199 | tm.dwFlags = TME_QUERY; 1200 | TrackMouseEvent(&tm); 1201 | 1202 | if(start) 1203 | { 1204 | if(!(tm.dwFlags & TME_HOVER)) 1205 | { 1206 | tm.dwFlags = TME_HOVER; 1207 | tm.hwndTrack = m_hWndParent; 1208 | tm.dwHoverTime = m_show_time; 1209 | TrackMouseEvent(&tm); 1210 | m_mouse_hover_on = true; 1211 | } 1212 | } else 1213 | { 1214 | if(tm.dwFlags & TME_HOVER) 1215 | { 1216 | tm.dwFlags = TME_HOVER | TME_CANCEL; 1217 | tm.hwndTrack = m_hWndParent; 1218 | tm.dwHoverTime = m_show_time; 1219 | TrackMouseEvent(&tm); 1220 | m_mouse_hover_on = false; 1221 | } 1222 | } 1223 | } 1224 | 1225 | void litehtml::tooltips::update_tool(unsigned int id, bool re_render, bool redraw_only) 1226 | { 1227 | if(IsWindow(m_hWnd)) 1228 | { 1229 | if(redraw_only) 1230 | { 1231 | PostMessage(m_hWnd, WM_REDRAW_TIP, (WPARAM) id, 0); 1232 | } else 1233 | { 1234 | PostMessage(m_hWnd, WM_UPDATE_TIP, (WPARAM) id, (LPARAM) re_render); 1235 | } 1236 | } 1237 | } 1238 | 1239 | bool litehtml::tooltips::is_tool_interactive( unsigned int id ) 1240 | { 1241 | tool::map::iterator ti = m_tools.find(id); 1242 | if(ti != m_tools.end()) 1243 | { 1244 | if(ti->second.options & tool_opt_interactive) 1245 | { 1246 | return true; 1247 | } 1248 | } 1249 | return false; 1250 | } 1251 | 1252 | void litehtml::tooltips::hide_current_tool() 1253 | { 1254 | if(!is_tool_interactive(m_show_tool)) 1255 | { 1256 | hide(); 1257 | } else 1258 | { 1259 | if(!m_hide_timer_active) 1260 | { 1261 | m_hide_timer_active = true; 1262 | SetTimer(m_hWnd, TIMER_HIDE_TIP, m_hide_time_int, NULL); 1263 | } 1264 | } 1265 | } 1266 | 1267 | void litehtml::tooltips::on_mouse_over_tool( int x, int y ) 1268 | { 1269 | if(!m_disabled) 1270 | { 1271 | unsigned int over_id = find_tool(x, y); 1272 | if(over_id != m_last_shown_tool) 1273 | { 1274 | m_last_shown_tool = 0; 1275 | } 1276 | 1277 | if(over_id != m_show_tool) 1278 | { 1279 | hide_current_tool(); 1280 | } 1281 | 1282 | if(!m_show_tool && !m_last_shown_tool || m_hide_timer_active) 1283 | { 1284 | start_hover_tracking(true); 1285 | } 1286 | } 1287 | } 1288 | 1289 | void litehtml::tooltips::on_mouse_hover_tool( int x, int y ) 1290 | { 1291 | m_mouse_hover_on = false; 1292 | if(!m_disabled) 1293 | { 1294 | unsigned int over_id = find_tool(x, y); 1295 | if(over_id) 1296 | { 1297 | hide(); 1298 | show(over_id); 1299 | } 1300 | } 1301 | } 1302 | 1303 | void litehtml::tooltips::update_cursor() 1304 | { 1305 | LPCWSTR defArrow = IDC_ARROW; 1306 | 1307 | if(!m_html) 1308 | { 1309 | SetCursor(LoadCursor(NULL, defArrow)); 1310 | } else 1311 | { 1312 | if(m_cursor == L"pointer") 1313 | { 1314 | SetCursor(LoadCursor(NULL, IDC_HAND)); 1315 | } else 1316 | { 1317 | SetCursor(LoadCursor(NULL, defArrow)); 1318 | } 1319 | } 1320 | } 1321 | 1322 | void litehtml::tooltips::draw_borders( litehtml::uint_ptr hdc, const litehtml::css_borders& borders, const litehtml::position& draw_pos, bool root ) 1323 | { 1324 | if(root) 1325 | { 1326 | if(borders.top.width.val() != 0 && borders.top.style > litehtml::border_style_hidden) 1327 | { 1328 | cairo_t* cr = (cairo_t*) hdc; 1329 | 1330 | cairo_save(cr); 1331 | cairo_reset_clip(cr); 1332 | 1333 | set_color(cr, borders.top.color); 1334 | 1335 | m_bg_cache.create_tip_path(cr, (int) borders.top.width.val()); 1336 | cairo_set_line_width(cr, borders.top.width.val()); 1337 | cairo_stroke(cr); 1338 | 1339 | cairo_restore(cr); 1340 | } 1341 | } else 1342 | { 1343 | cairo_container::draw_borders(hdc, borders, draw_pos, root); 1344 | } 1345 | } 1346 | 1347 | ////////////////////////////////////////////////////////////////////////// 1348 | 1349 | bool litehtml::tooltips_bg_cache::need_redraw( tip_layout* layout ) 1350 | { 1351 | if(!m_surface) return true; 1352 | 1353 | if( layout->width != m_layout.width || 1354 | layout->height != m_layout.height || 1355 | layout->anchor_x != m_layout.anchor_x || 1356 | layout->anchor_y != m_layout.anchor_y || 1357 | layout->style != m_layout.style || 1358 | layout->align != m_layout.align) 1359 | { 1360 | return true; 1361 | } 1362 | return false; 1363 | } 1364 | 1365 | void litehtml::tooltips_bg_cache::draw( cairo_t* cr, tip_layout* layout, HWND hWnd, BYTE alpha ) 1366 | { 1367 | if(need_redraw(layout)) 1368 | { 1369 | m_layout = *layout; 1370 | if(m_surface) 1371 | { 1372 | cairo_surface_destroy(m_surface); 1373 | m_surface = NULL; 1374 | } 1375 | if(m_dib.width() != m_layout.width || m_dib.height() != m_layout.height) 1376 | { 1377 | m_dib.create(m_layout.width, m_layout.height); 1378 | } else 1379 | { 1380 | m_dib.clear(); 1381 | } 1382 | m_surface = cairo_image_surface_create_for_data((unsigned char*) m_dib.bits(), CAIRO_FORMAT_ARGB32, m_dib.width(), m_dib.height(), m_dib.width() * 4); 1383 | 1384 | // begin draw background 1385 | 1386 | cairo_t* m_cr = cairo_create(m_surface); 1387 | 1388 | COLORREF clr_bg = GetSysColor(COLOR_INFOBK); 1389 | m_clr_border = GetSysColor(COLOR_INFOTEXT); 1390 | 1391 | create_tip_path(m_cr, 1); 1392 | 1393 | BOOL stdDraw = TRUE; 1394 | 1395 | HTHEME ttTheme = OpenThemeData(hWnd, VSCLASS_TOOLTIP); 1396 | if(ttTheme) 1397 | { 1398 | simpledib::dib dib_bg; 1399 | dib_bg.create(m_dib.width(), m_dib.height(), true); 1400 | RECT rcDraw = {0, 0, m_dib.width(), m_dib.height()}; 1401 | 1402 | if(DrawThemeBackground(ttTheme, dib_bg, TTP_STANDARD, 0, &rcDraw, &rcDraw) == S_OK) 1403 | { 1404 | cairo_surface_t* bg_sf = cairo_image_surface_create_for_data((unsigned char*) dib_bg.bits(), CAIRO_FORMAT_ARGB32, dib_bg.width(), dib_bg.height(), dib_bg.width() * 4); 1405 | 1406 | cairo_save(m_cr); 1407 | cairo_clip_preserve(m_cr); 1408 | cairo_set_source_surface(m_cr, bg_sf, 0, 0); 1409 | cairo_paint(m_cr); 1410 | cairo_restore(m_cr); 1411 | 1412 | int border_idx = dib_bg.width() / 2; 1413 | 1414 | m_clr_border = RGB(dib_bg.bits()[border_idx].rgbRed, dib_bg.bits()[border_idx].rgbGreen, dib_bg.bits()[border_idx].rgbBlue); 1415 | 1416 | cairo_surface_destroy(bg_sf); 1417 | 1418 | stdDraw = FALSE; 1419 | } 1420 | CloseThemeData(ttTheme); 1421 | } 1422 | 1423 | if(stdDraw) 1424 | { 1425 | cairo_set_source_rgb(m_cr, (double) GetRValue(clr_bg) / 255.0, (double) GetGValue(clr_bg) / 255.0, (double) GetBValue(clr_bg) / 255.0); 1426 | cairo_fill_preserve(m_cr); 1427 | } 1428 | cairo_set_line_width(m_cr, 1); 1429 | cairo_set_source_rgb(m_cr, (double) GetRValue(m_clr_border) / 255.0, (double) GetGValue(m_clr_border) / 255.0, (double) GetBValue(m_clr_border) / 255.0); 1430 | cairo_stroke(m_cr); 1431 | 1432 | int shadow_width = 10; 1433 | int shadow_height = 10; 1434 | switch(m_layout.style) 1435 | { 1436 | case tips_style_rounded: 1437 | shadow_width = 15; 1438 | shadow_height = 15; 1439 | break; 1440 | case tips_style_baloon: 1441 | shadow_width = 15; 1442 | shadow_height = 15; 1443 | if(layout->align == tool_opt_align_top) 1444 | { 1445 | shadow_height += 8; 1446 | } 1447 | if(layout->align == tool_opt_align_left) 1448 | { 1449 | shadow_width += 8; 1450 | } 1451 | break; 1452 | } 1453 | 1454 | CTxDIB img; 1455 | img._copy(m_dib.bits(), m_dib.width(), m_dib.height(), TRUE); 1456 | 1457 | RGBQUAD* pixels = img.getBits(); 1458 | int sz = img.getWidth() * img.getHeight(); 1459 | 1460 | for(int i=0; i < sz; i++) 1461 | { 1462 | pixels[i].rgbRed = 0; 1463 | pixels[i].rgbGreen = 0; 1464 | pixels[i].rgbBlue = 0; 1465 | } 1466 | 1467 | img.resample(img.getWidth() - 5, img.getHeight() - 5); 1468 | cairo_surface_t* img_sf = cairo_image_surface_create_for_data((unsigned char*) img.getBits(), CAIRO_FORMAT_ARGB32, img.getWidth(), img.getHeight(), img.getWidth() * 4); 1469 | 1470 | // draw shadow at the right side 1471 | { 1472 | simpledib::dib shadow; 1473 | shadow.create(shadow_width, m_dib.height(), true); 1474 | 1475 | cairo_surface_t* surface_shadow = cairo_image_surface_create_for_data((unsigned char*) shadow.bits(), CAIRO_FORMAT_ARGB32, shadow.width(), shadow.height(), shadow.width() * 4); 1476 | cairo_t* cr_shadow = cairo_create(surface_shadow); 1477 | 1478 | cairo_set_source_surface(cr_shadow, img_sf, -m_dib.width() + shadow_width + 5, 5); 1479 | cairo_paint(cr_shadow); 1480 | fastbluralpha(shadow.bits(), shadow.width(), shadow.height(), 5); 1481 | 1482 | cairo_set_operator(m_cr, CAIRO_OPERATOR_DEST_OVER); 1483 | 1484 | cairo_save(m_cr); 1485 | 1486 | cairo_rectangle(m_cr, m_dib.width() - shadow_width, 0, shadow_width, m_dib.height()); 1487 | cairo_clip(m_cr); 1488 | cairo_set_source_surface(m_cr, surface_shadow, m_dib.width() - shadow.width(), 0); 1489 | cairo_paint(m_cr); 1490 | 1491 | cairo_restore(m_cr); 1492 | 1493 | cairo_destroy(cr_shadow); 1494 | cairo_surface_destroy(surface_shadow); 1495 | } 1496 | 1497 | // draw shadow at the bottom side 1498 | { 1499 | simpledib::dib shadow; 1500 | shadow.create(m_dib.width(), shadow_height, true); 1501 | 1502 | cairo_surface_t* surface_shadow = cairo_image_surface_create_for_data((unsigned char*) shadow.bits(), CAIRO_FORMAT_ARGB32, shadow.width(), shadow.height(), shadow.width() * 4); 1503 | cairo_t* cr_shadow = cairo_create(surface_shadow); 1504 | 1505 | cairo_set_source_surface(cr_shadow, img_sf, 5, -m_dib.height() + shadow_height + 5); 1506 | cairo_paint(cr_shadow); 1507 | fastbluralpha(shadow.bits(), shadow.width(), shadow.height(), 5); 1508 | 1509 | cairo_set_operator(m_cr, CAIRO_OPERATOR_DEST_OVER); 1510 | 1511 | cairo_save(m_cr); 1512 | 1513 | cairo_rectangle(m_cr, 0, 0, m_dib.width() - shadow_width, m_dib.height()); 1514 | cairo_clip(m_cr); 1515 | cairo_set_source_surface(m_cr, surface_shadow, 0, m_dib.height() - shadow.height()); 1516 | cairo_paint(m_cr); 1517 | 1518 | cairo_restore(m_cr); 1519 | 1520 | cairo_destroy(cr_shadow); 1521 | cairo_surface_destroy(surface_shadow); 1522 | } 1523 | 1524 | cairo_surface_destroy(img_sf); 1525 | 1526 | cairo_destroy(m_cr); 1527 | } 1528 | 1529 | if(m_surface) 1530 | { 1531 | cairo_save(cr); 1532 | 1533 | cairo_set_source_surface(cr, m_surface, 0, 0); 1534 | if(alpha == 255) 1535 | { 1536 | cairo_paint(cr); 1537 | } else 1538 | { 1539 | cairo_paint_with_alpha(cr, (double) alpha / 255.0); 1540 | } 1541 | 1542 | cairo_restore(cr); 1543 | } 1544 | } 1545 | 1546 | void litehtml::tooltips_bg_cache::fastbluralpha(LPRGBQUAD pixels, int width, int height, int radius) 1547 | { 1548 | if (radius < 1) { 1549 | return; 1550 | } 1551 | 1552 | LPRGBQUAD pix = pixels; 1553 | int w = width; 1554 | int h = height; 1555 | int wm = w - 1; 1556 | int hm = h - 1; 1557 | int wh = w * h; 1558 | int div = radius + radius + 1; 1559 | 1560 | int *a = new int[wh]; 1561 | int asum, x, y, i, yp, yi, yw; 1562 | int *vmin = new int[max(w,h)]; 1563 | 1564 | int divsum = (div+1) >> 1; 1565 | divsum *= divsum; 1566 | int *dv = new int[256 * divsum]; 1567 | for (i=0; i < 256 * divsum; ++i) 1568 | { 1569 | dv[i] = (i/divsum); 1570 | } 1571 | 1572 | yw = yi = 0; 1573 | 1574 | int* stack = new int[div]; 1575 | 1576 | 1577 | int stackpointer; 1578 | int stackstart; 1579 | int *sir; 1580 | int rbs; 1581 | int r1 = radius + 1; 1582 | int aoutsum; 1583 | int ainsum; 1584 | 1585 | for (y = 0; y < h; ++y) 1586 | { 1587 | ainsum = 0; 1588 | aoutsum = 0; 1589 | asum = 0; 1590 | for(i =- radius; i <= radius; ++i) 1591 | { 1592 | sir = stack + i + radius; 1593 | *sir = pix[yi + min(wm, max(i, 0))].rgbReserved; 1594 | 1595 | rbs = r1 - abs(i); 1596 | asum += (*sir) * rbs; 1597 | 1598 | if (i > 0) 1599 | { 1600 | ainsum += *sir; 1601 | } else 1602 | { 1603 | aoutsum += *sir; 1604 | } 1605 | } 1606 | stackpointer = radius; 1607 | 1608 | for (x=0; x < w; ++x) 1609 | { 1610 | a[yi] = dv[asum]; 1611 | 1612 | asum -= aoutsum; 1613 | 1614 | stackstart = stackpointer - radius + div; 1615 | sir = stack + (stackstart % div); 1616 | 1617 | aoutsum -= *sir; 1618 | 1619 | if (y == 0) 1620 | { 1621 | vmin[x] = min(x + radius + 1, wm); 1622 | } 1623 | 1624 | *sir = pix[yw + vmin[x]].rgbReserved; 1625 | 1626 | ainsum += *sir; 1627 | asum += ainsum; 1628 | 1629 | stackpointer = (stackpointer + 1) % div; 1630 | sir = stack + (stackpointer % div); 1631 | 1632 | aoutsum += *sir; 1633 | 1634 | ainsum -= *sir; 1635 | 1636 | ++yi; 1637 | } 1638 | yw += w; 1639 | } 1640 | for (x=0; x < w; ++x) 1641 | { 1642 | ainsum = aoutsum = asum = 0; 1643 | yp =- radius * w; 1644 | 1645 | for(i=-radius; i <= radius; ++i) 1646 | { 1647 | yi = max(0, yp) + x; 1648 | 1649 | sir = stack + i + radius; 1650 | 1651 | *sir = a[yi]; 1652 | 1653 | rbs = r1 - abs(i); 1654 | 1655 | asum += a[yi] * rbs; 1656 | 1657 | if (i > 0) 1658 | { 1659 | ainsum += *sir; 1660 | } else 1661 | { 1662 | aoutsum += *sir; 1663 | } 1664 | 1665 | if (i < hm) 1666 | { 1667 | yp += w; 1668 | } 1669 | } 1670 | 1671 | yi = x; 1672 | stackpointer = radius; 1673 | 1674 | for (y=0; y < h; ++y) 1675 | { 1676 | pix[yi].rgbReserved = dv[asum]; 1677 | 1678 | asum -= aoutsum; 1679 | 1680 | stackstart = stackpointer - radius + div; 1681 | sir = stack + (stackstart % div); 1682 | 1683 | aoutsum -= *sir; 1684 | 1685 | if (x==0) 1686 | { 1687 | vmin[y] = min(y + r1, hm) * w; 1688 | } 1689 | int p = x + vmin[y]; 1690 | 1691 | *sir = a[p]; 1692 | 1693 | ainsum += *sir; 1694 | 1695 | asum += ainsum; 1696 | 1697 | stackpointer = (stackpointer + 1) % div; 1698 | sir = stack + stackpointer; 1699 | 1700 | aoutsum += *sir; 1701 | ainsum -= *sir; 1702 | 1703 | yi += w; 1704 | } 1705 | } 1706 | delete [] a; 1707 | delete [] vmin; 1708 | delete [] dv; 1709 | delete stack; 1710 | } 1711 | 1712 | void litehtml::tooltips_bg_cache::create_tip_path( cairo_t* cr, int line_width ) 1713 | { 1714 | switch(m_layout.style) 1715 | { 1716 | case tips_style_rounded: 1717 | tooltips::rounded_rect(cr, 0, 0, m_dib.width() - 5, m_dib.height() - 5, m_layout.radius, line_width); 1718 | break; 1719 | case tips_style_baloon: 1720 | tooltips::baloon(cr, 0, 0, m_dib.width() - 5, m_dib.height() - 5, m_layout.anchor_x - m_layout.x, m_layout.anchor_y - m_layout.y, m_layout.align, m_layout.radius, line_width); 1721 | break; 1722 | default: 1723 | cairo_rectangle(cr, 0.5, 0.5, m_dib.width() - 1 - 5, m_dib.height() - 1 - 5); 1724 | break; 1725 | } 1726 | } 1727 | 1728 | -------------------------------------------------------------------------------- /src/tooltips.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace litehtml 6 | { 7 | enum tips_style 8 | { 9 | tips_style_square, 10 | tips_style_rounded, 11 | tips_style_baloon 12 | }; 13 | 14 | enum tool_options 15 | { 16 | tool_opt_align_top = 0x0000, 17 | tool_opt_align_bottom = 0x0001, 18 | tool_opt_align_left = 0x0002, 19 | tool_opt_align_right = 0x0003, 20 | tool_opt_align_mask = 0x000F, 21 | tool_opt_ask_text = 0x0010, 22 | tool_opt_interactive = 0x0020, 23 | }; 24 | 25 | class tooltips_callback 26 | { 27 | public: 28 | virtual CTxDIB* ttcb_get_image(unsigned int id, LPCWSTR url, bool redraw_on_ready) = 0; 29 | virtual void ttcb_get_text(unsigned int id, std::wstring& text, std::wstring& css_text) = 0; 30 | virtual bool ttcb_on_anchor_click(unsigned int id, const std::wstring& url) = 0; 31 | }; 32 | 33 | struct tip_layout 34 | { 35 | int x; 36 | int y; 37 | int width; 38 | int height; 39 | int content_x; 40 | int content_y; 41 | int content_height; 42 | int content_width; 43 | int anchor_x; 44 | int anchor_y; 45 | UINT align; 46 | tips_style style; 47 | int radius; 48 | }; 49 | 50 | struct tool 51 | { 52 | typedef std::map map; 53 | 54 | std::wstring text; 55 | HWND hWnd; 56 | RECT rc_tool; 57 | UINT options; 58 | 59 | tool() 60 | { 61 | hWnd = 0; 62 | options = 0; 63 | ZeroMemory(&rc_tool, sizeof(rc_tool)); 64 | } 65 | 66 | tool(const tool& val) 67 | { 68 | text = val.text; 69 | hWnd = val.hWnd; 70 | rc_tool = val.rc_tool; 71 | options = val.options; 72 | } 73 | 74 | tool(const wchar_t* txt, HWND wnd, LPCRECT rc, UINT opt) 75 | { 76 | if(txt) 77 | { 78 | text = txt; 79 | } 80 | hWnd = wnd; 81 | rc_tool = *rc; 82 | options = opt; 83 | } 84 | 85 | void operator=(const tool& val) 86 | { 87 | text = val.text; 88 | hWnd = val.hWnd; 89 | rc_tool = val.rc_tool; 90 | options = val.options; 91 | } 92 | }; 93 | 94 | struct tooltips_bg_cache 95 | { 96 | simpledib::dib m_dib; 97 | cairo_surface_t* m_surface; 98 | tip_layout m_layout; 99 | COLORREF m_clr_border; 100 | 101 | tooltips_bg_cache() 102 | { 103 | ZeroMemory(&m_layout, sizeof(m_layout)); 104 | m_surface = NULL; 105 | m_clr_border = 0; 106 | } 107 | ~tooltips_bg_cache() 108 | { 109 | clear(); 110 | } 111 | 112 | bool need_redraw(tip_layout* layout); 113 | void draw(cairo_t* cr, tip_layout* layout, HWND hWnd, BYTE alpha); 114 | void create_tip_path(cairo_t* cr, int line_width); 115 | void fastbluralpha(LPRGBQUAD pixels, int width, int height, int radius); 116 | 117 | void clear() 118 | { 119 | if(m_surface) 120 | { 121 | cairo_surface_destroy(m_surface); 122 | m_surface = NULL; 123 | } 124 | m_dib.clear(); 125 | } 126 | }; 127 | 128 | #define WM_REDRAW_TIP (WM_USER + 1000) 129 | #define WM_UPDATE_TIP (WM_USER + 1001) 130 | 131 | class tooltips : public cairo_container 132 | { 133 | simpledib::dib m_dib; 134 | cairo_t* m_cr; 135 | cairo_surface_t* m_surface; 136 | tooltips_bg_cache m_bg_cache; 137 | 138 | HINSTANCE m_hInst; 139 | HWND m_hWnd; 140 | HWND m_hWndParent; 141 | tooltips_callback* m_callback; 142 | tool::map m_tools; 143 | litehtml::document::ptr m_html; 144 | litehtml::context* m_html_context; 145 | int m_max_width; 146 | int m_max_height; 147 | int m_show_time; 148 | int m_hide_time; 149 | int m_hide_time_int; 150 | bool m_hide_timer_active; 151 | tips_style m_style; 152 | POINT m_mouse_pos; 153 | unsigned int m_over_tool; 154 | unsigned int m_show_tool; 155 | unsigned int m_last_shown_tool; 156 | unsigned int m_cached_tool; 157 | std::wstring m_def_font_name; 158 | int m_def_font_size; 159 | bool m_disabled; 160 | BYTE m_alpha; 161 | tip_layout m_layout; 162 | int m_top; 163 | bool m_mouse_hover_on; 164 | int m_radius; 165 | std::wstring m_cursor; 166 | std::wstring m_anchor; 167 | public: 168 | tooltips(HINSTANCE hInst, litehtml::context* html_context, int radius = 8); 169 | virtual ~tooltips(void); 170 | 171 | void add_tool(unsigned int id, const wchar_t* text, HWND ctl, LPCRECT rc_tool, UINT options); 172 | void clear(); 173 | void create(HWND parent); 174 | void show(unsigned int id, int top = 0, bool is_update = false, bool re_render = false); 175 | 176 | void hide(); 177 | void set_style(tips_style style) { m_style = style; } 178 | void disable(bool val); 179 | void set_max_size(int width, int height); 180 | void set_times(int show_time, int hide_time, int hide_time_int); 181 | void set_callback(tooltips_callback* cb) { m_callback = cb; } 182 | void set_alpha(int alpha) { m_alpha = alpha; } 183 | void update(unsigned int id, bool re_render); 184 | unsigned int get_current_tip_id() { return m_show_tool; } 185 | void update_tool(unsigned int id, bool re_render, bool redraw_only); 186 | void set_def_font(const wchar_t* font_name, int font_size); 187 | 188 | static void rounded_rect( cairo_t* cr, int x, int y, int width, int height, int radius, int line_width ); 189 | static void baloon( cairo_t* cr, int x, int y, int width, int height, int ax, int ay, UINT align, int radius, int line_width ); 190 | 191 | private: 192 | // cairo_container members 193 | virtual void make_url(LPCWSTR url, LPCWSTR basepath, std::wstring& out); 194 | virtual CTxDIB* get_image(LPCWSTR url, bool redraw_on_ready); 195 | 196 | // litehtml::document_container members 197 | virtual void set_caption(const wchar_t* caption); 198 | virtual void set_base_url(const wchar_t* base_url); 199 | virtual void link(litehtml::document* doc, litehtml::element::ptr el); 200 | virtual void import_css(std::wstring& text, const std::wstring& url, std::wstring& baseurl); 201 | virtual void on_anchor_click(const wchar_t* url, litehtml::element::ptr el); 202 | virtual void set_cursor(const wchar_t* cursor); 203 | virtual void get_client_rect(litehtml::position& client); 204 | virtual int get_default_font_size(); 205 | virtual const wchar_t* get_default_font_name(); 206 | virtual void draw_background(litehtml::uint_ptr hdc, const litehtml::background_paint& bg); 207 | virtual void draw_borders(litehtml::uint_ptr hdc, const litehtml::css_borders& borders, const litehtml::position& draw_pos, bool root); 208 | 209 | virtual LRESULT OnMessage(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam); 210 | static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam); 211 | static LRESULT CALLBACK SubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData); 212 | void registerClass(HINSTANCE hInstance); 213 | int tip_width(); 214 | int tip_height(); 215 | void content_point(LPPOINT pt); 216 | void stop_timers(); 217 | unsigned int find_tool(int x, int y); 218 | void calc_layout(tool* t, tip_layout* layout); 219 | void calc_position(UINT align, LPRECT rc_tool, tip_layout* layout, bool second = false); 220 | void GetDesktopRect(RECT* rcDsk, HWND hWnd); 221 | void init_def_font(); 222 | void create_dib(int width, int height); 223 | void draw_window(BOOL clr = FALSE); 224 | BOOL scroll(int dx); 225 | BOOL can_scroll(); 226 | void start_hover_tracking(bool start); 227 | bool is_tool_interactive(unsigned int id); 228 | void hide_current_tool(); 229 | void on_mouse_over_tool(int x, int y); 230 | void on_mouse_hover_tool(int x, int y); 231 | void update_cursor(); 232 | }; 233 | 234 | inline void tooltips::set_times(int show_time, int hide_time, int hide_time_int) 235 | { 236 | m_show_time = show_time; 237 | m_hide_time = hide_time; 238 | m_hide_time_int = hide_time_int; 239 | } 240 | 241 | inline void tooltips::set_max_size(int width, int height) 242 | { 243 | m_max_width = width; 244 | m_max_height = height; 245 | } 246 | 247 | } 248 | -------------------------------------------------------------------------------- /test/MainWnd.cpp: -------------------------------------------------------------------------------- 1 | #include "globals.h" 2 | #include "MainWnd.h" 3 | 4 | CMainWnd::CMainWnd(HINSTANCE hInst) : m_tips(hInst, &m_html_context) 5 | { 6 | m_counter = 0; 7 | m_hInst = hInst; 8 | setClass(TTT_TEST_WND); 9 | 10 | LPWSTR css = NULL; 11 | 12 | HRSRC hResource = ::FindResource(m_hInst, L"master.css", L"CSS"); 13 | if(hResource) 14 | { 15 | DWORD imageSize = ::SizeofResource(m_hInst, hResource); 16 | if(imageSize) 17 | { 18 | LPCSTR pResourceData = (LPCSTR) ::LockResource(::LoadResource(m_hInst, hResource)); 19 | if(pResourceData) 20 | { 21 | css = new WCHAR[imageSize * 3]; 22 | int ret = MultiByteToWideChar(CP_UTF8, 0, pResourceData, imageSize, css, imageSize * 3); 23 | css[ret] = 0; 24 | } 25 | } 26 | } 27 | if(css) 28 | { 29 | m_html_context.load_master_stylesheet(css); 30 | m_html_context.load_master_stylesheet(L"body {margin:0;}"); 31 | delete css; 32 | } 33 | m_tips.set_callback(this); 34 | m_tips.set_max_size(400, 500); 35 | //m_tips.set_alpha(200); 36 | } 37 | 38 | CMainWnd::~CMainWnd(void) 39 | { 40 | } 41 | 42 | void CMainWnd::preRegisterClass( WNDCLASSEX* wcex ) 43 | { 44 | wcex->hCursor = LoadCursor(NULL, IDC_ARROW); 45 | wcex->hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); 46 | } 47 | 48 | struct 49 | { 50 | UINT id; 51 | RECT rc; 52 | UINT options; 53 | LPCWSTR text; 54 | } g_tools[] = 55 | { 56 | {3, {10, 10, 110, 110}, litehtml::tool_opt_align_left, L"

Lite HTML engine


The Lite HTML engine is created for embedding into applications, to show the HTML code. Lite HTML supports most of the CSS2/CSS3 standards. The engine is written on C++ with STL (MS Visual Studio 2008) and was tested on Windows platform only.

Embedding Lite HTML

Firstly, the Lite HTML don't have the code for draw anything. You are free to use any draw engine. We've included the win32_container class as example to show how to implement the draw code.

" }, 57 | {2, {10, 120, 110, 220}, litehtml::tool_opt_ask_text | litehtml::tool_opt_align_left, L"Simple text" }, 58 | {1, {300, 300, 350, 350}, litehtml::tool_opt_ask_text | litehtml::tool_opt_align_top | litehtml::tool_opt_interactive, L"

Lite HTML engine


The Lite HTML engine is created for embedding into applications, to show the HTML code. Lite HTML supports most of the CSS2/CSS3 standards. The engine is written on C++ with STL (MS Visual Studio 2008) and was tested on Windows platform only.

Embedding Lite HTML

Firstly, the Lite HTML don't have the code for draw anything. You are free to use any draw engine. We've included the win32_container class as example to show how to implement the draw code.

" }, 59 | {0, {0, 0, 0, 0}, NULL }, 60 | }; 61 | 62 | LRESULT CMainWnd::OnMessage( HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam ) 63 | { 64 | switch(uMessage) 65 | { 66 | case WM_CLOSE: 67 | PostQuitMessage(0); 68 | break; 69 | case WM_PAINT: 70 | { 71 | PAINTSTRUCT ps; 72 | HDC hdc = BeginPaint(hWnd, &ps); 73 | for(int i = 0; g_tools[i].id; i++) 74 | { 75 | FillRect(hdc, &g_tools[i].rc, (HBRUSH) (COLOR_HIGHLIGHT + 1)); 76 | } 77 | } 78 | return 0; 79 | case WM_TIMER: 80 | m_counter++; 81 | m_tips.update_tool(2, false, false); 82 | return 0; 83 | } 84 | return CTxWnd::OnMessage(hWnd, uMessage, wParam, lParam); 85 | } 86 | 87 | BOOL CMainWnd::create() 88 | { 89 | BOOL ret = CTxWnd::create(0, L"Light HTML Tooltips", WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 90 | CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, m_hInst); 91 | 92 | SetTimer(m_hWnd, 1, 200, NULL); 93 | 94 | ShowWindow(m_hWnd, SW_SHOW); 95 | UpdateWindow(m_hWnd); 96 | 97 | for(int i = 0; g_tools[i].id; i++) 98 | { 99 | m_tips.add_tool(g_tools[i].id, g_tools[i].text, NULL, &g_tools[i].rc, g_tools[i].options); 100 | } 101 | 102 | m_tips.create(m_hWnd); 103 | m_tips.set_style(litehtml::tips_style_baloon); 104 | 105 | return ret; 106 | } 107 | 108 | void CMainWnd::ttcb_get_text( unsigned int id, std::wstring& text, std::wstring& css_text ) 109 | { 110 | switch(id) 111 | { 112 | case 1: 113 | { 114 | WCHAR path[MAX_PATH]; 115 | GetModuleFileName(NULL, path, MAX_PATH); 116 | PathRemoveFileSpec(path); 117 | PathAddBackslash(path); 118 | StringCchCat(path, MAX_PATH, L"tip1.html"); 119 | LPWSTR html = load_text_file(path, true, L"UTF-8"); 120 | if(html) 121 | { 122 | text = html; 123 | delete html; 124 | } 125 | } 126 | break; 127 | case 2: 128 | { 129 | WCHAR cnt[255]; 130 | wsprintf(cnt, L"Current count: %d", m_counter); 131 | text = cnt; 132 | } 133 | break; 134 | } 135 | } 136 | 137 | CTxDIB* CMainWnd::ttcb_get_image( unsigned int id, LPCWSTR url, bool redraw_on_ready ) 138 | { 139 | return NULL; 140 | } 141 | 142 | bool CMainWnd::ttcb_on_anchor_click( unsigned int id, const std::wstring& url ) 143 | { 144 | ShellExecute(m_hWnd, NULL, url.c_str(), NULL, NULL, SW_SHOW); 145 | 146 | return false; 147 | } 148 | -------------------------------------------------------------------------------- /test/MainWnd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "txwnd.h" 3 | #include 4 | 5 | #define TTT_TEST_WND L"TTT_TEST_WND" 6 | 7 | class CMainWnd : public CTxWnd, 8 | public litehtml::tooltips_callback 9 | { 10 | HINSTANCE m_hInst; 11 | litehtml::tooltips m_tips; 12 | litehtml::context m_html_context; 13 | int m_counter; 14 | public: 15 | CMainWnd(HINSTANCE hInst); 16 | virtual ~CMainWnd(void); 17 | 18 | virtual void preRegisterClass( WNDCLASSEX* wcex ); 19 | virtual LRESULT OnMessage( HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam ); 20 | 21 | virtual CTxDIB* ttcb_get_image(unsigned int id, LPCWSTR url, bool redraw_on_ready); 22 | virtual void ttcb_get_text(unsigned int id, std::wstring& text, std::wstring& css_text); 23 | virtual bool ttcb_on_anchor_click(unsigned int id, const std::wstring& url); 24 | 25 | BOOL create(); 26 | }; 27 | -------------------------------------------------------------------------------- /test/TxWnd.cpp: -------------------------------------------------------------------------------- 1 | #include "globals.h" 2 | #include "TxWnd.h" 3 | 4 | CTxWnd::CTxWnd() 5 | { 6 | m_hWnd = NULL; 7 | setClass(TXWND_CLASS); 8 | } 9 | 10 | CTxWnd::~CTxWnd(void) 11 | { 12 | destroy(); 13 | } 14 | 15 | LRESULT CALLBACK CTxWnd::WndProc( HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam ) 16 | { 17 | CTxWnd* pThis = NULL; 18 | if(IsWindow(hWnd)) 19 | { 20 | pThis = (CTxWnd*)GetProp(hWnd, TEXT("CTxWnd_this")); 21 | if(pThis && pThis->m_hWnd != hWnd) 22 | { 23 | pThis = NULL; 24 | } 25 | } 26 | if(pThis || uMessage == WM_CREATE) 27 | { 28 | switch (uMessage) 29 | { 30 | case WM_CREATE: 31 | { 32 | LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam; 33 | pThis = (CTxWnd*)(lpcs->lpCreateParams); 34 | SetProp(hWnd, TEXT("CTxWnd_this"), (HANDLE) pThis); 35 | pThis->m_hWnd = hWnd; 36 | } 37 | break; 38 | case WM_DESTROY: 39 | { 40 | LRESULT ret = pThis->OnMessage(hWnd, uMessage, wParam, lParam); 41 | RemoveProp(hWnd, TEXT("CTxWnd_this")); 42 | pThis->m_hWnd = NULL; 43 | return ret; 44 | } 45 | break; 46 | } 47 | return pThis->OnMessage(hWnd, uMessage, wParam, lParam); 48 | } 49 | return DefWindowProc(hWnd, uMessage, wParam, lParam); 50 | } 51 | 52 | void CTxWnd::registerClass(HINSTANCE hInstance) 53 | { 54 | WNDCLASSEX wc; 55 | if(!GetClassInfoEx(hInstance, m_class, &wc)) 56 | { 57 | ZeroMemory(&wc, sizeof(wc)); 58 | wc.cbSize = sizeof(wc); 59 | wc.lpfnWndProc = (WNDPROC)CTxWnd::WndProc; 60 | wc.hInstance = hInstance; 61 | wc.lpszClassName = m_class; 62 | 63 | preRegisterClass(&wc); 64 | 65 | ATOM ret = RegisterClassEx(&wc); 66 | int i=0; 67 | i++; 68 | } 69 | } 70 | 71 | void CTxWnd::preRegisterClass( WNDCLASSEX* wcex ) 72 | { 73 | 74 | } 75 | 76 | LRESULT CTxWnd::OnMessage( HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam ) 77 | { 78 | return DefWindowProc(hWnd, uMessage, wParam, lParam); 79 | } 80 | 81 | BOOL CTxWnd::create( DWORD dwExStyle, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance ) 82 | { 83 | registerClass(hInstance); 84 | m_hWnd = CreateWindowEx( 85 | dwExStyle, 86 | m_class, 87 | lpWindowName, 88 | dwStyle, 89 | x, y, 90 | nWidth, nHeight, 91 | hWndParent, 92 | hMenu, 93 | hInstance, 94 | this); 95 | if(m_hWnd) 96 | { 97 | return TRUE; 98 | } 99 | return FALSE; 100 | } 101 | 102 | void CTxWnd::destroy() 103 | { 104 | if(m_hWnd) 105 | { 106 | DestroyWindow(m_hWnd); 107 | m_hWnd = NULL; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /test/TxWnd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define TXWND_CLASS L"TORDEX_WINDOW" 4 | 5 | class CTxWnd 6 | { 7 | protected: 8 | HWND m_hWnd; 9 | WCHAR m_class[256]; 10 | public: 11 | CTxWnd(); 12 | virtual ~CTxWnd(void); 13 | 14 | BOOL create(DWORD dwExStyle, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance); 15 | void destroy(); 16 | void setClass(LPCWSTR className) { StringCchCopy(m_class, 256, className); } 17 | operator HWND() 18 | { 19 | return m_hWnd; 20 | } 21 | 22 | protected: 23 | virtual void preRegisterClass(WNDCLASSEX* wcex); 24 | virtual LRESULT OnMessage(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam); 25 | 26 | private: 27 | static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam); 28 | 29 | void registerClass(HINSTANCE hInstance); 30 | }; 31 | 32 | 33 | template class TTxWnd : public CTxWnd 34 | { 35 | public: 36 | typedef private LRESULT (T::*OnMessageFunction)(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam); 37 | 38 | TTxWnd() 39 | { 40 | m_pOnMessageFunction = NULL; 41 | m_pClass = NULL; 42 | } 43 | 44 | void setHandler(T *pClass, OnMessageFunction pFunc) 45 | { 46 | m_pClass = pClass; 47 | m_pOnMessageFunction = pFunc; 48 | } 49 | 50 | protected: 51 | virtual LRESULT OnMessage(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam) 52 | { 53 | if (m_pOnMessageFunction && m_pClass) 54 | { 55 | return (m_pClass->*m_pOnMessageFunction)(hWnd, uMessage, wParam, lParam); 56 | } 57 | return CTxWnd::OnMessage(hWnd, uMessage, wParam, lParam); 58 | } 59 | 60 | private: 61 | T *m_pClass; 62 | OnMessageFunction m_pOnMessageFunction; 63 | }; -------------------------------------------------------------------------------- /test/globals.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define ISOLATION_AWARE_ENABLED 1 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | // include TxDIB project 16 | #include 17 | #pragma comment(lib, "txdib.lib") 18 | 19 | // include CAIRO project 20 | #include 21 | #include 22 | #pragma comment(lib, "cairo.lib") 23 | 24 | // include SIMPLEDIB project 25 | #include 26 | #pragma comment(lib, "simpledib.lib") 27 | 28 | #pragma comment(lib, "shlwapi.lib") 29 | #pragma comment(lib, "Msimg32.lib") 30 | 31 | extern LPWSTR load_text_file( LPCWSTR path, bool is_html, LPCWSTR defEncoding ); 32 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | #include "globals.h" 2 | #include "MainWnd.h" 3 | #include "..\containers\cairo\cairo_font.h" 4 | 5 | CRITICAL_SECTION cairo_font::m_sync; 6 | 7 | int APIENTRY _tWinMain(HINSTANCE hInstance, 8 | HINSTANCE hPrevInstance, 9 | LPTSTR lpCmdLine, 10 | int nCmdShow) 11 | { 12 | CoInitialize(NULL); 13 | InitCommonControls(); 14 | 15 | InitializeCriticalSectionAndSpinCount(&cairo_font::m_sync, 1000); 16 | 17 | { 18 | CMainWnd wnd(hInstance); 19 | wnd.create(); 20 | 21 | MSG msg; 22 | 23 | while (GetMessage(&msg, NULL, 0, 0)) 24 | { 25 | TranslateMessage(&msg); 26 | DispatchMessage(&msg); 27 | } 28 | } 29 | 30 | return 0; 31 | } 32 | 33 | -------------------------------------------------------------------------------- /test/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by tooltipstest.rc 4 | // 5 | #define IDR_CSS1 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /test/tooltipstest.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include "windows.h" 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // Neutral resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU) 19 | #ifdef _WIN32 20 | LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL 21 | #pragma code_page(1251) 22 | #endif //_WIN32 23 | 24 | ///////////////////////////////////////////////////////////////////////////// 25 | // 26 | // CSS 27 | // 28 | 29 | master.css CSS "..\\litehtml\\include\\master.css" 30 | #endif // Neutral resources 31 | ///////////////////////////////////////////////////////////////////////////// 32 | 33 | 34 | ///////////////////////////////////////////////////////////////////////////// 35 | // Russian resources 36 | 37 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS) 38 | #ifdef _WIN32 39 | LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT 40 | #pragma code_page(1251) 41 | #endif //_WIN32 42 | 43 | #ifdef APSTUDIO_INVOKED 44 | ///////////////////////////////////////////////////////////////////////////// 45 | // 46 | // TEXTINCLUDE 47 | // 48 | 49 | 1 TEXTINCLUDE 50 | BEGIN 51 | "resource.h\0" 52 | END 53 | 54 | 2 TEXTINCLUDE 55 | BEGIN 56 | "#include ""afxres.h""\r\n" 57 | "\0" 58 | END 59 | 60 | 3 TEXTINCLUDE 61 | BEGIN 62 | "\r\n" 63 | "\0" 64 | END 65 | 66 | #endif // APSTUDIO_INVOKED 67 | 68 | #endif // Russian resources 69 | ///////////////////////////////////////////////////////////////////////////// 70 | 71 | 72 | 73 | #ifndef APSTUDIO_INVOKED 74 | ///////////////////////////////////////////////////////////////////////////// 75 | // 76 | // Generated from the TEXTINCLUDE 3 resource. 77 | // 78 | 79 | 80 | ///////////////////////////////////////////////////////////////////////////// 81 | #endif // not APSTUDIO_INVOKED 82 | 83 | -------------------------------------------------------------------------------- /test/tooltipstest.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 15 | 18 | 19 | 20 | 21 | 22 | 29 | 32 | 35 | 38 | 41 | 44 | 56 | 59 | 62 | 65 | 74 | 77 | 80 | 83 | 86 | 89 | 92 | 95 | 96 | 103 | 106 | 109 | 112 | 115 | 119 | 131 | 134 | 137 | 140 | 149 | 152 | 155 | 158 | 161 | 164 | 167 | 170 | 171 | 179 | 182 | 185 | 188 | 191 | 194 | 206 | 209 | 212 | 215 | 226 | 229 | 232 | 235 | 238 | 241 | 244 | 247 | 248 | 256 | 259 | 262 | 265 | 268 | 272 | 284 | 287 | 290 | 293 | 304 | 307 | 310 | 313 | 316 | 319 | 322 | 325 | 326 | 327 | 328 | 329 | 330 | 335 | 338 | 339 | 342 | 343 | 346 | 347 | 350 | 351 | 354 | 355 | 358 | 359 | 362 | 363 | 364 | 369 | 372 | 373 | 376 | 377 | 380 | 381 | 384 | 385 | 388 | 389 | 392 | 393 | 396 | 397 | 398 | 403 | 406 | 407 | 408 | 411 | 412 | 413 | 414 | 415 | 416 | -------------------------------------------------------------------------------- /test/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "globals.h" 2 | #include 3 | 4 | LPWSTR load_text_file( LPCWSTR path, bool is_html, LPCWSTR defEncoding ) 5 | { 6 | CoInitialize(NULL); 7 | 8 | LPWSTR strW = NULL; 9 | 10 | HANDLE fl = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 11 | if(fl != INVALID_HANDLE_VALUE) 12 | { 13 | DWORD size = GetFileSize(fl, NULL); 14 | LPSTR str = (LPSTR) malloc(size + 1); 15 | 16 | DWORD cbRead = 0; 17 | ReadFile(fl, str, size, &cbRead, NULL); 18 | str[cbRead] = 0; 19 | CloseHandle(fl); 20 | 21 | int bom = 0; 22 | if(str[0] == '\xEF' && str[1] == '\xBB' && str[2] == '\xBF') 23 | { 24 | bom = 3; 25 | } 26 | 27 | if(is_html) 28 | { 29 | std::wstring encoding; 30 | char* begin = StrStrIA(str, ""); 34 | char* s1 = StrStrIA(begin, "Content-Type"); 35 | if(s1 && s1 < end) 36 | { 37 | s1 = StrStrIA(begin, "charset"); 38 | if(s1) 39 | { 40 | s1 += strlen("charset"); 41 | while(!isalnum(s1[0]) && s1 < end) 42 | { 43 | s1++; 44 | } 45 | while((isalnum(s1[0]) || s1[0] == '-') && s1 < end) 46 | { 47 | encoding += s1[0]; 48 | s1++; 49 | } 50 | } 51 | } 52 | if(encoding.empty()) 53 | { 54 | begin = StrStrIA(begin + strlen("GetCharsetInfo(bstrCharSet, &charset_src); 73 | SysFreeString(bstrCharSet); 74 | 75 | bstrCharSet = SysAllocString(L"utf-8"); 76 | ml->GetCharsetInfo(bstrCharSet, &charset_dst); 77 | SysFreeString(bstrCharSet); 78 | 79 | DWORD dwMode = 0; 80 | UINT szDst = (UINT) strlen(str) * 4; 81 | LPSTR dst = new char[szDst]; 82 | 83 | if(ml->ConvertString(&dwMode, charset_src.uiInternetEncoding, charset_dst.uiInternetEncoding, (LPBYTE) str + bom, NULL, (LPBYTE) dst, &szDst) == S_OK) 84 | { 85 | dst[szDst] = 0; 86 | cbRead = szDst; 87 | delete str; 88 | str = dst; 89 | bom = 0; 90 | } else 91 | { 92 | delete dst; 93 | } 94 | } 95 | } 96 | 97 | if(!strW) 98 | { 99 | strW = new WCHAR[cbRead + 1]; 100 | MultiByteToWideChar(CP_UTF8, 0, str + bom, -1, strW, cbRead + 1); 101 | } 102 | 103 | free(str); 104 | } 105 | 106 | CoUninitialize(); 107 | 108 | return strW; 109 | } 110 | --------------------------------------------------------------------------------