|
12 | 12 |
|
13 | 13 | #include <shellapi.h> |
14 | 14 |
|
| 15 | +/** |
| 16 | + * @brief |
| 17 | + * ReactOS-only feature: |
| 18 | + * Enable or disable support for copying event info text using space padding |
| 19 | + * between header titles and data, when pressing the SHIFT key while clicking |
| 20 | + * on the "Copy" button, instead of using TABs as separators. |
| 21 | + * |
| 22 | + * @see CopyEventEntry(). |
| 23 | + **/ |
| 24 | +#define COPY_EVTTEXT_SPACE_PADDING_MODE |
| 25 | + |
15 | 26 | // FIXME: |
16 | 27 | #define EVENT_MESSAGE_EVENTTEXT_BUFFER (1024*10) |
17 | 28 | extern WCHAR szTitle[]; |
@@ -250,83 +261,291 @@ DisplayEventData( |
250 | 261 | HeapFree(GetProcessHeap(), 0, pTextBuffer); |
251 | 262 | } |
252 | 263 |
|
253 | | -static |
254 | | -HFONT |
255 | | -CreateMonospaceFont(VOID) |
256 | | -{ |
257 | | - LOGFONTW tmpFont = {0}; |
258 | | - HFONT hFont; |
259 | | - HDC hDC; |
| 264 | +#ifdef COPY_EVTTEXT_SPACE_PADDING_MODE |
260 | 265 |
|
261 | | - hDC = GetDC(NULL); |
| 266 | +static inline |
| 267 | +int my_cType3ToWidth(WORD wType, wchar_t ucs) |
| 268 | +{ |
| 269 | + if (wType & C3_HALFWIDTH) |
| 270 | + return 1; |
| 271 | + else if (wType & (C3_FULLWIDTH | C3_KATAKANA | C3_HIRAGANA | C3_IDEOGRAPH)) |
| 272 | + return 2; |
| 273 | + /* |
| 274 | + * HACK for Wide Hangul characters not recognized by GetStringTypeW(CT_CTYPE3) |
| 275 | + * See: |
| 276 | + * https://unicode.org/reports/tr11/ |
| 277 | + * https://www.unicode.org/Public/UCD/latest/ucd/EastAsianWidth.txt |
| 278 | + * https://www.unicode.org/Public/UCD/latest/ucd/extracted/DerivedEastAsianWidth.txt |
| 279 | + * (or the /Public/UNIDATA/ files) |
| 280 | + */ |
| 281 | + else if ((ucs >= 0x1100 && ucs <= 0x115F) || (ucs >= 0x302E && ucs <= 0x302F) || |
| 282 | + (ucs >= 0x3131 && ucs <= 0x318E) || (ucs >= 0x3260 && ucs <= 0x327F) || |
| 283 | + (ucs >= 0xA960 && ucs <= 0xA97C) || (ucs >= 0xAC00 && ucs <= 0xD7A3)) |
| 284 | + return 2; |
| 285 | + else if (wType & (C3_SYMBOL | C3_KASHIDA | C3_LEXICAL | C3_ALPHA)) |
| 286 | + return 1; |
| 287 | + else // if (wType & (C3_NONSPACING | C3_DIACRITIC | C3_VOWELMARK | C3_HIGHSURROGATE | C3_LOWSURROGATE | C3_NOTAPPLICABLE)) |
| 288 | + return 0; |
| 289 | +} |
262 | 290 |
|
263 | | - tmpFont.lfHeight = -MulDiv(8, GetDeviceCaps(hDC, LOGPIXELSY), 72); |
264 | | - tmpFont.lfWeight = FW_NORMAL; |
265 | | - wcscpy(tmpFont.lfFaceName, L"Courier New"); |
| 291 | +int my_wcwidth(wchar_t ucs) |
| 292 | +{ |
| 293 | + WORD wType = 0; |
| 294 | + GetStringTypeW(CT_CTYPE3, &ucs, sizeof(ucs)/sizeof(WCHAR), &wType); |
| 295 | + return my_cType3ToWidth(wType, ucs); |
| 296 | +} |
266 | 297 |
|
267 | | - hFont = CreateFontIndirectW(&tmpFont); |
| 298 | +int my_wcswidth(const wchar_t *pwcs, size_t n) |
| 299 | +{ |
| 300 | + int width = 0; |
| 301 | + PWORD pwType, pwt; |
268 | 302 |
|
269 | | - ReleaseDC(NULL, hDC); |
| 303 | + pwType = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, n * sizeof(WORD)); |
| 304 | + if (!pwType) |
| 305 | + return 0; |
| 306 | + if (!GetStringTypeW(CT_CTYPE3, pwcs, n, pwType)) |
| 307 | + goto Quit; |
270 | 308 |
|
271 | | - return hFont; |
| 309 | + for (pwt = pwType; n-- > 0; ++pwt, ++pwcs) |
| 310 | + { |
| 311 | + width += my_cType3ToWidth(*pwt, *pwcs); |
| 312 | + } |
| 313 | +Quit: |
| 314 | + HeapFree(GetProcessHeap(), 0, pwType); |
| 315 | + return width; |
272 | 316 | } |
273 | 317 |
|
| 318 | +#endif // COPY_EVTTEXT_SPACE_PADDING_MODE |
| 319 | + |
| 320 | +/** |
| 321 | + * @brief |
| 322 | + * Retrieves the already-gathered event information, structure it in |
| 323 | + * text format and copy it into the clipboard for user consumption. |
| 324 | + * |
| 325 | + * The copied event information has the following text format, where |
| 326 | + * each text line ends with CR-LF newlines: |
| 327 | + * ``` |
| 328 | + * Event Type: <event_type>\r\n |
| 329 | + * Event Source: <event_source>\r\n |
| 330 | + * Event Category: <event_cat>\r\n |
| 331 | + * Event ID: <event_id>\r\n |
| 332 | + * Date: <event_date>\r\n |
| 333 | + * Time: <event_time>\r\n |
| 334 | + * User: <event_user>\r\n |
| 335 | + * Computer: <event_computer>\r\n |
| 336 | + * Description:\r\n |
| 337 | + * <event_description>\r\n |
| 338 | + * Data:\r\n |
| 339 | + * <event... |
| 340 | + * ...data... |
| 341 | + * ...if any>\r\n |
| 342 | + * ``` |
| 343 | + * |
| 344 | + * For the single-line fields, the spacing between the header title and |
| 345 | + * information is either a TAB (default), to facilitate data import in |
| 346 | + * spreadsheet programs, or space-padding (when the user presses the |
| 347 | + * SHIFT key while copying the data) to prettify information display. |
| 348 | + * (This latter functionality is supported only if the program is compiled |
| 349 | + * with the @b COPY_EVTTEXT_SPACE_PADDING_MODE define.) |
| 350 | + **/ |
274 | 351 | static |
275 | 352 | VOID |
276 | | -CopyEventEntry(HWND hWnd) |
| 353 | +CopyEventEntry( |
| 354 | + _In_ HWND hWnd) |
277 | 355 | { |
278 | | - WCHAR tmpHeader[512]; |
279 | | - WCHAR szEventType[MAX_PATH]; |
280 | | - WCHAR szSource[MAX_PATH]; |
281 | | - WCHAR szCategory[MAX_PATH]; |
282 | | - WCHAR szEventID[MAX_PATH]; |
283 | | - WCHAR szDate[MAX_PATH]; |
284 | | - WCHAR szTime[MAX_PATH]; |
285 | | - WCHAR szUser[MAX_PATH]; |
286 | | - WCHAR szComputer[MAX_PATH]; |
287 | | - WCHAR evtDesc[EVENT_MESSAGE_EVENTTEXT_BUFFER]; |
288 | | - ULONG size = 0; |
289 | | - LPWSTR output; |
| 356 | +#ifdef COPY_EVTTEXT_SPACE_PADDING_MODE |
| 357 | + static const LONG nTabWidth = 4; |
| 358 | +#endif |
| 359 | + static const WCHAR szCRLF[] = L"\r\n"; |
| 360 | + struct |
| 361 | + { |
| 362 | + WORD uHdrID; // Header string resource ID. |
| 363 | + WORD nDlgItemID; // Dialog control ID containing the corresponding info. |
| 364 | + WORD bSameLine : 1; // Info follows header on same line (TRUE) or not (FALSE). |
| 365 | + WORD bOptional : 1; // Omit if info is empty (TRUE) or keep it (FALSE). |
| 366 | + PCWCH pchHdrText; // Pointer to header string resource. |
| 367 | + SIZE_T cchHdrLen; // Header string length (number of characters). |
| 368 | + SIZE_T cchInfoLen; // Info string length (number of characters). |
| 369 | +#ifdef COPY_EVTTEXT_SPACE_PADDING_MODE |
| 370 | + UINT nHdrWidth; // Display width of the header string. |
| 371 | + UINT nSpacesPad; // Padding after header in number of spaces. |
| 372 | +#endif |
| 373 | + } CopyData[] = |
| 374 | + { |
| 375 | + {IDS_COPY_EVTTYPE, IDC_EVENTTYPESTATIC , TRUE , FALSE, NULL, 0, 0}, |
| 376 | + {IDS_COPY_EVTSRC , IDC_EVENTSOURCESTATIC , TRUE , FALSE, NULL, 0, 0}, |
| 377 | + {IDS_COPY_EVTCAT , IDC_EVENTCATEGORYSTATIC, TRUE , FALSE, NULL, 0, 0}, |
| 378 | + {IDS_COPY_EVTID , IDC_EVENTIDSTATIC , TRUE , FALSE, NULL, 0, 0}, |
| 379 | + {IDS_COPY_EVTDATE, IDC_EVENTDATESTATIC , TRUE , FALSE, NULL, 0, 0}, |
| 380 | + {IDS_COPY_EVTTIME, IDC_EVENTTIMESTATIC , TRUE , FALSE, NULL, 0, 0}, |
| 381 | + {IDS_COPY_EVTUSER, IDC_EVENTUSERSTATIC , TRUE , FALSE, NULL, 0, 0}, |
| 382 | + {IDS_COPY_EVTCOMP, IDC_EVENTCOMPUTERSTATIC, TRUE , FALSE, NULL, 0, 0}, |
| 383 | + {IDS_COPY_EVTTEXT, IDC_EVENTTEXTEDIT , FALSE, FALSE, NULL, 0, 0}, |
| 384 | + {IDS_COPY_EVTDATA, IDC_EVENTDATAEDIT , FALSE, TRUE , NULL, 0, 0}, |
| 385 | + }; |
| 386 | + USHORT i; |
| 387 | +#ifdef COPY_EVTTEXT_SPACE_PADDING_MODE |
| 388 | + BOOL bUsePad; // Use space padding (TRUE) or not (FALSE, default). |
| 389 | + UINT nMaxHdrWidth = 0; |
| 390 | +#endif |
| 391 | + SIZE_T size = 0; |
| 392 | + PWSTR output; |
| 393 | + PWSTR pszDestEnd; |
| 394 | + size_t cchRemaining; |
290 | 395 | HGLOBAL hMem; |
291 | 396 |
|
292 | 397 | /* Try to open the clipboard */ |
293 | 398 | if (!OpenClipboard(hWnd)) |
294 | 399 | return; |
295 | 400 |
|
296 | | - /* Get the formatted text needed to place the content into */ |
297 | | - size += LoadStringW(hInst, IDS_COPY, tmpHeader, ARRAYSIZE(tmpHeader)); |
| 401 | +#ifdef COPY_EVTTEXT_SPACE_PADDING_MODE |
| 402 | + /* Use space padding only if the user presses SHIFT */ |
| 403 | + bUsePad = !!(GetKeyState(VK_SHIFT) & 0x8000); |
| 404 | +#endif |
| 405 | + |
| 406 | + /* |
| 407 | + * Grab all the information and get it ready for the clipboard. |
| 408 | + */ |
| 409 | + |
| 410 | + /* Calculate the necessary string buffer size */ |
| 411 | + for (i = 0; i < _countof(CopyData); ++i) |
| 412 | + { |
| 413 | + /* Retrieve the event info string length (without NUL terminator) */ |
| 414 | + CopyData[i].cchInfoLen = GetWindowTextLengthW(GetDlgItem(hWnd, CopyData[i].nDlgItemID)); |
| 415 | + |
| 416 | + /* If no data is present and is optional, ignore it */ |
| 417 | + if ((CopyData[i].cchInfoLen == 0) && CopyData[i].bOptional) |
| 418 | + continue; |
| 419 | + |
| 420 | + /* Load the header string from resources */ |
| 421 | + CopyData[i].cchHdrLen = LoadStringW(hInst, CopyData[i].uHdrID, (PWSTR)&CopyData[i].pchHdrText, 0); |
| 422 | + size += CopyData[i].cchHdrLen; |
| 423 | + |
| 424 | + if (CopyData[i].bSameLine) |
| 425 | + { |
| 426 | + /* The header and info are on the same line */ |
| 427 | +#ifdef COPY_EVTTEXT_SPACE_PADDING_MODE |
| 428 | + if (bUsePad) |
| 429 | + { |
| 430 | + /* Retrieve the maximum header string displayed |
| 431 | + * width for computing space padding later */ |
| 432 | + CopyData[i].nHdrWidth = my_wcswidth(CopyData[i].pchHdrText, CopyData[i].cchHdrLen); |
| 433 | + nMaxHdrWidth = max(nMaxHdrWidth, CopyData[i].nHdrWidth); |
| 434 | + } |
| 435 | + else |
| 436 | +#endif |
| 437 | + { |
| 438 | + /* Count a TAB separator */ |
| 439 | + size++; |
| 440 | + } |
| 441 | + } |
| 442 | + else |
| 443 | + { |
| 444 | + /* The data is on a separate line, count a newline */ |
| 445 | + size += _countof(szCRLF)-1; |
| 446 | + } |
| 447 | + |
| 448 | + /* Count the event info string and the newline that follows it */ |
| 449 | + size += CopyData[i].cchInfoLen; |
| 450 | + size += _countof(szCRLF)-1; |
| 451 | + } |
| 452 | +#ifdef COPY_EVTTEXT_SPACE_PADDING_MODE |
| 453 | + if (bUsePad) |
| 454 | + { |
| 455 | + /* Round nMaxHdrWidth to the next TAB width, and |
| 456 | + * compute the space padding for each field */ |
| 457 | + UINT nSpaceWidth = 1; // my_wcwidth(L' '); |
| 458 | + nMaxHdrWidth = ((nMaxHdrWidth / nTabWidth) + 1) * nTabWidth; |
| 459 | + for (i = 0; i < _countof(CopyData); ++i) |
| 460 | + { |
| 461 | + /* If no data is present and is optional, ignore it */ |
| 462 | + if ((CopyData[i].cchInfoLen == 0) && CopyData[i].bOptional) |
| 463 | + continue; |
298 | 464 |
|
299 | | - /* Grab all the information and get it ready for the clipboard */ |
300 | | - size += GetDlgItemTextW(hWnd, IDC_EVENTTYPESTATIC, szEventType, ARRAYSIZE(szEventType)); |
301 | | - size += GetDlgItemTextW(hWnd, IDC_EVENTSOURCESTATIC, szSource, ARRAYSIZE(szSource)); |
302 | | - size += GetDlgItemTextW(hWnd, IDC_EVENTCATEGORYSTATIC, szCategory, ARRAYSIZE(szCategory)); |
303 | | - size += GetDlgItemTextW(hWnd, IDC_EVENTIDSTATIC, szEventID, ARRAYSIZE(szEventID)); |
304 | | - size += GetDlgItemTextW(hWnd, IDC_EVENTDATESTATIC, szDate, ARRAYSIZE(szDate)); |
305 | | - size += GetDlgItemTextW(hWnd, IDC_EVENTTIMESTATIC, szTime, ARRAYSIZE(szTime)); |
306 | | - size += GetDlgItemTextW(hWnd, IDC_EVENTUSERSTATIC, szUser, ARRAYSIZE(szUser)); |
307 | | - size += GetDlgItemTextW(hWnd, IDC_EVENTCOMPUTERSTATIC, szComputer, ARRAYSIZE(szComputer)); |
308 | | - size += GetDlgItemTextW(hWnd, IDC_EVENTTEXTEDIT, evtDesc, ARRAYSIZE(evtDesc)); |
| 465 | + /* If the data is on a separate line, ignore padding */ |
| 466 | + if (!CopyData[i].bSameLine) |
| 467 | + continue; |
309 | 468 |
|
310 | | - size++; /* Null-termination */ |
311 | | - size *= sizeof(WCHAR); |
| 469 | + /* Compute the padding */ |
| 470 | + CopyData[i].nSpacesPad = (nMaxHdrWidth - CopyData[i].nHdrWidth) / nSpaceWidth; |
| 471 | + size += CopyData[i].nSpacesPad; |
| 472 | + } |
| 473 | + } |
| 474 | +#endif // COPY_EVTTEXT_SPACE_PADDING_MODE |
| 475 | + /* Add NUL-termination */ |
| 476 | + size++; |
312 | 477 |
|
313 | 478 | /* |
314 | | - * Consolidate the information into one big piece and |
315 | | - * sort out the memory needed to write to the clipboard. |
| 479 | + * Consolidate the information into a single buffer to copy in the clipboard. |
316 | 480 | */ |
317 | | - hMem = GlobalAlloc(GMEM_MOVEABLE, size); |
318 | | - if (hMem == NULL) goto Quit; |
| 481 | + hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, size * sizeof(WCHAR)); |
| 482 | + if (!hMem) |
| 483 | + goto Quit; |
319 | 484 |
|
320 | 485 | output = GlobalLock(hMem); |
321 | | - if (output == NULL) |
| 486 | + if (!output) |
322 | 487 | { |
323 | 488 | GlobalFree(hMem); |
324 | 489 | goto Quit; |
325 | 490 | } |
326 | 491 |
|
327 | | - StringCbPrintfW(output, size, |
328 | | - tmpHeader, szEventType, szSource, szCategory, szEventID, |
329 | | - szDate, szTime, szUser, szComputer, evtDesc); |
| 492 | + /* Build the string */ |
| 493 | + pszDestEnd = output; |
| 494 | + cchRemaining = size; |
| 495 | + for (i = 0; i < _countof(CopyData); ++i) |
| 496 | + { |
| 497 | + SIZE_T sizeDataStr; |
| 498 | + |
| 499 | + /* If no data is present and is optional, ignore it */ |
| 500 | + if ((CopyData[i].cchInfoLen == 0) && CopyData[i].bOptional) |
| 501 | + continue; |
| 502 | + |
| 503 | + /* Copy the header string */ |
| 504 | + StringCchCopyNExW(pszDestEnd, cchRemaining, |
| 505 | + CopyData[i].pchHdrText, CopyData[i].cchHdrLen, |
| 506 | + &pszDestEnd, &cchRemaining, 0); |
| 507 | + |
| 508 | + if (CopyData[i].bSameLine) |
| 509 | + { |
| 510 | + /* The header and info are on the same line, add |
| 511 | + * either the space padding or the TAB separator */ |
| 512 | +#ifdef COPY_EVTTEXT_SPACE_PADDING_MODE |
| 513 | + if (bUsePad) |
| 514 | + { |
| 515 | + UINT j = CopyData[i].nSpacesPad; |
| 516 | + while (j--) |
| 517 | + { |
| 518 | + *pszDestEnd++ = L' '; |
| 519 | + cchRemaining--; |
| 520 | + } |
| 521 | + } |
| 522 | + else |
| 523 | +#endif |
| 524 | + { |
| 525 | + *pszDestEnd++ = L'\t'; |
| 526 | + cchRemaining--; |
| 527 | + } |
| 528 | + } |
| 529 | + else |
| 530 | + { |
| 531 | + /* The data is on a separate line, add a newline */ |
| 532 | + StringCchCopyExW(pszDestEnd, cchRemaining, szCRLF, |
| 533 | + &pszDestEnd, &cchRemaining, 0); |
| 534 | + } |
| 535 | + |
| 536 | + /* Copy the event info */ |
| 537 | + sizeDataStr = min(cchRemaining, CopyData[i].cchInfoLen + 1); |
| 538 | + sizeDataStr = GetDlgItemTextW(hWnd, CopyData[i].nDlgItemID, pszDestEnd, sizeDataStr); |
| 539 | + pszDestEnd += sizeDataStr; |
| 540 | + cchRemaining -= sizeDataStr; |
| 541 | + |
| 542 | + /* A newline follows the data */ |
| 543 | + StringCchCopyExW(pszDestEnd, cchRemaining, szCRLF, |
| 544 | + &pszDestEnd, &cchRemaining, 0); |
| 545 | + } |
| 546 | + /* NUL-terminate the buffer */ |
| 547 | + *pszDestEnd++ = UNICODE_NULL; |
| 548 | + cchRemaining--; |
330 | 549 |
|
331 | 550 | GlobalUnlock(hMem); |
332 | 551 |
|
@@ -777,6 +996,27 @@ ClearContents( |
777 | 996 | SetDlgItemTextW(hDlg, IDC_EVENTDATAEDIT, L""); |
778 | 997 | } |
779 | 998 |
|
| 999 | +static |
| 1000 | +HFONT |
| 1001 | +CreateMonospaceFont(VOID) |
| 1002 | +{ |
| 1003 | + LOGFONTW tmpFont = {0}; |
| 1004 | + HFONT hFont; |
| 1005 | + HDC hDC; |
| 1006 | + |
| 1007 | + hDC = GetDC(NULL); |
| 1008 | + |
| 1009 | + tmpFont.lfHeight = -MulDiv(8, GetDeviceCaps(hDC, LOGPIXELSY), 72); |
| 1010 | + tmpFont.lfWeight = FW_NORMAL; |
| 1011 | + wcscpy(tmpFont.lfFaceName, L"Courier New"); |
| 1012 | + |
| 1013 | + hFont = CreateFontIndirectW(&tmpFont); |
| 1014 | + |
| 1015 | + ReleaseDC(NULL, hDC); |
| 1016 | + |
| 1017 | + return hFont; |
| 1018 | +} |
| 1019 | + |
780 | 1020 | static |
781 | 1021 | VOID |
782 | 1022 | InitDetailsDlgCtrl(HWND hDlg, PDETAILDATA pData) |
|
0 commit comments