mirror of
https://github.com/jmarshall23/msword.git
synced 2026-08-11 23:50:53 +02:00
Fixed memory alignment bugs.
Fixed numerous display issues. Fixed numerous windows messaging issues.
This commit is contained in:
+88
-72
@@ -34,62 +34,6 @@ foreach(config Debug Release RelWithDebInfo MinSizeRel)
|
||||
set(CMAKE_COMPILE_PDB_OUTPUT_DIRECTORY_${config_upper} "${MSWORD_BUILD_ROOT}/pdb/${config}")
|
||||
endforeach()
|
||||
|
||||
set(MSWORD_BOOTSTRAP_SOURCES
|
||||
port/winword_x64.cpp
|
||||
port/legacy_asm_port.cpp
|
||||
port/legacy_asm_port.h
|
||||
port/winword_x64.rc
|
||||
port/winword.manifest
|
||||
)
|
||||
|
||||
# This is a migration aid only. It is deliberately excluded from the default
|
||||
# build and may never claim the final WORD1.exe product name: that name is
|
||||
# reserved for the executable linked from the original Opus source graph.
|
||||
add_executable(WINWORD_BOOTSTRAP WIN32 EXCLUDE_FROM_ALL ${MSWORD_BOOTSTRAP_SOURCES})
|
||||
target_compile_features(WINWORD_BOOTSTRAP PRIVATE cxx_std_20)
|
||||
target_compile_definitions(WINWORD_BOOTSTRAP PRIVATE UNICODE _UNICODE WIN32_LEAN_AND_MEAN NOMINMAX)
|
||||
target_include_directories(WINWORD_BOOTSTRAP PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/port")
|
||||
target_link_libraries(WINWORD_BOOTSTRAP PRIVATE
|
||||
comctl32
|
||||
comdlg32
|
||||
gdi32
|
||||
ole32
|
||||
shell32
|
||||
user32
|
||||
)
|
||||
|
||||
if(MSVC)
|
||||
target_compile_options(WINWORD_BOOTSTRAP PRIVATE /W4 /permissive- /utf-8 /EHsc /guard:cf)
|
||||
# The application manifest is compiled as resource 1; disable MSVC's
|
||||
# second auto-generated manifest to avoid a duplicate RT_MANIFEST entry.
|
||||
target_link_options(WINWORD_BOOTSTRAP PRIVATE /DYNAMICBASE /HIGHENTROPYVA /NXCOMPAT /MANIFEST:NO)
|
||||
set_property(TARGET WINWORD_BOOTSTRAP PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
|
||||
endif()
|
||||
|
||||
set_target_properties(WINWORD_BOOTSTRAP PROPERTIES
|
||||
OUTPUT_NAME "WINWORD_BOOTSTRAP"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${MSWORD_BUILD_ROOT}/bootstrap"
|
||||
VS_GLOBAL_ROOTNAMESPACE "MicrosoftWordX64Port"
|
||||
# The target uses only Windows system libraries. Disable Visual Studio's
|
||||
# global vcpkg app-local hook so it does not invoke an unrelated pwsh.exe.
|
||||
VS_GLOBAL_VcpkgApplocalDeps "false"
|
||||
)
|
||||
foreach(config Debug Release RelWithDebInfo MinSizeRel)
|
||||
string(TOUPPER "${config}" config_upper)
|
||||
set_target_properties(WINWORD_BOOTSTRAP PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY_${config_upper} "${MSWORD_BUILD_ROOT}/bootstrap/${config}"
|
||||
)
|
||||
endforeach()
|
||||
|
||||
# The original assembly remains in the repository for behavioral reference,
|
||||
# but no .asm file is permitted to enter the native target.
|
||||
get_target_property(winword_sources WINWORD_BOOTSTRAP SOURCES)
|
||||
foreach(source IN LISTS winword_sources)
|
||||
if(source MATCHES "\\.[aA][sS][mM]$")
|
||||
message(FATAL_ERROR "Legacy assembly leaked into the x64 target: ${source}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
file(GLOB_RECURSE LEGACY_ASSEMBLY CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/*.asm")
|
||||
set(LEGACY_ASSEMBLY_RELATIVE)
|
||||
foreach(assembly_file IN LISTS LEGACY_ASSEMBLY)
|
||||
@@ -99,23 +43,11 @@ foreach(assembly_file IN LISTS LEGACY_ASSEMBLY)
|
||||
endforeach()
|
||||
list(SORT LEGACY_ASSEMBLY_RELATIVE)
|
||||
|
||||
# Extract the explicit source paths from the C++ disposition table and compare
|
||||
# names, not merely counts. Removing one module and adding another cannot pass.
|
||||
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/port/legacy_asm_port.cpp" ASM_PORT_SOURCE)
|
||||
string(REGEX MATCHALL "AssemblyPortEntry\\{\"[^\"]+\\.asm\"" ASM_PORT_ROWS "${ASM_PORT_SOURCE}")
|
||||
set(ASM_PORT_PATHS)
|
||||
foreach(port_row IN LISTS ASM_PORT_ROWS)
|
||||
string(REGEX REPLACE "AssemblyPortEntry\\{\"([^\"]+)\"" "\\1" port_path "${port_row}")
|
||||
list(APPEND ASM_PORT_PATHS "${port_path}")
|
||||
endforeach()
|
||||
list(SORT ASM_PORT_PATHS)
|
||||
|
||||
list(LENGTH LEGACY_ASSEMBLY_RELATIVE LEGACY_ASSEMBLY_COUNT)
|
||||
if(NOT LEGACY_ASSEMBLY_COUNT EQUAL 59 OR
|
||||
NOT "${LEGACY_ASSEMBLY_RELATIVE}" STREQUAL "${ASM_PORT_PATHS}")
|
||||
if(NOT LEGACY_ASSEMBLY_COUNT EQUAL 59)
|
||||
message(FATAL_ERROR
|
||||
"The assembly-port manifest does not exactly match the legacy source tree "
|
||||
"(${LEGACY_ASSEMBLY_COUNT} modules found). Update port/legacy_asm_port.cpp."
|
||||
"The original assembly reference tree changed "
|
||||
"(${LEGACY_ASSEMBLY_COUNT} modules found; expected 59). Review the x64 translations."
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -185,6 +117,35 @@ foreach(config Debug Release RelWithDebInfo MinSizeRel)
|
||||
)
|
||||
endforeach()
|
||||
|
||||
add_executable(opus_cabi_tool EXCLUDE_FROM_ALL
|
||||
port/tools/opus_cabi_tool.cpp
|
||||
)
|
||||
target_compile_features(opus_cabi_tool PRIVATE cxx_std_20)
|
||||
target_compile_definitions(opus_cabi_tool PRIVATE
|
||||
WIN WIN23 CRLF NONATIVE OPUS_X64 NOMINMAX
|
||||
)
|
||||
target_include_directories(opus_cabi_tool PRIVATE
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/generated/original"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/port/original"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Opus/lib"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Opus"
|
||||
)
|
||||
if(MSVC)
|
||||
target_compile_options(opus_cabi_tool PRIVATE
|
||||
/J /W3 /wd4005 /permissive-)
|
||||
endif()
|
||||
set_target_properties(opus_cabi_tool PROPERTIES
|
||||
FOLDER "Original Opus x64/build tools"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${MSWORD_BUILD_ROOT}/tools"
|
||||
VS_GLOBAL_VcpkgApplocalDeps "false"
|
||||
)
|
||||
foreach(config Debug Release RelWithDebInfo MinSizeRel)
|
||||
string(TOUPPER "${config}" config_upper)
|
||||
set_target_properties(opus_cabi_tool PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY_${config_upper} "${MSWORD_BUILD_ROOT}/tools/${config}"
|
||||
)
|
||||
endforeach()
|
||||
|
||||
# Preserve Microsoft's original BITAPP resource conversion step. These
|
||||
# native x64 tool builds consume the checked-in Win16 .cur files and emit the
|
||||
# same byte initializers that SCREEN2.C historically included.
|
||||
@@ -353,6 +314,18 @@ set(OPUS_GENERATED_COMMAND_FILES
|
||||
"${OPUS_GENERATED_COMMAND_DIR}/opuscmd.asm"
|
||||
"${OPUS_GENERATED_COMMAND_DIR}/opuscmd_native.inc"
|
||||
)
|
||||
file(GLOB OPUS_RECONSTRUCTED_DIALOG_HEADERS CONFIGURE_DEPENDS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/port/original/*.hs")
|
||||
set(OPUS_GENERATED_CABI_DIR "${OPUS_GENERATED_COMMAND_DIR}/cabi")
|
||||
set(OPUS_GENERATED_CABI_OPEN "${OPUS_GENERATED_CABI_DIR}/open.hs")
|
||||
add_custom_command(
|
||||
OUTPUT "${OPUS_GENERATED_CABI_OPEN}"
|
||||
COMMAND "${CMAKE_COMMAND}" -E make_directory "${OPUS_GENERATED_CABI_DIR}"
|
||||
COMMAND "$<TARGET_FILE:opus_cabi_tool>" "${OPUS_GENERATED_CABI_DIR}"
|
||||
DEPENDS opus_cabi_tool ${OPUS_RECONSTRUCTED_DIALOG_HEADERS}
|
||||
COMMENT "Computing native SDM CAB initializers"
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_command(
|
||||
OUTPUT ${OPUS_GENERATED_COMMAND_FILES}
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
|
||||
@@ -368,7 +341,8 @@ add_custom_command(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Opus/resource/ibcm.rel"
|
||||
"${OPUS_GENERATED_COMMAND_DIR}/IBCM.H"
|
||||
COMMAND "$<TARGET_FILE:opus_mkcmd_tool>"
|
||||
-T@13 -s../../../src/Opus/dlg/ opuscmd keys menus
|
||||
-T@13 -s../../../src/Opus/dlg/ -bcabi/
|
||||
opuscmd keys menus
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
-DINPUT=${OPUS_GENERATED_COMMAND_DIR}/MENUHELP.TXT
|
||||
-DOUTPUT=${OPUS_GENERATED_COMMAND_DIR}/menuhelp.h
|
||||
@@ -380,6 +354,8 @@ add_custom_command(
|
||||
Opus/resource/menus.cmd
|
||||
Opus/resource/ibcm.rel
|
||||
Opus/dlg/elx.txt
|
||||
"${OPUS_GENERATED_CABI_OPEN}"
|
||||
${OPUS_RECONSTRUCTED_DIALOG_HEADERS}
|
||||
cmake/GenerateMenuHelpHeader.cmake
|
||||
WORKING_DIRECTORY "${OPUS_GENERATED_COMMAND_DIR}"
|
||||
COMMENT "Regenerating original Opus command tables with MKCMD"
|
||||
@@ -893,6 +869,7 @@ endif()
|
||||
set_target_properties(WORD1 PROPERTIES
|
||||
FOLDER "Original Opus x64/product"
|
||||
OUTPUT_NAME "${MSWORD_PRODUCT_OUTPUT_NAME}"
|
||||
VS_DEBUGGER_WORKING_DIRECTORY "${MSWORD_BIN_ROOT}"
|
||||
VS_GLOBAL_VcpkgApplocalDeps "false"
|
||||
)
|
||||
|
||||
@@ -965,10 +942,49 @@ foreach(config Debug Release RelWithDebInfo MinSizeRel)
|
||||
)
|
||||
endforeach()
|
||||
|
||||
add_executable(opus_word1_ui_test
|
||||
port/original/opus_word1_ui_test.cpp
|
||||
)
|
||||
add_dependencies(opus_word1_ui_test WORD1)
|
||||
target_compile_features(opus_word1_ui_test PRIVATE cxx_std_20)
|
||||
target_compile_definitions(opus_word1_ui_test PRIVATE NOMINMAX UNICODE _UNICODE)
|
||||
target_link_libraries(opus_word1_ui_test PRIVATE user32 gdi32)
|
||||
if(MSVC)
|
||||
target_compile_options(opus_word1_ui_test PRIVATE /W4 /utf-8)
|
||||
endif()
|
||||
set_target_properties(opus_word1_ui_test PROPERTIES
|
||||
FOLDER "Original Opus x64/tests"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${MSWORD_BUILD_ROOT}/tests"
|
||||
VS_GLOBAL_VcpkgApplocalDeps "false"
|
||||
)
|
||||
foreach(config Debug Release RelWithDebInfo MinSizeRel)
|
||||
string(TOUPPER "${config}" config_upper)
|
||||
set_target_properties(opus_word1_ui_test PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY_${config_upper} "${MSWORD_BUILD_ROOT}/tests/${config}"
|
||||
)
|
||||
endforeach()
|
||||
|
||||
enable_testing()
|
||||
add_test(NAME opus_original_strtbl_test COMMAND $<TARGET_FILE:opus_original_strtbl_test>)
|
||||
add_test(NAME opus_x64_runtime_test COMMAND $<TARGET_FILE:opus_x64_runtime_test>)
|
||||
add_test(NAME opus_original_sttb_test COMMAND $<TARGET_FILE:opus_original_sttb_test>)
|
||||
add_test(NAME opus_original_plc_test COMMAND $<TARGET_FILE:opus_original_plc_test>)
|
||||
add_test(NAME opus_sdm_cab_test COMMAND $<TARGET_FILE:opus_sdm_cab_test>)
|
||||
add_test(NAME opus_original_command_test COMMAND $<TARGET_FILE:opus_original_command_test>)
|
||||
add_test(NAME word1_port_smoke_test COMMAND $<TARGET_FILE:WORD1> --self-test)
|
||||
add_test(NAME opus_word1_ui_test
|
||||
COMMAND $<TARGET_FILE:opus_word1_ui_test> $<TARGET_FILE:WORD1>
|
||||
)
|
||||
set_tests_properties(opus_word1_ui_test PROPERTIES TIMEOUT 20)
|
||||
add_test(NAME opus_word1_typing_test
|
||||
COMMAND $<TARGET_FILE:opus_word1_ui_test> $<TARGET_FILE:WORD1> --typing
|
||||
)
|
||||
set_tests_properties(opus_word1_typing_test PROPERTIES TIMEOUT 20)
|
||||
add_test(NAME opus_word1_interaction_test
|
||||
COMMAND $<TARGET_FILE:opus_word1_ui_test> $<TARGET_FILE:WORD1> --interaction
|
||||
)
|
||||
set_tests_properties(opus_word1_interaction_test PROPERTIES TIMEOUT 25)
|
||||
add_test(NAME opus_word1_selection_test
|
||||
COMMAND $<TARGET_FILE:opus_word1_ui_test> $<TARGET_FILE:WORD1> --selection
|
||||
)
|
||||
set_tests_properties(opus_word1_selection_test PROPERTIES TIMEOUT 20)
|
||||
|
||||
@@ -374,7 +374,7 @@ DumpGrpchr()
|
||||
return;
|
||||
}
|
||||
CommSz( SzShared( "\r\n" ) );
|
||||
pchr = ((char *)pchr) + pchr->chrm;
|
||||
pchr = ((char *)pchr) + CbFromChrm(pchr->chrm);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1719,7 +1719,7 @@ CMB *pcmb;
|
||||
CommSz("\n\r");
|
||||
pch = rgch;
|
||||
}
|
||||
(char *)pchr += chrm;
|
||||
(char *)pchr += CbFromChrm(chrm);
|
||||
}
|
||||
return cmdOK;
|
||||
}
|
||||
|
||||
@@ -1117,7 +1117,7 @@ NATIVE CkVfli() /* makes CkVfli visible to hand NATIVE */
|
||||
iAssert = 0;
|
||||
/* = because chrmVanish may preceed ichMac */
|
||||
Assert (vfli.fSplatBreak || pchr->ich <= vfli.ichMac);
|
||||
(char *)pchr += pchr->chrm;
|
||||
(char *)pchr += CbFromChrm(pchr->chrm);
|
||||
}
|
||||
iAssert = 1;
|
||||
Assert (vfli.doc >= 0 && vfli.doc < docMac);
|
||||
|
||||
@@ -155,7 +155,11 @@ CMB * pcmb;
|
||||
|
||||
SetBytes(&dli, 0, sizeof (DLI));
|
||||
dli.fdlg = fdlgModal | fdlgFedt;
|
||||
#ifdef OPUS_X64
|
||||
dli.wRef = (UINT_PTR)pcmb;
|
||||
#else
|
||||
dli.wRef = (WORD) pcmb;
|
||||
#endif
|
||||
|
||||
/* this returns from this func if there is not enough stack for AllocA below */
|
||||
ReturnOnNoStack(cb, tmcError, fTrue);
|
||||
@@ -771,7 +775,13 @@ SuccessCCP:
|
||||
return(1L);
|
||||
|
||||
case EM_SETSEL:
|
||||
#ifdef OPUS_X64
|
||||
/* Win32 and later pass the start in wParam and the end in lParam.
|
||||
* The Win16 edit message packed both 16-bit values into lParam. */
|
||||
FedtSetSel(hfedt, (int) wParam, (int) lParam);
|
||||
#else
|
||||
FedtSetSel(hfedt, LOWORD(lParam), HIWORD(lParam));
|
||||
#endif
|
||||
FedtReleaseDC(hfedt);
|
||||
break;
|
||||
|
||||
|
||||
+4
-4
@@ -956,7 +956,7 @@ int dxpToXw, ywLine;
|
||||
ptPen.xp = xpNew;
|
||||
ptPen.yp = ypNew;
|
||||
fNoSkip = vfli.fPrint;
|
||||
(char *) pchr += chrmFormula;
|
||||
(char *) pchr += cbCHRF;
|
||||
}
|
||||
else if (chrm == chrmFormatGroup)
|
||||
{
|
||||
@@ -971,7 +971,7 @@ int dxpToXw, ywLine;
|
||||
}
|
||||
/* note that rcOpaque and rcErase will no
|
||||
longer change (rcErase not used) */
|
||||
(char *) pchr += chrmFormatGroup;
|
||||
(char *) pchr += cbCHRFG;
|
||||
}
|
||||
else /*if (chrm == chrmEnd)*/
|
||||
|
||||
@@ -1718,8 +1718,8 @@ struct RC *prcwClip;
|
||||
|
||||
else if (chrm == chrmEnd)
|
||||
break;
|
||||
bchrCur += chrm;
|
||||
(char *)pchr += chrm;
|
||||
bchrCur += CbFromChrm(chrm);
|
||||
(char *)pchr += CbFromChrm(chrm);
|
||||
}
|
||||
|
||||
/* draw CRJ symbol if needed */
|
||||
|
||||
+15
-14
@@ -141,8 +141,9 @@ int *pbchr;
|
||||
{
|
||||
struct CHR *pchr;
|
||||
int bchr = vbchrMac;
|
||||
if ((vbchrMac += chrm) > vbchrMax)
|
||||
if (!FExpandGrpchr(chrm))
|
||||
int cb = CbFromChrm(chrm);
|
||||
if ((vbchrMac += cb) > vbchrMax)
|
||||
if (!FExpandGrpchr(cb))
|
||||
return 0;
|
||||
pchr = &((**vhgrpchr)[*pbchr = bchr]);
|
||||
pchr->ich = ich;
|
||||
@@ -887,12 +888,12 @@ fraction line is dypFract above the base line (func of hps of *f command)
|
||||
if (vprsu.prid == pridLaser && vfli.fPrint)
|
||||
dypChSym = dypChSym * 9 / 8;
|
||||
#endif /* MAC */
|
||||
PchpSetChrDyp(pfma1->bchr - chrmChp,
|
||||
PchpSetChrDyp(pfma1->bchr - cbCHR,
|
||||
dypChSym,
|
||||
chRootExt, fTrue)->fSpec = fTrue;
|
||||
|
||||
FFormatChSpecSymbol(
|
||||
PchpSetChrDyp((bchr = pfma1->bchr - chrmChp - chrmFormula - chrmChp),
|
||||
PchpSetChrDyp((bchr = pfma1->bchr - cbCHR - cbCHRF - cbCHR),
|
||||
dypChSym,
|
||||
chRoot, fTrue),
|
||||
chRoot, &dxpChSym, &dxtChSym,
|
||||
@@ -915,7 +916,7 @@ fraction line is dypFract above the base line (func of hps of *f command)
|
||||
dytAscent = dytAscentCh1 - dyt2;
|
||||
|
||||
/* update CHRF to goto start of root symbol */
|
||||
SetChrf(bchr - chrmFormula, dxp3 - dxpChSym,
|
||||
SetChrf(bchr - cbCHRF, dxp3 - dxpChSym,
|
||||
dyp2,
|
||||
fFalse);
|
||||
#ifdef MAC /* win does not have prid */
|
||||
@@ -924,7 +925,7 @@ same place as the root symbol and also ends up there.
|
||||
On other printers, extension is drawn with width dxp2 after the root symbol */
|
||||
if (vprsu.prid == pridLaser && vfli.fPrint)
|
||||
{
|
||||
SetChrf(bchr + chrmChp, - dxpChSym,
|
||||
SetChrf(bchr + cbCHR, - dxpChSym,
|
||||
0, fFalse);
|
||||
dxpLarger = dxpChSym; /* move right from ext to operand */
|
||||
}
|
||||
@@ -1033,7 +1034,7 @@ On other printers, extension is drawn with width dxp2 after the root symbol */
|
||||
Assert(pchrf->chrm == chrmFormula);
|
||||
|
||||
/* for fixing up bounding symbols */
|
||||
bchr -= chrmChp + chrmFormula;
|
||||
bchr -= cbCHR + cbCHRF;
|
||||
ich = pfma1->ich - 1;
|
||||
|
||||
ch1 = pfma1->chLeft;
|
||||
@@ -1049,11 +1050,11 @@ On other printers, extension is drawn with width dxp2 after the root symbol */
|
||||
{
|
||||
if (ch2 != 0)
|
||||
{
|
||||
bchr -= chrmChp + chrmFormula;
|
||||
bchr -= cbCHR + cbCHRF;
|
||||
ich--;
|
||||
}
|
||||
FFormatChSpecSymbol(
|
||||
PchpSetChrDyp(bchr + chrmFormula, dyp1, ch1, fTrue),
|
||||
PchpSetChrDyp(bchr + cbCHRF, dyp1, ch1, fTrue),
|
||||
ch1, &dxp2, &dxt2,
|
||||
&dypAscentCh1, &dypDescentCh1, &dytAscentCh1, &dytDescentCh1);
|
||||
SetChrf(bchr, 0, dyp2 =
|
||||
@@ -1065,7 +1066,7 @@ On other printers, extension is drawn with width dxp2 after the root symbol */
|
||||
(dytDescentCh1 + dytAscentCh1 - dyt1) / 2;
|
||||
|
||||
vfli.rgdxp[ich++] = dxp2;
|
||||
bchr += chrmChp + chrmFormula;
|
||||
bchr += cbCHR + cbCHRF;
|
||||
}
|
||||
|
||||
/* right symbol */
|
||||
@@ -1078,7 +1079,7 @@ On other printers, extension is drawn with width dxp2 after the root symbol */
|
||||
else
|
||||
{
|
||||
FFormatChSpecSymbol(
|
||||
PchpSetChrDyp(bchr + chrmFormula, dyp1, ch2, fTrue),
|
||||
PchpSetChrDyp(bchr + cbCHRF, dyp1, ch2, fTrue),
|
||||
ch2, &dxp3, &dxt3,
|
||||
&dypAscentCh2, &dypDescentCh2, &dytAscentCh2, &dytDescentCh2);
|
||||
SetChrf(bchr, dxp1, -dyp2 + (dyp3 =
|
||||
@@ -1142,7 +1143,7 @@ On other printers, extension is drawn with width dxp2 after the root symbol */
|
||||
dyt3 = pfmaEnd->dytAscent + pfmaEnd->dytDescent;
|
||||
|
||||
/* calculate size of symbol */
|
||||
bchr = pfma1->bchr - chrmChp;
|
||||
bchr = pfma1->bchr - cbCHR;
|
||||
ch1 = pfma1->chLeft;
|
||||
|
||||
FFormatChSpecSymbol(
|
||||
@@ -1211,7 +1212,7 @@ On other printers, extension is drawn with width dxp2 after the root symbol */
|
||||
dxtLarger = max(dxt1, dxt2);
|
||||
|
||||
/* goto start of symbol */
|
||||
SetChrf(bchr - chrmFormula, 0,
|
||||
SetChrf(bchr - cbCHRF, 0,
|
||||
dyp3,
|
||||
fFalse);
|
||||
|
||||
@@ -1255,7 +1256,7 @@ On other printers, extension is drawn with width dxp2 after the root symbol */
|
||||
dxt1 / 2 )) - dxtChSym / 2;
|
||||
|
||||
/* goto start of symbol */
|
||||
SetChrf(bchr - chrmFormula,
|
||||
SetChrf(bchr - cbCHRF,
|
||||
dxpLarger,
|
||||
dyp3,
|
||||
fFalse);
|
||||
|
||||
@@ -108,8 +108,13 @@ extern int vwWinVersion;
|
||||
extern CHAR szNone[];
|
||||
extern struct PREF vpref;
|
||||
extern long vcmsecHelp;
|
||||
#ifdef OPUS_X64
|
||||
extern WPARAM vwParamHelp;
|
||||
extern LPARAM vlParamHelp;
|
||||
#else
|
||||
extern WORD vwParamHelp;
|
||||
extern LONG vlParamHelp;
|
||||
#endif
|
||||
extern int wwCur;
|
||||
extern int mwCur;
|
||||
extern int vlm;
|
||||
@@ -137,12 +142,21 @@ extern struct FB vfbReplace;
|
||||
|
||||
extern int far pascal initwin_q();
|
||||
|
||||
#ifdef OPUS_X64
|
||||
int ImnuFromHMenu(HMENU, UINT_PTR);
|
||||
#endif
|
||||
|
||||
/* G L O B A L S */
|
||||
|
||||
long vcmsecHelp = 0L; /* Time (in msec) to invalidate */
|
||||
/* double-clicking for help mode. */
|
||||
#ifdef OPUS_X64
|
||||
WPARAM vwParamHelp; /* Saved native WM_MENUSELECT parameters */
|
||||
LPARAM vlParamHelp = 0;
|
||||
#else
|
||||
WORD vwParamHelp; /* Saved for cxt computation */
|
||||
LONG vlParamHelp = 0; /* Saved for cxt computation */
|
||||
#endif
|
||||
HWND vhwndCBT = NULL; /* CBT application window */
|
||||
BOOL vfHelp = fFalse; /* whether we're in Help mode */
|
||||
int vcxtHelp = cxtNil; /* current context */
|
||||
@@ -581,17 +595,30 @@ HWND hwnd;
|
||||
one of the drop-downs). It is cxtDocMenuSelect if we have hilighted
|
||||
something in the Doc system menu.
|
||||
*/
|
||||
#ifdef OPUS_X64
|
||||
int CxtMenuSelect(WPARAM wParam, LPARAM lParam, int cxt)
|
||||
#else
|
||||
int CxtMenuSelect(wParam, lParam, cxt)
|
||||
WORD wParam;
|
||||
LONG lParam;
|
||||
int cxt;
|
||||
#endif
|
||||
{
|
||||
#ifdef OPUS_X64
|
||||
int mf = HIWORD(wParam);
|
||||
WORD item = LOWORD(wParam);
|
||||
#else
|
||||
int mf = LOWORD(lParam);
|
||||
#endif
|
||||
|
||||
Assert(cxt == cxtAppMenuSelect || cxt == cxtDocMenuSelect);
|
||||
|
||||
/* this case should have been taken care of in SaveMenuHelpContext */
|
||||
#ifdef OPUS_X64
|
||||
Assert(lParam != 0);
|
||||
#else
|
||||
Assert (HIWORD(lParam) != 0 && wParam != 0);
|
||||
#endif
|
||||
|
||||
/* Application or Document system menu */
|
||||
if (mf & MF_SYSMENU)
|
||||
@@ -605,8 +632,13 @@ int cxt;
|
||||
the SC_ code of the menu item */
|
||||
{
|
||||
return ((cxt == cxtAppMenuSelect) ?
|
||||
#ifdef OPUS_X64
|
||||
CxtAppSysMenu(item) :
|
||||
CxtDocSysMenu(item));
|
||||
#else
|
||||
CxtAppSysMenu(wParam) :
|
||||
CxtDocSysMenu(wParam));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -614,7 +646,13 @@ int cxt;
|
||||
/* HIWORD is the main menu handle; wParam is the submenu handle */
|
||||
if (mf & MF_POPUP)
|
||||
{
|
||||
#ifdef OPUS_X64
|
||||
HMENU hMenu = (HMENU) lParam;
|
||||
HMENU hPopup = GetSubMenu(hMenu, item);
|
||||
int imnu = ImnuFromHMenu(hMenu, (UINT_PTR) hPopup);
|
||||
#else
|
||||
int imnu = ImnuFromHMenu(HIWORD(lParam), wParam);
|
||||
#endif
|
||||
#ifdef DEBUG
|
||||
#define imnuHelp 11 /* stolen from menuhelp.c */
|
||||
/* give the right context for Help menu in debug version (debug
|
||||
@@ -631,7 +669,11 @@ int cxt;
|
||||
{
|
||||
/* MF_SYSMENU && !MF_POPUP case handled in CmdHelp */
|
||||
Assert (!(mf & MF_SYSMENU));
|
||||
#ifdef OPUS_X64
|
||||
return (CxtFromBcm(item));
|
||||
#else
|
||||
return (CxtFromBcm(wParam));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -748,8 +790,13 @@ CMD CmdHelp(pcmb)
|
||||
CMB *pcmb;
|
||||
{
|
||||
int cxt = vcxtHelp; /* because EndMenu can blow away vcxtHelp */
|
||||
#ifdef OPUS_X64
|
||||
WPARAM wParamSav = vwParamHelp; /* EndMenu resets these globals */
|
||||
LPARAM lParamSav = vlParamHelp;
|
||||
#else
|
||||
WORD wParamSav = vwParamHelp; /* EndMenu resets these globals */
|
||||
LONG lParamSav = vlParamHelp;
|
||||
#endif
|
||||
|
||||
EndMenu(); /* end menu mode if we're in it */
|
||||
|
||||
|
||||
@@ -401,7 +401,11 @@ typedef struct _dli // DiaLog Initializer.
|
||||
HWND hwnd;
|
||||
int dx, dy;
|
||||
FDLG fdlg;
|
||||
#ifdef OPUS_X64
|
||||
UINT_PTR wRef;
|
||||
#else
|
||||
WORD wRef;
|
||||
#endif
|
||||
BYTE * rgb; // App-supplied rgtmw (in sbDlg).
|
||||
|
||||
#ifdef SDM_MULTI_SB
|
||||
@@ -419,7 +423,11 @@ typedef struct _dli // DiaLog Initializer.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Misc Functions .
|
||||
|
||||
#ifdef OPUS_X64
|
||||
extern UINT_PTR PASCAL wRefDlgCur; // Cached pointer-sized value.
|
||||
#else
|
||||
extern WORD PASCAL wRefDlgCur; // Cached value.
|
||||
#endif
|
||||
extern HCAB PASCAL hcabDlgCur; // Cached value.
|
||||
|
||||
#define WRefDlgCur() wRefDlgCur
|
||||
@@ -743,7 +751,11 @@ typedef struct _dlh
|
||||
// of mess in the sdm .c files.
|
||||
#endif //!SDM_ENV_PM
|
||||
|
||||
#ifdef OPUS_X64
|
||||
UINT_PTR wRef; // User supplied Dialog pointer/value.
|
||||
#else
|
||||
WORD wRef; // User supplied Dialog word.
|
||||
#endif
|
||||
FDLG fdlg; // Dialog flags.
|
||||
WORD hid; // Help ID.
|
||||
} DLH; // Public part of DLG structure.
|
||||
|
||||
+15
-3
@@ -32,6 +32,10 @@ extern CHAR vstPrompt[];
|
||||
extern int vfSysMenu;
|
||||
char *PchGetStr();
|
||||
|
||||
#ifdef OPUS_X64
|
||||
int ImnuFromHMenu(HMENU, UINT_PTR);
|
||||
#endif
|
||||
|
||||
/* This file is generated by a series of processes from OpusCmd.CMD
|
||||
* it contains the help prompt strings in compressed form.
|
||||
*/
|
||||
@@ -103,10 +107,14 @@ csconst char csrgstMenuBarHelp [] [] =
|
||||
/* D R A W M E N U H E L P */
|
||||
/* Called from WM_MENUSELECT handler... */
|
||||
/* %%Function:DrawMenuHelp %%Owner:krishnam */
|
||||
#ifdef OPUS_X64
|
||||
void DrawMenuHelp(HMENU hMenu, UINT_PTR bcm, int mf)
|
||||
#else
|
||||
DrawMenuHelp(hMenu, bcm, mf)
|
||||
HMENU hMenu;
|
||||
BCM bcm;
|
||||
int mf;
|
||||
#endif
|
||||
{
|
||||
char FAR * lpst;
|
||||
int cch;
|
||||
@@ -153,11 +161,11 @@ int mf;
|
||||
goto LHaveString;
|
||||
}
|
||||
else if (mf & MF_SYSMENU)
|
||||
bcm = BcmFromSysMenu(bcm);
|
||||
bcm = BcmFromSysMenu((WORD) bcm);
|
||||
|
||||
{
|
||||
CHAR szT[256];
|
||||
GetMenuHelpSz(bcm, szT);
|
||||
GetMenuHelpSz((BCM) bcm, szT);
|
||||
SzToStInPlace(szT);
|
||||
*szT = min (*szT, cchPromptMax-1);
|
||||
CopySt (szT, vstPrompt);
|
||||
@@ -169,8 +177,12 @@ LHaveString:
|
||||
|
||||
|
||||
/* %%Function:ImnuFromMenu %%Owner:krishnam */
|
||||
#ifdef OPUS_X64
|
||||
int ImnuFromHMenu(HMENU hMenu, UINT_PTR bcm)
|
||||
#else
|
||||
int ImnuFromHMenu(hMenu, bcm)
|
||||
HMENU hMenu;
|
||||
#endif
|
||||
{
|
||||
int imnu = 0;
|
||||
HMENU hMenuT;
|
||||
@@ -180,7 +192,7 @@ HMENU hMenu;
|
||||
|
||||
while ((hMenuT = GetSubMenu(vhMenu, imnu++)) != NULL)
|
||||
{
|
||||
if (hMenuT == bcm)
|
||||
if ((UINT_PTR) hMenuT == (UINT_PTR) bcm)
|
||||
{
|
||||
if (!vfSysMenu)
|
||||
imnu++;
|
||||
|
||||
+1
-1
@@ -511,7 +511,7 @@ DYY *pdyyAscent, *pdyyDescent;
|
||||
Profile( vpfi == pfiPictFmt ? StartProf( 30) : 0);
|
||||
|
||||
/* find the CP for the picture, taking vanished text into account */
|
||||
for (vfmtss.cpRef = vfli.cpMin + ich; bchr < vbchrMac; bchr += pchr->chrm)
|
||||
for (vfmtss.cpRef = vfli.cpMin + ich; bchr < vbchrMac; bchr += CbFromChrm(pchr->chrm))
|
||||
{
|
||||
pchr = (char *) **vhgrpchr + bchr;
|
||||
if (pchr->ich > ich)
|
||||
|
||||
+14
-4
@@ -477,6 +477,8 @@ LEndPaint:
|
||||
|
||||
case WM_VSCROLL:
|
||||
{
|
||||
int sbMessage;
|
||||
int posNew;
|
||||
/* Deals with mouse activity in vertical scroll bar.
|
||||
Jumps by 1 or 2 pages (depending on vpvs.fFacing flag)
|
||||
unless thumb is manually moved, in which case the
|
||||
@@ -491,10 +493,18 @@ LEndPaint:
|
||||
goto LRet;
|
||||
}
|
||||
|
||||
switch (wParam)
|
||||
#ifdef OPUS_X64
|
||||
sbMessage = LOWORD(wParam);
|
||||
posNew = HIWORD(wParam);
|
||||
#else
|
||||
sbMessage = wParam;
|
||||
posNew = LOWORD(lParam);
|
||||
#endif
|
||||
|
||||
switch (sbMessage)
|
||||
{
|
||||
case SB_THUMBPOSITION:
|
||||
fFail = !FGotoPagePrvw(hdc,LOWORD(lParam),dipgdRandom);
|
||||
fFail = !FGotoPagePrvw(hdc, posNew, dipgdRandom);
|
||||
break;
|
||||
|
||||
case SB_THUMBTRACK:
|
||||
@@ -507,10 +517,10 @@ LEndPaint:
|
||||
struct PLCPGD **hplcpgd = PdodDoc(vpvs.docPrvw)->hplcpgd;
|
||||
CP cpThumb;
|
||||
|
||||
if (LOWORD(lParam) == vpvs.ypLastScrlBar)
|
||||
if (posNew == vpvs.ypLastScrlBar)
|
||||
cpThumb= cpMac-1;
|
||||
else
|
||||
cpThumb = (cpMac*LOWORD(lParam))/(vpvs.ypLastScrlBar);
|
||||
cpThumb = (cpMac*posNew)/(vpvs.ypLastScrlBar);
|
||||
if (hplcpgd == hNil || (ipgd=IInPlcCheck(hplcpgd,cpThumb)) < 0)
|
||||
ipgd = 0;
|
||||
if (ipgd != ipgdLast)
|
||||
|
||||
+1
-1
@@ -1868,7 +1868,7 @@ struct RC *prc;
|
||||
pdxp = &vfli.rgdxp[0];
|
||||
ich = 0;
|
||||
FreezeHp();
|
||||
for (pchr = &(**vhgrpchr)[0]; ; (char *)pchr += chrm)
|
||||
for (pchr = &(**vhgrpchr)[0]; ; (char *)pchr += CbFromChrm(chrm))
|
||||
{
|
||||
for (ichNext = pchr->ich; cpT < cpPic && ich < ichNext; ich++, cpT++)
|
||||
xp += *pdxp++;
|
||||
|
||||
@@ -227,11 +227,15 @@ int n1, n2;
|
||||
|
||||
|
||||
/* %%Function:AppWndProcRare %%Owner:chic */
|
||||
#ifdef OPUS_X64
|
||||
LRESULT AppWndProcRare(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
#else
|
||||
long AppWndProcRare(hwnd, message, wParam, lParam)
|
||||
HWND hwnd;
|
||||
int message;
|
||||
WORD wParam;
|
||||
LONG lParam;
|
||||
#endif
|
||||
{
|
||||
HDLG hdlg;
|
||||
LPPOINT rgpt;
|
||||
@@ -328,7 +332,11 @@ LONG lParam;
|
||||
EnsureFocusInPane();
|
||||
|
||||
if (vfRecording && fRecordMove)
|
||||
#ifdef OPUS_X64
|
||||
RecordAppMove((short) LOWORD(lParam), (short) HIWORD(lParam));
|
||||
#else
|
||||
RecordAppMove(LOWORD(lParam), HIWORD(lParam));
|
||||
#endif
|
||||
fRecordMove = fFalse;
|
||||
|
||||
defproc:
|
||||
@@ -449,7 +457,11 @@ defproc:
|
||||
InvalidateRect(wParam, (LPRECT) NULL, fTrue);
|
||||
break;
|
||||
}
|
||||
#ifdef OPUS_X64
|
||||
PaintClipboard((HWND) wParam, (HANDLE) lParam);
|
||||
#else
|
||||
PaintClipboard( wParam, LOWORD(lParam) );
|
||||
#endif
|
||||
break;
|
||||
|
||||
case WM_VSCROLLCLIPBOARD:
|
||||
@@ -484,7 +496,11 @@ defproc:
|
||||
if (vrf.fInDisplayFli)
|
||||
break;
|
||||
|
||||
#ifdef OPUS_X64
|
||||
SizeClipboard((HWND) wParam, (HANDLE) lParam);
|
||||
#else
|
||||
SizeClipboard( wParam, LOWORD(lParam) );
|
||||
#endif
|
||||
break;
|
||||
|
||||
case WM_ASKCBFORMATNAME:
|
||||
|
||||
+2
-2
@@ -706,8 +706,8 @@ long lParam;
|
||||
"FSplitBoxMouse");
|
||||
break;
|
||||
}
|
||||
pt.xp = LOWORD(lParam);
|
||||
pt.yp = HIWORD(lParam);
|
||||
pt.xp = (short) LOWORD(lParam);
|
||||
pt.yp = (short) HIWORD(lParam);
|
||||
ClientToScreen(hwnd, (LPPOINT) &pt );
|
||||
TrackSplitBar( pt, wk );
|
||||
if ((wk == wkFtn || wk == wkAtn) && PwwdWw(wwCur)->wk == wk)
|
||||
|
||||
@@ -17,6 +17,19 @@ struct EDL
|
||||
union {
|
||||
int grpfEdl;
|
||||
struct {
|
||||
#ifdef OPUS_X64
|
||||
/* Win16 packed dcpDepend and the EDL flags into one word. A
|
||||
* byte followed by int bitfields starts a second allocation unit
|
||||
* under MSVC x64, so grpfEdl = 0 did not clear dlk and the other
|
||||
* flags. Keep the original four-byte overlay explicitly. */
|
||||
unsigned int dcpDepend : 8;
|
||||
unsigned int dlk : 3;
|
||||
unsigned int fDirty : 1;
|
||||
unsigned int fHasBitmap : 1;
|
||||
unsigned int fTableDirty : 1;
|
||||
unsigned int fColorMFP : 1;
|
||||
unsigned int fNeedEnhance : 1;
|
||||
#else
|
||||
unsigned char dcpDepend;
|
||||
int dlk : 3;
|
||||
int fDirty : 1;
|
||||
@@ -24,8 +37,18 @@ struct EDL
|
||||
int fTableDirty : 1;
|
||||
int fColorMFP: 1; /* color metafile (WIN) */
|
||||
int fNeedEnhance: 1; /* postponed display (WIN) */
|
||||
#endif
|
||||
};
|
||||
struct {
|
||||
#ifdef OPUS_X64
|
||||
unsigned int : 8;
|
||||
unsigned int fEnd : 1;
|
||||
unsigned int fEndPage : 1;
|
||||
unsigned int fEndDr : 1;
|
||||
unsigned int : 1;
|
||||
unsigned int fRMark : 1;
|
||||
unsigned int : 3;
|
||||
#else
|
||||
int : 8;
|
||||
/* set iff edl is the endmark. Other fields are then:
|
||||
cp = cpMac of doc, dcpMac = 0 */
|
||||
@@ -39,6 +62,7 @@ cp = cpMac of doc, dcpMac = 0 */
|
||||
/**/
|
||||
int fRMark : 1;
|
||||
int : 3;
|
||||
#endif
|
||||
};
|
||||
};
|
||||
struct {
|
||||
|
||||
@@ -2883,7 +2883,7 @@ LEndWord:
|
||||
cpChr = cpT;
|
||||
}
|
||||
ichChr = pchr->ich;
|
||||
*pbchrBreak += chrm;
|
||||
*pbchrBreak += CbFromChrm(chrm);
|
||||
}
|
||||
/* now ichLim is in the "break" run */
|
||||
vfli.cpMac = cpChr + ichLim - ichChr;
|
||||
@@ -3010,7 +3010,7 @@ else if (chrm == chrmFormula)
|
||||
|
||||
#endif
|
||||
if (chrm == chrmEnd) break;
|
||||
(char *)pchr += chrm;
|
||||
(char *)pchr += CbFromChrm(chrm);
|
||||
}
|
||||
if ((CP) ich >= dcp)
|
||||
{
|
||||
|
||||
@@ -335,10 +335,30 @@ struct CHRFG
|
||||
|
||||
#define cbCHRFG sizeof(struct CHRFG)
|
||||
|
||||
/* The original 16-bit layout used each structure's byte length as its type
|
||||
tag, which made grpchr records self-scanning. Native x64 alignment makes
|
||||
some otherwise unrelated records the same size (CHR and CHRDF are both 32
|
||||
bytes), so the x64 port uses explicit unique tags and maps them back to their
|
||||
storage lengths while scanning. */
|
||||
#ifdef OPUS_X64
|
||||
#define chrmEnd 1
|
||||
#define chrmChp 2
|
||||
#define chrmTab 3
|
||||
#define chrmVanish 4
|
||||
#define chrmFormula 5
|
||||
#define chrmDisplayField 6
|
||||
#define chrmFormatGroup 7
|
||||
#define CbFromChrm(chrm) \
|
||||
((chrm) == chrmChp ? cbCHR : \
|
||||
(chrm) == chrmTab ? cbCHRT : \
|
||||
(chrm) == chrmVanish ? cbCHRV : \
|
||||
(chrm) == chrmFormula ? cbCHRF : \
|
||||
(chrm) == chrmDisplayField ? cbCHRDF : \
|
||||
(chrm) == chrmFormatGroup ? cbCHRFG : cbCHRE)
|
||||
#else
|
||||
/* chrm's are also used as the lengths of the structures so that grpchr
|
||||
can be scanned easily. Note that in case of cbCHRT = cbCHRV we fudge the
|
||||
numbers so that they come out all different */
|
||||
|
||||
#define chrmChp cbCHR /* 14(mac) 18(win) */
|
||||
#define chrmTab cbCHRT /* 4 */
|
||||
#define chrmVanish cbCHRV /* 6 */
|
||||
@@ -348,6 +368,8 @@ numbers so that they come out all different */
|
||||
/* the chrmEnd type chr has no separate definition, we just use the first
|
||||
two bytes of a chr */
|
||||
#define chrmEnd cbCHRE /* 2 */
|
||||
#define CbFromChrm(chrm) (chrm)
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef MAC
|
||||
@@ -396,7 +418,11 @@ typedef union {
|
||||
int dyp;
|
||||
int dyt;
|
||||
};
|
||||
#ifdef OPUS_X64
|
||||
long long wlAll; /* covers both native 32-bit metric fields */
|
||||
#else
|
||||
long wlAll;
|
||||
#endif
|
||||
} DYY;
|
||||
#else
|
||||
typedef union {
|
||||
|
||||
@@ -1403,6 +1403,11 @@ LNoHyphen:
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Appending just beyond the terminal display PLC must dirty the DR
|
||||
* itself, not only the WWD. InvalCp3 deliberately ignores ranges
|
||||
* beyond the currently displayed cpMac, so without this the window
|
||||
* is later marked clean with no EDL for the new paragraph. */
|
||||
pdrT->fDirty = fTrue;
|
||||
FreePdrf(&drfFetch);
|
||||
InvalCp(PcaSet(&caT, selCur.doc, cpMinInval, cpMinInval + 1));
|
||||
UpdateWw(wwCur, fFalse);
|
||||
@@ -1636,6 +1641,14 @@ not following in cp space (yp space already checked above) */
|
||||
/* next line is invalid if it exists (<dlMac) and
|
||||
not following in cp space or not following in yp space */
|
||||
if (cpNextLine != vfli.cpMac ||
|
||||
#ifdef OPUS_X64
|
||||
/* The quick-insert piece can leave the terminal PLC
|
||||
* boundary equal to the EOP line's cpMac even though
|
||||
* the insertion point is the start of a new paragraph.
|
||||
* Do not mark the window clean until UpdateWw has
|
||||
* materialized that following display line. */
|
||||
(dl >= IMacPlc(hplcedl) && cpInsNew >= vfli.cpMac) ||
|
||||
#endif
|
||||
(dl < IMacPlc(hplcedl) &&
|
||||
((GetPlc(hplcedl, dl, &edl), edl.fDirty) ||
|
||||
edl.ypTop != ypTop + vfli.dypLine)) ||
|
||||
|
||||
+32
-12
@@ -133,26 +133,40 @@ struct SCC
|
||||
};
|
||||
struct CA ca;
|
||||
};
|
||||
int dcpDepend : 8;
|
||||
unsigned char dcpDepend;
|
||||
#ifdef OPUS_X64
|
||||
int fDirtyDr : 1;
|
||||
#else
|
||||
int fDirty : 1;
|
||||
#endif
|
||||
int fCpBad : 1;
|
||||
int fLimSuspect: 1;
|
||||
int : 5;
|
||||
int fLimSuspect : 1;
|
||||
int fNoParaStart : 1;
|
||||
int fIncomplete : 1;
|
||||
int fInTable : 1;
|
||||
int fBottomTableFrame : 1;
|
||||
int fForceWidth : 1;
|
||||
struct PLCEDL **hplcedl;
|
||||
int dypAbove;
|
||||
int dxpOutLeft;
|
||||
int dxpOutRight;
|
||||
int idrFlow; /* text should flow to this dr */
|
||||
int lrk; /* type of lr from which this came */
|
||||
int fNoPrev : 1; /* redundant bit: 1 means there is no
|
||||
dr with this dr as successor */
|
||||
int fConstrainLeft : 1; /* constrained by abs on left */
|
||||
int fConstrainRight : 1; /* on right */
|
||||
int wSpare1 : 13; /* spare for future use */
|
||||
int dxpOutLeft;
|
||||
int dxpOutRight;
|
||||
int idrFlow; /* text should flow to this dr */
|
||||
char ccolM1;
|
||||
char lrk; /* type of lr from which this came */
|
||||
int fNoPrev : 1; /* redundant bit: 1 means there is no
|
||||
dr with this dr as successor */
|
||||
int fConstrainLeft : 1; /* constrained by abs on left */
|
||||
int fConstrainRight : 1; /* on right */
|
||||
int fNewColOnly : 1;
|
||||
int fFatLine : 1;
|
||||
int fCantGrow : 1;
|
||||
int fHdrFtr : 1;
|
||||
int fForceFirstRow : 1;
|
||||
int fRMark : 1;
|
||||
int fSpaceBefore : 1;
|
||||
int : 6;
|
||||
int xwLimScroll;
|
||||
int dxaBetween;
|
||||
/* Note: the DR definition now includes a PLCEDL
|
||||
header, but this is only for far PLCEDL's. Since
|
||||
the SCC is using a near PLCEDL defined at the
|
||||
@@ -212,6 +226,12 @@ struct SCC
|
||||
};
|
||||
};
|
||||
|
||||
#ifdef OPUS_X64
|
||||
typedef char OPUS_SCC_HPLCEDL_OFFSET_MUST_MATCH_DR[
|
||||
(offsetof(struct SCC, hplcedl) - offsetof(struct SCC, rgdr) ==
|
||||
offsetof(struct DR, hplcedl)) ? 1 : -1];
|
||||
#endif
|
||||
|
||||
#define cwSCC (sizeof (struct SCC) / sizeof (int))
|
||||
|
||||
#define cbBmbAbove 20000
|
||||
|
||||
+26
-26
@@ -23,21 +23,21 @@ struct SEL
|
||||
{
|
||||
union {
|
||||
struct {
|
||||
int : 10;
|
||||
int sk : 6;
|
||||
unsigned int : 10;
|
||||
unsigned int sk : 6;
|
||||
};
|
||||
struct {
|
||||
int fRightward : 1;
|
||||
int fSelAtPara: 1;
|
||||
int fWithinCell : 1;
|
||||
int fTableAnchor : 1;
|
||||
int : 6;
|
||||
int fColumn : 1;
|
||||
int fTable : 1;
|
||||
int fGraphics : 1;
|
||||
int fBlock : 1;
|
||||
int fNil : 1;
|
||||
int fIns : 1;
|
||||
unsigned int fRightward : 1;
|
||||
unsigned int fSelAtPara: 1;
|
||||
unsigned int fWithinCell : 1;
|
||||
unsigned int fTableAnchor : 1;
|
||||
unsigned int : 6;
|
||||
unsigned int fColumn : 1;
|
||||
unsigned int fTable : 1;
|
||||
unsigned int fGraphics : 1;
|
||||
unsigned int fBlock : 1;
|
||||
unsigned int fNil : 1;
|
||||
unsigned int fIns : 1;
|
||||
};
|
||||
};
|
||||
unsigned char fForward;
|
||||
@@ -89,22 +89,22 @@ struct SEL
|
||||
int dyp;
|
||||
/* tick at which cursor was last blinked */
|
||||
long tickOld;
|
||||
int fUpdateChp : 1;
|
||||
int fUpdateChpGray : 1;
|
||||
int fUpdatePap : 1;
|
||||
int fUpdateRibbon : 1;
|
||||
int fUpdateRuler : 1;
|
||||
int fUpdateStatLine : 1;
|
||||
int fUpdateStatLine2: 1;
|
||||
unsigned int fUpdateChp : 1;
|
||||
unsigned int fUpdateChpGray : 1;
|
||||
unsigned int fUpdatePap : 1;
|
||||
unsigned int fUpdateRibbon : 1;
|
||||
unsigned int fUpdateRuler : 1;
|
||||
unsigned int fUpdateStatLine : 1;
|
||||
unsigned int fUpdateStatLine2: 1;
|
||||
|
||||
#ifdef MAC
|
||||
int fNinchChp : 1;
|
||||
int fNinchPap : 1;
|
||||
int fNinchSep : 1;
|
||||
int fItalic : 1;
|
||||
int : 5;
|
||||
unsigned int fNinchChp : 1;
|
||||
unsigned int fNinchPap : 1;
|
||||
unsigned int fNinchSep : 1;
|
||||
unsigned int fItalic : 1;
|
||||
unsigned int : 5;
|
||||
#else
|
||||
int : 9;
|
||||
unsigned int : 9;
|
||||
#endif /* MAC */
|
||||
struct RC rcwClip;
|
||||
struct CHP chp;
|
||||
|
||||
@@ -957,7 +957,7 @@ over xp, depending whichever is closer to the middle point of the char */
|
||||
break;
|
||||
}
|
||||
/* note how chrm is cb of the variant chr structure! */
|
||||
(char *)pchr += chrm;
|
||||
(char *)pchr += CbFromChrm(chrm);
|
||||
}
|
||||
|
||||
LHaveCp:
|
||||
|
||||
@@ -107,6 +107,14 @@ typedef IB HUGE * HQ;
|
||||
|
||||
/* for source compatibilty with Opus huge block manipulations */
|
||||
struct BPS HUGE *HpbpsIbp();
|
||||
#ifdef OPUS_X64
|
||||
/* Native replacement for FILEWINN.ASM. This must be declared because an
|
||||
* implicit-int call truncates the flat cache-page pointer on AMD64. */
|
||||
CHAR HUGE *HpOfBptbExt(int ibp);
|
||||
/* DEBUG.H remaps HpchGetPn calls to the translated native entry. Declare
|
||||
* that entry before the remap so C callers retain the full AMD64 pointer. */
|
||||
CHAR HUGE *N_HpchGetPn(int fn, int pn);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -644,7 +644,7 @@ struct PREF
|
||||
int fShowAllMacros: 1 ;
|
||||
int : 1 ; /* spare */
|
||||
/* used in customize dialog */
|
||||
int iAS: 2; /* Auto Save frequency index. */
|
||||
unsigned int iAS: 2; /* Auto Save frequency index (0..3). */
|
||||
|
||||
int fDraftView: 1;
|
||||
int fZoomApp: 1;
|
||||
|
||||
+230
-9
@@ -316,8 +316,13 @@ extern HWND vhwndCBT;
|
||||
extern int vcxtHelp; /* help context id */
|
||||
extern BOOL vfHelp; /* whether we're in Help Mode */
|
||||
extern long vcmsecHelp;
|
||||
#ifdef OPUS_X64
|
||||
extern WPARAM vwParamHelp;
|
||||
extern LPARAM vlParamHelp;
|
||||
#else
|
||||
extern WORD vwParamHelp;
|
||||
extern LONG vlParamHelp;
|
||||
#endif
|
||||
extern HCURSOR vhcHourGlass;
|
||||
extern HCURSOR vhcIBeam;
|
||||
extern HCURSOR vhcArrow;
|
||||
@@ -390,7 +395,25 @@ extern int vfNoInval;
|
||||
void MwdVertScroll( int, WORD, int );
|
||||
void MwdHorzScroll( int, WORD, int );
|
||||
void WwPaneMouse( int, unsigned, WORD, POINT );
|
||||
#ifdef OPUS_X64
|
||||
LRESULT LMenuChar(HWND, WPARAM, LPARAM);
|
||||
void DrawMenuHelp(HMENU, UINT_PTR, int);
|
||||
void SaveMenuHelpContext(WPARAM, LPARAM, int);
|
||||
|
||||
/*
|
||||
* Opus used the sign bit of a Win16 WORD-sized int to distinguish its
|
||||
* command ids from the SC_* values in WM_SYSCOMMAND. A Win64 int is 32
|
||||
* bits, so casting WPARAM to int makes every SC_* value positive and causes
|
||||
* Word to swallow window move/size/close commands as application commands.
|
||||
*/
|
||||
#define FOpusCommandWParam(wParam) ((short) LOWORD(wParam) >= 0)
|
||||
#define BcmFromWParam(wParam) ((WORD) LOWORD(wParam))
|
||||
#define WM_OPUS_X64_QUERY_SELECTION (WM_APP + 0x351)
|
||||
#else
|
||||
LONG LMenuChar(HWND, WORD, LONG);
|
||||
#define FOpusCommandWParam(wParam) ((int) (wParam) >= 0)
|
||||
#define BcmFromWParam(wParam) (wParam)
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
@@ -750,12 +773,21 @@ LONG lParam;
|
||||
{
|
||||
static int fEnteredIdle=fTrue;
|
||||
static HMENU hMenu;
|
||||
#ifdef OPUS_X64
|
||||
static UINT_PTR bcm;
|
||||
#else
|
||||
static int bcm;
|
||||
#endif
|
||||
static int mf;
|
||||
int cxt;
|
||||
|
||||
#ifdef OPUS_X64
|
||||
LRESULT AppWndProcRare(HWND, UINT, WPARAM, LPARAM);
|
||||
LRESULT AppWndProcAct(HWND, UINT, WPARAM, LPARAM);
|
||||
#else
|
||||
long AppWndProcRare();
|
||||
long AppWndProcAct();
|
||||
#endif
|
||||
|
||||
struct RC rc;
|
||||
|
||||
@@ -799,7 +831,7 @@ LONG lParam;
|
||||
/* CBT hook must be the first thing we do under WM_COMMAND */
|
||||
|
||||
if (vhwndCBT && !SendMessage(vhwndCBT, WM_CBTSEMEV, smvCommand,
|
||||
MAKELONG(CxtFromBcm(wParam), 0 /* from menu */)))
|
||||
MAKELONG(CxtFromBcm(BcmFromWParam(wParam)), 0 /* from menu */)))
|
||||
{
|
||||
return (0L);
|
||||
}
|
||||
@@ -813,12 +845,12 @@ LONG lParam;
|
||||
|
||||
RestorePrompt();
|
||||
|
||||
if (wParam == bcmNil) /* menu empty */
|
||||
if (BcmFromWParam(wParam) == bcmNil) /* menu empty */
|
||||
return (0L);
|
||||
|
||||
Assert((int) wParam >= 0);
|
||||
Assert(FOpusCommandWParam(wParam));
|
||||
vcxtHelp = cxtNil;
|
||||
FExecCmd(wParam);
|
||||
FExecCmd(BcmFromWParam(wParam));
|
||||
break;
|
||||
|
||||
case WM_SYSCOMMAND:
|
||||
@@ -887,9 +919,9 @@ LONG lParam;
|
||||
Profile(vpfi == pfiMenu ? StartProf(30) : 0);
|
||||
EnsureFocusInPane();
|
||||
vcxtHelp = cxtNil;
|
||||
if ((int) wParam >= 0)
|
||||
if (FOpusCommandWParam(wParam))
|
||||
{
|
||||
CmdExecBcmKc(wParam, kcNil);
|
||||
CmdExecBcmKc(BcmFromWParam(wParam), kcNil);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -955,9 +987,17 @@ LDefProc:
|
||||
|
||||
if (vhwndStatLine)
|
||||
{
|
||||
#ifdef OPUS_X64
|
||||
hMenu = (HMENU) lParam;
|
||||
mf = HIWORD(wParam);
|
||||
bcm = (mf & MF_POPUP) && hMenu != NULL ?
|
||||
(UINT_PTR) GetSubMenu(hMenu, LOWORD(wParam)) :
|
||||
(UINT_PTR) LOWORD(wParam);
|
||||
#else
|
||||
hMenu = HIWORD(lParam);
|
||||
bcm = wParam;
|
||||
mf = LOWORD(lParam );
|
||||
#endif
|
||||
if (hMenu == 0 && fEnteredIdle)
|
||||
DrawMenuHelp(hMenu, bcm, mf);
|
||||
fEnteredIdle = fFalse;
|
||||
@@ -1026,9 +1066,17 @@ LONG lParam;
|
||||
extern int mwCreate;
|
||||
static int fEnteredIdle=fTrue;
|
||||
static HMENU hMenu;
|
||||
#ifdef OPUS_X64
|
||||
static UINT_PTR bcm;
|
||||
#else
|
||||
static int bcm;
|
||||
#endif
|
||||
static int mf;
|
||||
#ifdef OPUS_X64
|
||||
LRESULT MwdWndProcRare(HWND, UINT, WPARAM, LPARAM);
|
||||
#else
|
||||
long MwdWndProcRare();
|
||||
#endif
|
||||
int ww;
|
||||
PAINTSTRUCT ps;
|
||||
|
||||
@@ -1065,6 +1113,21 @@ LONG lParam;
|
||||
SetUatMode(uamNavigation);
|
||||
StopUatTimer();
|
||||
#endif /* RSH */
|
||||
#ifdef OPUS_X64
|
||||
{
|
||||
WORD code = LOWORD(wParam);
|
||||
int dqThumb = HIWORD(wParam);
|
||||
HWND hwndScroll = (HWND) lParam;
|
||||
|
||||
if (message == WM_HSCROLL)
|
||||
MwdHorzScroll(wwCur, code, dqThumb);
|
||||
else
|
||||
MwdVertScroll(((*hwwdCur)->hwndVScroll == hwndScroll) ?
|
||||
wwCur : WwOther(wwCur), code, dqThumb);
|
||||
if (vfRecording)
|
||||
RecordScroll(message, code, dqThumb);
|
||||
}
|
||||
#else
|
||||
if (message == WM_HSCROLL)
|
||||
MwdHorzScroll( wwCur, wParam, (int)lParam);
|
||||
else
|
||||
@@ -1072,11 +1135,17 @@ LONG lParam;
|
||||
wwCur : WwOther(wwCur), wParam, (int)lParam);
|
||||
if (vfRecording)
|
||||
RecordScroll(message, wParam, LOWORD(lParam));
|
||||
#endif
|
||||
break;
|
||||
|
||||
case WM_MOUSEACTIVATE:
|
||||
{
|
||||
int iRet = 0;
|
||||
int iRet =
|
||||
#ifdef OPUS_X64
|
||||
MA_ACTIVATE;
|
||||
#else
|
||||
0;
|
||||
#endif
|
||||
|
||||
if (vhwndAppModalFocus != hNil)
|
||||
if (!vfDeactByOtherApp)
|
||||
@@ -1115,7 +1184,14 @@ LONG lParam;
|
||||
NewCurWw( PmwdMw(MwFromHwndMw(hwnd))->wwActive, fFalse /*fDoNotSelect*/);
|
||||
/* fall through */
|
||||
default:
|
||||
#ifdef OPUS_X64
|
||||
/* Modern USER can activate and deliver this click without the
|
||||
* Win16 re-entrancy workaround. Eating it makes the first click
|
||||
* after switching documents disappear. */
|
||||
iRet = MA_ACTIVATE;
|
||||
#else
|
||||
iRet = MA_ACTIVATEANDEAT;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1189,8 +1265,8 @@ LONG lParam;
|
||||
|
||||
EnsureFocusInPane();
|
||||
|
||||
if ((int) wParam >= 0)
|
||||
return CmdExecBcmKc(wParam, kcNil) == cmdOK;
|
||||
if (FOpusCommandWParam(wParam))
|
||||
return CmdExecBcmKc(BcmFromWParam(wParam), kcNil) == cmdOK;
|
||||
|
||||
switch (wParam & 0xfff0)
|
||||
{
|
||||
@@ -1229,9 +1305,17 @@ LONG lParam;
|
||||
|
||||
if (vhwndStatLine)
|
||||
{
|
||||
#ifdef OPUS_X64
|
||||
hMenu = (HMENU) lParam;
|
||||
mf = HIWORD(wParam);
|
||||
bcm = (mf & MF_POPUP) && hMenu != NULL ?
|
||||
(UINT_PTR) GetSubMenu(hMenu, LOWORD(wParam)) :
|
||||
(UINT_PTR) LOWORD(wParam);
|
||||
#else
|
||||
hMenu = HIWORD(lParam);
|
||||
bcm = wParam;
|
||||
mf = LOWORD(lParam );
|
||||
#endif
|
||||
if (hMenu == 0 && fEnteredIdle)
|
||||
DrawMenuHelp(hMenu, bcm, mf);
|
||||
fEnteredIdle = fFalse;
|
||||
@@ -1278,15 +1362,23 @@ DefaultProc: /* All messages not processed come here. */
|
||||
else, we can compute the context from these saved values.
|
||||
*/
|
||||
/* %%Function:SaveMenuHelpContext %%Owner:rosiep */
|
||||
#ifdef OPUS_X64
|
||||
void SaveMenuHelpContext(WPARAM wParam, LPARAM lParam, int cxt)
|
||||
#else
|
||||
SaveMenuHelpContext(wParam,lParam,cxt)
|
||||
WORD wParam;
|
||||
LONG lParam;
|
||||
int cxt;
|
||||
#endif
|
||||
{
|
||||
Assert(cxt == cxtAppMenuSelect || cxt == cxtDocMenuSelect);
|
||||
|
||||
/* if we're exiting Menu Mode, no more help context */
|
||||
#ifdef OPUS_X64
|
||||
if (lParam == 0 && HIWORD(wParam) == 0xffff)
|
||||
#else
|
||||
if (HIWORD(lParam) == 0 || wParam == 0)
|
||||
#endif
|
||||
{
|
||||
vwParamHelp = 0;
|
||||
vlParamHelp = 0;
|
||||
@@ -1380,7 +1472,11 @@ LONG lParam;
|
||||
{
|
||||
int ww;
|
||||
struct RC rc;
|
||||
#ifdef OPUS_X64
|
||||
LRESULT RareWwPaneWndProc(HWND, UINT, WPARAM, LPARAM);
|
||||
#else
|
||||
long RareWwPaneWndProc();
|
||||
#endif
|
||||
|
||||
#ifdef SHOWPNEMSG
|
||||
ShowMsg ("pn", hwnd, message, wParam, lParam);
|
||||
@@ -1400,6 +1496,104 @@ LONG lParam;
|
||||
default:
|
||||
return RareWwPaneWndProc( hwnd, message, wParam,lParam );
|
||||
|
||||
#ifdef OPUS_X64
|
||||
case WM_OPUS_X64_QUERY_SELECTION:
|
||||
switch (LOWORD(wParam))
|
||||
{
|
||||
case 0: return (LRESULT) selCur.cpFirst;
|
||||
case 1: return (LRESULT) selCur.cpLim;
|
||||
case 2: return (LRESULT) selCur.fIns;
|
||||
case 3: return (LRESULT) selCur.sty;
|
||||
case 4: return (LRESULT) selCur.xw;
|
||||
case 5: return (LRESULT) vfDoubleClick;
|
||||
case 6: return (LRESULT) selCur.sk;
|
||||
case 7: return (LRESULT) selCur.yw;
|
||||
case 8: return (LRESULT) selCur.dyp;
|
||||
case 9: return (LRESULT) selCur.fOn;
|
||||
case 10: return (LRESULT) selCur.fHidden;
|
||||
case 11: return (LRESULT) selCur.xpFirst;
|
||||
case 12: return (LRESULT) selCur.xpLim;
|
||||
case 13: return (LRESULT) vfli.cpMin;
|
||||
case 14: return (LRESULT) vfli.cpMac;
|
||||
case 15: return (LRESULT) vfli.xpLeft;
|
||||
case 16: return (LRESULT) vfli.xpRight;
|
||||
case 17: return (LRESULT) vfli.ichMac;
|
||||
case 18: return (LRESULT) vfli.rgdxp[0];
|
||||
case 19: return (LRESULT) vfli.rgdxp[
|
||||
vfli.ichMac == 0 ? 0 : vfli.ichMac - 1];
|
||||
case 24: return (LRESULT) sizeof(struct CHR);
|
||||
case 25: return (LRESULT) sizeof(struct CHRT);
|
||||
case 26: return (LRESULT) sizeof(struct CHRV);
|
||||
case 27: return (LRESULT) sizeof(struct CHRF);
|
||||
case 28: return (LRESULT) sizeof(struct CHRDF);
|
||||
case 29: return (LRESULT) sizeof(struct CHRFG);
|
||||
case 30:
|
||||
{
|
||||
struct DR *pdr = PdrGalley(PwwdWw(WwFromHwnd(hwnd)));
|
||||
return (LRESULT) IMacPlc(pdr->hplcedl);
|
||||
}
|
||||
case 31:
|
||||
{
|
||||
struct DR *pdr = PdrGalley(PwwdWw(WwFromHwnd(hwnd)));
|
||||
return (LRESULT) CpPlc(pdr->hplcedl, (int) lParam);
|
||||
}
|
||||
case 32:
|
||||
case 33:
|
||||
case 34:
|
||||
case 35:
|
||||
{
|
||||
struct EDL edl;
|
||||
struct DR *pdr = PdrGalley(PwwdWw(WwFromHwnd(hwnd)));
|
||||
GetPlc(pdr->hplcedl, (int) lParam, &edl);
|
||||
if (LOWORD(wParam) == 32) return (LRESULT) edl.ypTop;
|
||||
if (LOWORD(wParam) == 33) return (LRESULT) edl.dyp;
|
||||
if (LOWORD(wParam) == 34) return (LRESULT) edl.dcp;
|
||||
return (LRESULT) edl.fDirty;
|
||||
}
|
||||
case 36:
|
||||
case 37:
|
||||
{
|
||||
struct DR *pdr = PdrGalley(PwwdWw(WwFromHwnd(hwnd)));
|
||||
return LOWORD(wParam) == 36 ?
|
||||
(LRESULT) pdr->cpFirst : (LRESULT) pdr->cpLim;
|
||||
}
|
||||
case 38: return (LRESULT) sizeof(struct EDL);
|
||||
case 39:
|
||||
{
|
||||
struct DR *pdr = PdrGalley(PwwdWw(WwFromHwnd(hwnd)));
|
||||
return (LRESULT) pdr->fDirty;
|
||||
}
|
||||
case 40:
|
||||
return (LRESULT) PwwdWw(WwFromHwnd(hwnd))->fDirty;
|
||||
case 41: return (LRESULT) CpMacDocEdit(selCur.doc);
|
||||
case 42:
|
||||
{
|
||||
struct DR *pdr = PdrGalley(PwwdWw(WwFromHwnd(hwnd)));
|
||||
return (LRESULT) pdr->dyl;
|
||||
}
|
||||
case 43:
|
||||
return (LRESULT) DyOfRc(&PwwdWw(WwFromHwnd(hwnd))->rcwDisp);
|
||||
case 44:
|
||||
{
|
||||
struct DR *pdr = PdrGalley(PwwdWw(WwFromHwnd(hwnd)));
|
||||
return (LRESULT) pdr->dxl;
|
||||
}
|
||||
case 45:
|
||||
return (LRESULT) DxOfRc(&PwwdWw(WwFromHwnd(hwnd))->rcwDisp);
|
||||
case 46: return (LRESULT) CpMacDoc(selCur.doc);
|
||||
case 47:
|
||||
case 48:
|
||||
{
|
||||
struct EDL edl;
|
||||
struct DR *pdr = PdrGalley(PwwdWw(WwFromHwnd(hwnd)));
|
||||
GetPlc(pdr->hplcedl, (int) lParam, &edl);
|
||||
return LOWORD(wParam) == 47 ?
|
||||
(LRESULT) edl.dlk : (LRESULT) edl.fEnd;
|
||||
}
|
||||
}
|
||||
return (LRESULT) -1;
|
||||
#endif
|
||||
|
||||
case WM_SETFOCUS:
|
||||
/* The window is getting the focus. wParam contains the window
|
||||
** handle of the window that previously had the focus. */
|
||||
@@ -1483,7 +1677,14 @@ LONG lParam;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef OPUS_X64
|
||||
/* The Win16 eat-on-activate workaround makes the next
|
||||
* physical click look like a double-click on modern USER.
|
||||
* Let the activating click position the caret immediately. */
|
||||
return(MA_ACTIVATE);
|
||||
#else
|
||||
return(MA_ACTIVATEANDEAT);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
DefaultProc: /* All messages not processed come here. */
|
||||
@@ -2816,15 +3017,24 @@ lblSet:
|
||||
by the window proc receiving the message.
|
||||
*/
|
||||
/* %%Function:LMenuChar %%Owner:peterj */
|
||||
#ifdef OPUS_X64
|
||||
LRESULT LMenuChar(HWND hwnd, WPARAM wParam, LPARAM lParam)
|
||||
#else
|
||||
LONG LMenuChar(hwnd, wParam, lParam)
|
||||
HWND hwnd;
|
||||
WORD wParam;
|
||||
LONG lParam;
|
||||
#endif
|
||||
{
|
||||
if (vhwndAppModalFocus != NULL)
|
||||
return MAKELONG(0, 1);
|
||||
|
||||
#ifdef OPUS_X64
|
||||
if (LOWORD(wParam) == '-' && !(HIWORD(wParam) & MF_POPUP) &&
|
||||
hwwdCur != hNil)
|
||||
#else
|
||||
if (wParam == '-' && !(LOWORD(lParam) & MF_POPUP) && hwwdCur != hNil)
|
||||
#endif
|
||||
{
|
||||
if (vfSysMenu)
|
||||
{
|
||||
@@ -3181,7 +3391,18 @@ BOOL fRightButton;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef OPUS_X64
|
||||
/*
|
||||
* The Win16 loop could rely on GetKeyState's queue snapshot while nested
|
||||
* inside the button-down dispatch. On modern Windows that snapshot can
|
||||
* lag the physical/SENDINPUT transition and prematurely end a drag before
|
||||
* the corresponding button-up reaches this replay loop.
|
||||
*/
|
||||
return ((GetAsyncKeyState(fRightButton ? VK_RBUTTON : VK_LBUTTON) &
|
||||
0x8000) != 0);
|
||||
#else
|
||||
return (GetKeyState( fRightButton ? VK_RBUTTON : VK_LBUTTON ) < 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -133,11 +133,15 @@ extern int vfTableKeymap;
|
||||
|
||||
|
||||
/* %%Function:AppWndProcAct %%Owner:chic */
|
||||
#ifdef OPUS_X64
|
||||
LRESULT AppWndProcAct(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
#else
|
||||
long AppWndProcAct(hwnd, message, wParam, lParam)
|
||||
HWND hwnd;
|
||||
unsigned message;
|
||||
WORD wParam;
|
||||
LONG lParam;
|
||||
#endif
|
||||
{
|
||||
|
||||
switch (message)
|
||||
|
||||
@@ -131,11 +131,15 @@ HWND vhwndZoom = hNil;
|
||||
|
||||
|
||||
/* %%Function:MwdWndProcRare %%Owner:chic */
|
||||
#ifdef OPUS_X64
|
||||
LRESULT MwdWndProcRare(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
#else
|
||||
long MwdWndProcRare(hwnd, message, wParam, lParam)
|
||||
HWND hwnd;
|
||||
int message;
|
||||
WORD wParam;
|
||||
LONG lParam;
|
||||
#endif
|
||||
{
|
||||
|
||||
extern int mwCreate;
|
||||
@@ -169,12 +173,21 @@ LONG lParam;
|
||||
{
|
||||
struct MWD *pmwd = PmwdMw(mw);
|
||||
|
||||
#ifdef OPUS_X64
|
||||
pmwd->xp = (short) LOWORD(lParam);
|
||||
pmwd->yp = (short) HIWORD(lParam);
|
||||
#else
|
||||
pmwd->xp = LOWORD(lParam);
|
||||
pmwd->yp = HIWORD(lParam);
|
||||
#endif
|
||||
/* the dimensions are recorded in the size message */
|
||||
|
||||
if (vfRecording && fRecordMove)
|
||||
#ifdef OPUS_X64
|
||||
RecordDocMove((short) LOWORD(lParam), (short) HIWORD(lParam));
|
||||
#else
|
||||
RecordDocMove(LOWORD(lParam), HIWORD(lParam));
|
||||
#endif
|
||||
fRecordMove = fFalse;
|
||||
}
|
||||
break;
|
||||
@@ -214,11 +227,16 @@ LONG lParam;
|
||||
|
||||
|
||||
/* %%Function:RareWwPaneWndProc %%Owner:chic */
|
||||
#ifdef OPUS_X64
|
||||
LRESULT RareWwPaneWndProc(HWND hwnd, UINT message, WPARAM wParam,
|
||||
LPARAM lParam)
|
||||
#else
|
||||
long RareWwPaneWndProc(hwnd, message, wParam, lParam)
|
||||
HWND hwnd;
|
||||
int message;
|
||||
WORD wParam;
|
||||
LONG lParam;
|
||||
#endif
|
||||
{
|
||||
int ww;
|
||||
struct WWD *pwwd;
|
||||
|
||||
@@ -1,240 +0,0 @@
|
||||
#include "legacy_asm_port.h"
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace opus::x64 {
|
||||
namespace {
|
||||
|
||||
enum class PortKind : std::uint8_t {
|
||||
translated_cpp,
|
||||
portable_c_counterpart,
|
||||
win32_replacement,
|
||||
retired_platform_code,
|
||||
};
|
||||
|
||||
struct AssemblyPortEntry {
|
||||
std::string_view path;
|
||||
PortKind kind;
|
||||
};
|
||||
|
||||
// One entry per .asm file under src. This table is intentionally explicit:
|
||||
// adding an assembly module makes CMake fail until its x64 disposition is
|
||||
// reviewed. "portable_c_counterpart" means the hand-written assembly was an
|
||||
// optimized version of an extant C implementation in Opus/wordtech.
|
||||
constexpr std::array kAssemblyPorts{
|
||||
AssemblyPortEntry{"Opus/asm/asserth.asm", PortKind::retired_platform_code},
|
||||
AssemblyPortEntry{"Opus/asm/auxout.asm", PortKind::win32_replacement},
|
||||
AssemblyPortEntry{"Opus/asm/clsplcn.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/cmdlookn.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/dialog1n.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/disp1n.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/disp1n2.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/disp2n.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/disp3n.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/disptbln.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/disptbn2.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/editn.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/editn2.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/eldden.asm", PortKind::win32_replacement},
|
||||
AssemblyPortEntry{"Opus/asm/expn.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/fetch2n.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/fetchn.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/fetchn2.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/fetchn3.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/fetchtbn.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/fieldcrn.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/fieldfmn.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/fieldspn.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/file2n.asm", PortKind::win32_replacement},
|
||||
AssemblyPortEntry{"Opus/asm/filewinn.asm", PortKind::win32_replacement},
|
||||
AssemblyPortEntry{"Opus/asm/formatn.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/formatn2.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/formulan.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/grbitx.asm", PortKind::translated_cpp},
|
||||
AssemblyPortEntry{"Opus/asm/grhc.asm", PortKind::translated_cpp},
|
||||
AssemblyPortEntry{"Opus/asm/index2n.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/inssubsn.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/int3f.asm", PortKind::retired_platform_code},
|
||||
AssemblyPortEntry{"Opus/asm/layout22.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/layout2n.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/movecmds.asm", PortKind::retired_platform_code},
|
||||
AssemblyPortEntry{"Opus/asm/natinit.asm", PortKind::translated_cpp},
|
||||
AssemblyPortEntry{"Opus/asm/nattrans.asm", PortKind::translated_cpp},
|
||||
AssemblyPortEntry{"Opus/asm/prln.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/profc.asm", PortKind::retired_platform_code},
|
||||
AssemblyPortEntry{"Opus/asm/profctl.asm", PortKind::retired_platform_code},
|
||||
AssemblyPortEntry{"Opus/asm/promptn.asm", PortKind::win32_replacement},
|
||||
AssemblyPortEntry{"Opus/asm/qplc.asm", PortKind::retired_platform_code},
|
||||
AssemblyPortEntry{"Opus/asm/resn.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/resn2.asm", PortKind::translated_cpp},
|
||||
AssemblyPortEntry{"Opus/asm/rip.asm", PortKind::retired_platform_code},
|
||||
AssemblyPortEntry{"Opus/asm/rtfinn.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/rtfsubsn.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/searchn.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/sttbn.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/wordgrep.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"Opus/asm/wprocn.asm", PortKind::win32_replacement},
|
||||
AssemblyPortEntry{"OpusEtAl/tools/src/appstub.asm", PortKind::retired_platform_code},
|
||||
AssemblyPortEntry{"OpusEtAl/tools/src/convtest/doslib.asm", PortKind::win32_replacement},
|
||||
AssemblyPortEntry{"OpusEtAl/tools/src/convtest/init.asm", PortKind::retired_platform_code},
|
||||
AssemblyPortEntry{"OpusEtAl/tools/src/makekeyn.asm", PortKind::portable_c_counterpart},
|
||||
AssemblyPortEntry{"OpusEtAl/tools/src/timedate.asm", PortKind::translated_cpp},
|
||||
AssemblyPortEntry{"OpusEtAl/tools/src/winemscs.asm", PortKind::retired_platform_code},
|
||||
AssemblyPortEntry{"OpusEtAl/tools/src/winemsds.asm", PortKind::retired_platform_code},
|
||||
};
|
||||
|
||||
static_assert(kAssemblyPorts.size() == 59);
|
||||
|
||||
} // namespace
|
||||
|
||||
std::int32_t multiply_divide(const std::int32_t value,
|
||||
const std::int32_t numerator,
|
||||
const std::int32_t denominator) noexcept {
|
||||
if (denominator == 0) {
|
||||
const bool negative = (value < 0) != (numerator < 0);
|
||||
return negative ? std::numeric_limits<std::int32_t>::min()
|
||||
: std::numeric_limits<std::int32_t>::max();
|
||||
}
|
||||
const auto result = (static_cast<std::int64_t>(value) * numerator) / denominator;
|
||||
return static_cast<std::int32_t>(std::clamp<std::int64_t>(
|
||||
result, std::numeric_limits<std::int32_t>::min(),
|
||||
std::numeric_limits<std::int32_t>::max()));
|
||||
}
|
||||
|
||||
std::uint32_t unsigned_multiply_divide(const std::uint32_t value,
|
||||
const std::uint32_t numerator,
|
||||
const std::uint32_t denominator) noexcept {
|
||||
if (denominator == 0) {
|
||||
return std::numeric_limits<std::uint32_t>::max();
|
||||
}
|
||||
const auto result = (static_cast<std::uint64_t>(value) * numerator) / denominator;
|
||||
return static_cast<std::uint32_t>(std::min<std::uint64_t>(
|
||||
result, std::numeric_limits<std::uint32_t>::max()));
|
||||
}
|
||||
|
||||
double legacy_log(const double value) noexcept { return std::log(value); }
|
||||
double legacy_exp(const double value) noexcept { return std::exp(value); }
|
||||
double legacy_sqrt(const double value) noexcept { return std::sqrt(value); }
|
||||
double legacy_square(const double value) noexcept { return value * value; }
|
||||
|
||||
void fill_byte_pattern(void* destination, const std::size_t byte_count,
|
||||
const void* pattern, const std::size_t pattern_size) {
|
||||
if (byte_count == 0) {
|
||||
return;
|
||||
}
|
||||
if (destination == nullptr || pattern == nullptr || pattern_size == 0) {
|
||||
throw std::invalid_argument("fill_byte_pattern received an invalid buffer");
|
||||
}
|
||||
auto* output = static_cast<std::byte*>(destination);
|
||||
const auto* input = static_cast<const std::byte*>(pattern);
|
||||
for (std::size_t offset = 0; offset < byte_count; ++offset) {
|
||||
output[offset] = input[offset % pattern_size];
|
||||
}
|
||||
}
|
||||
|
||||
bool buffers_differ(const void* left, const void* right,
|
||||
const std::size_t byte_count) noexcept {
|
||||
if (byte_count == 0) {
|
||||
return false;
|
||||
}
|
||||
if (left == nullptr || right == nullptr) {
|
||||
return true;
|
||||
}
|
||||
return std::memcmp(left, right, byte_count) != 0;
|
||||
}
|
||||
|
||||
std::size_t zero_terminated_length(const char* text) noexcept {
|
||||
return text == nullptr ? 0 : std::strlen(text);
|
||||
}
|
||||
|
||||
std::size_t copy_zero_terminated(char* destination,
|
||||
const std::size_t destination_size,
|
||||
const char* source) noexcept {
|
||||
if (destination == nullptr || destination_size == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (source == nullptr) {
|
||||
destination[0] = '\0';
|
||||
return 0;
|
||||
}
|
||||
const auto count = std::min(destination_size - 1, std::strlen(source));
|
||||
std::memcpy(destination, source, count);
|
||||
destination[count] = '\0';
|
||||
return count;
|
||||
}
|
||||
|
||||
bool is_ascii_alpha(const std::uint8_t value) noexcept {
|
||||
const auto upper = ascii_upper(value);
|
||||
return upper >= 'A' && upper <= 'Z';
|
||||
}
|
||||
|
||||
bool is_ascii_digit(const std::uint8_t value) noexcept {
|
||||
return value >= '0' && value <= '9';
|
||||
}
|
||||
|
||||
bool is_ascii_alphanumeric(const std::uint8_t value) noexcept {
|
||||
return is_ascii_alpha(value) || is_ascii_digit(value);
|
||||
}
|
||||
|
||||
std::uint8_t ascii_upper(const std::uint8_t value) noexcept {
|
||||
return value >= 'a' && value <= 'z' ? static_cast<std::uint8_t>(value - ('a' - 'A'))
|
||||
: value;
|
||||
}
|
||||
|
||||
LegacyTime local_time() noexcept {
|
||||
SYSTEMTIME value{};
|
||||
GetLocalTime(&value);
|
||||
return {value.wHour, value.wMinute, value.wSecond,
|
||||
static_cast<std::uint16_t>(value.wMilliseconds / 10)};
|
||||
}
|
||||
|
||||
LegacyDate local_date() noexcept {
|
||||
SYSTEMTIME value{};
|
||||
GetLocalTime(&value);
|
||||
return {value.wYear, value.wMonth, value.wDay, value.wDayOfWeek};
|
||||
}
|
||||
|
||||
std::uint64_t available_disk_bytes(const std::wstring_view path) noexcept {
|
||||
ULARGE_INTEGER available{};
|
||||
const std::wstring owned_path(path);
|
||||
if (!GetDiskFreeSpaceExW(owned_path.c_str(), &available, nullptr, nullptr)) {
|
||||
return 0;
|
||||
}
|
||||
return available.QuadPart;
|
||||
}
|
||||
|
||||
bool is_remote_path(const std::wstring_view path) noexcept {
|
||||
if (path.starts_with(L"\\\\")) {
|
||||
return true;
|
||||
}
|
||||
if (path.size() < 2 || path[1] != L':') {
|
||||
return false;
|
||||
}
|
||||
wchar_t root[] = {path[0], L':', L'\\', L'\0'};
|
||||
return GetDriveTypeW(root) == DRIVE_REMOTE;
|
||||
}
|
||||
|
||||
bool assembly_manifest_complete() noexcept {
|
||||
for (std::size_t left = 0; left < kAssemblyPorts.size(); ++left) {
|
||||
if (kAssemblyPorts[left].path.empty()) {
|
||||
return false;
|
||||
}
|
||||
for (std::size_t right = left + 1; right < kAssemblyPorts.size(); ++right) {
|
||||
if (kAssemblyPorts[left].path == kAssemblyPorts[right].path) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::size_t assembly_manifest_count() noexcept { return kAssemblyPorts.size(); }
|
||||
|
||||
} // namespace opus::x64
|
||||
@@ -1,63 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace opus::x64 {
|
||||
|
||||
// Fixed-width replacements for assembly primitives that are still useful to
|
||||
// the native port. Higher-level Opus routines are replaced by the portable C
|
||||
// algorithms in Opus/wordtech as that document engine is migrated.
|
||||
|
||||
struct LegacyTime {
|
||||
std::uint16_t hour{};
|
||||
std::uint16_t minute{};
|
||||
std::uint16_t second{};
|
||||
std::uint16_t hundredths{};
|
||||
};
|
||||
|
||||
struct LegacyDate {
|
||||
std::uint16_t year{};
|
||||
std::uint16_t month{};
|
||||
std::uint16_t day{};
|
||||
std::uint16_t day_of_week{};
|
||||
};
|
||||
|
||||
[[nodiscard]] std::int32_t multiply_divide(std::int32_t value,
|
||||
std::int32_t numerator,
|
||||
std::int32_t denominator) noexcept;
|
||||
[[nodiscard]] std::uint32_t unsigned_multiply_divide(std::uint32_t value,
|
||||
std::uint32_t numerator,
|
||||
std::uint32_t denominator) noexcept;
|
||||
[[nodiscard]] double legacy_log(double value) noexcept;
|
||||
[[nodiscard]] double legacy_exp(double value) noexcept;
|
||||
[[nodiscard]] double legacy_sqrt(double value) noexcept;
|
||||
[[nodiscard]] double legacy_square(double value) noexcept;
|
||||
|
||||
void fill_byte_pattern(void* destination, std::size_t byte_count,
|
||||
const void* pattern, std::size_t pattern_size);
|
||||
[[nodiscard]] bool buffers_differ(const void* left, const void* right,
|
||||
std::size_t byte_count) noexcept;
|
||||
[[nodiscard]] std::size_t zero_terminated_length(const char* text) noexcept;
|
||||
[[nodiscard]] std::size_t copy_zero_terminated(char* destination,
|
||||
std::size_t destination_size,
|
||||
const char* source) noexcept;
|
||||
[[nodiscard]] bool is_ascii_alpha(std::uint8_t value) noexcept;
|
||||
[[nodiscard]] bool is_ascii_digit(std::uint8_t value) noexcept;
|
||||
[[nodiscard]] bool is_ascii_alphanumeric(std::uint8_t value) noexcept;
|
||||
[[nodiscard]] std::uint8_t ascii_upper(std::uint8_t value) noexcept;
|
||||
|
||||
[[nodiscard]] LegacyTime local_time() noexcept;
|
||||
[[nodiscard]] LegacyDate local_date() noexcept;
|
||||
[[nodiscard]] std::uint64_t available_disk_bytes(std::wstring_view path) noexcept;
|
||||
[[nodiscard]] bool is_remote_path(std::wstring_view path) noexcept;
|
||||
|
||||
// Returns true only if the checked-in migration table accounts for every
|
||||
// legacy .asm module. This is also run during application startup.
|
||||
[[nodiscard]] bool assembly_manifest_complete() noexcept;
|
||||
[[nodiscard]] std::size_t assembly_manifest_count() noexcept;
|
||||
|
||||
} // namespace opus::x64
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
typedef struct _CABABOUT
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
CHAR **hszAboutApp;
|
||||
CHAR **hszAboutVersion;
|
||||
CHAR **hszAboutCopyright;
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#define tmcUnassignKey (tmcUserMin + 9)
|
||||
#define tmcDescKey (tmcUserMin + 10)
|
||||
|
||||
#pragma pack(push, 2)
|
||||
typedef struct _CABCHANGEKEYS
|
||||
{
|
||||
CABH cabh;
|
||||
@@ -22,7 +21,6 @@ typedef struct _CABCHANGEKEYS
|
||||
unsigned uKeyList;
|
||||
int iContext;
|
||||
} CABCHANGEKEYS;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef CABCHANGEKEYS **HCABCHANGEKEYS;
|
||||
#define cabiCABCHANGEKEYS Cabi((sizeof(CABCHANGEKEYS) + sizeof(WORD) - 1) / sizeof(WORD), 1)
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#define tmcResetMenu (tmcUserMin + 9)
|
||||
#define tmcDescMenu (tmcUserMin + 10)
|
||||
|
||||
#pragma pack(push, 2)
|
||||
typedef struct _CABASSIGNTOMENU
|
||||
{
|
||||
CABH cabh;
|
||||
@@ -23,7 +22,6 @@ typedef struct _CABASSIGNTOMENU
|
||||
CHAR **hszTitle;
|
||||
int iContext;
|
||||
} CABASSIGNTOMENU;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef CABASSIGNTOMENU **HCABASSIGNTOMENU;
|
||||
#define cabiCABASSIGNTOMENU Cabi((sizeof(CABASSIGNTOMENU) + sizeof(WORD) - 1) / sizeof(WORD), 3)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
typedef struct _CABINSBOOKMARK
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
CHAR **hszBkmkName;
|
||||
} CABINSBOOKMARK;
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#define tmcCatDelete (tmcUserMin + 7)
|
||||
#define tmcCatSummary (tmcUserMin + 8)
|
||||
|
||||
#pragma pack(push, 2)
|
||||
typedef struct _CABCATALOG
|
||||
{
|
||||
CABH cabh;
|
||||
@@ -34,7 +33,6 @@ typedef struct _CABCATALOG
|
||||
BOOL fCatSrhMatchCase;
|
||||
BOOL fCatSrhRedo;
|
||||
} CABCATALOG;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef CABCATALOG **HCABCATALOG;
|
||||
#define cabiCABCATALOG Cabi((sizeof(CABCATALOG) + sizeof(WORD) - 1) / sizeof(WORD), 8)
|
||||
|
||||
@@ -6,13 +6,11 @@
|
||||
#define tmcCatSrhSearched (tmcUserMin + 2)
|
||||
#define tmcCatSrhFileInUse (tmcUserMin + 3)
|
||||
|
||||
#pragma pack(push, 2)
|
||||
typedef struct _CABCATSRHPROG
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
} CABCATSRHPROG;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef CABCATSRHPROG **HCABCATSRHPROG;
|
||||
#define cabiCABCATSRHPROG Cabi((sizeof(CABCATSRHPROG) + sizeof(WORD) - 1) / sizeof(WORD), 0)
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#define tmcCatSrhMatchCase (tmcUserMin + 11)
|
||||
#define tmcCatSrhRedo (tmcUserMin + 12)
|
||||
|
||||
#pragma pack(push, 2)
|
||||
typedef struct _CABCATSEARCH
|
||||
{
|
||||
CABH cabh;
|
||||
@@ -34,7 +33,6 @@ typedef struct _CABCATSEARCH
|
||||
BOOL fCatSrhMatchCase;
|
||||
BOOL fCatSrhRedo;
|
||||
} CABCATSEARCH;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef CABCATSEARCH **HCABCATSEARCH;
|
||||
#define cabiCABCATSEARCH Cabi((sizeof(CABCATSEARCH) + sizeof(WORD) - 1) / sizeof(WORD), 7)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
typedef struct _CABCHGPR
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
CHAR **hszLb;
|
||||
} CABCHGPR;
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
typedef struct _CABCONFIRMREPL
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
BOOL fConfirm;
|
||||
} CABCONFIRMREPL;
|
||||
|
||||
|
||||
@@ -22,13 +22,13 @@ typedef struct _CABCUSTOMIZE
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
CHAR **hszName;
|
||||
CHAR **hszInitials;
|
||||
int iAS;
|
||||
int iUnit;
|
||||
BOOL fBkgrndPag;
|
||||
BOOL fPromptSI;
|
||||
BOOL fAutoDelete;
|
||||
CHAR **hszName;
|
||||
CHAR **hszInitials;
|
||||
int cBtnFldClicks;
|
||||
} CABCUSTOMIZE;
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
typedef struct _CABDOCUMENT
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
CHAR **hszTemplate;
|
||||
unsigned uDocPageWidth;
|
||||
unsigned uDocPageHeight;
|
||||
unsigned uDocDefTabs;
|
||||
@@ -35,7 +37,6 @@ typedef struct _CABDOCUMENT
|
||||
int iFN;
|
||||
int iFNStartAt;
|
||||
BOOL fFNRestart;
|
||||
CHAR **hszTemplate;
|
||||
BOOL fWidowControl;
|
||||
} CABDOCUMENT;
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
typedef struct _CABDOCSTAT
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
CHAR **hszFName;
|
||||
CHAR **hszDir;
|
||||
CHAR **hszDOT;
|
||||
|
||||
@@ -17,10 +17,10 @@ typedef struct _CABEDMACRO
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
CHAR **hszName;
|
||||
int iContext;
|
||||
BOOL fShowAll;
|
||||
CHAR **hszDesc;
|
||||
CHAR **hszNewName;
|
||||
int iContext;
|
||||
BOOL fShowAll;
|
||||
} CABEDMACRO;
|
||||
|
||||
typedef CABEDMACRO **HCABEDMACRO;
|
||||
|
||||
@@ -26,9 +26,9 @@ typedef struct _CABDEFINESTYLE
|
||||
CHAR **hszDSStyle;
|
||||
CHAR **hszDSBasedOn;
|
||||
CHAR **hszDSNext;
|
||||
BOOL fTemplate;
|
||||
CHAR **hszDSNewName;
|
||||
CHAR **hszMergeStyle;
|
||||
BOOL fTemplate;
|
||||
unsigned uSource;
|
||||
} CABDEFINESTYLE;
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ typedef struct _CABINSERTFTN
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
BOOL fAuto;
|
||||
CHAR **hstRef;
|
||||
BOOL fAuto;
|
||||
} CABINSERTFTN;
|
||||
|
||||
typedef CABINSERTFTN **HCABINSERTFTN;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
typedef struct _CABGOTO
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
CHAR **hszGoto;
|
||||
} CABGOTO;
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#define tmcHypChange (tmcUserMin + 5)
|
||||
#define tmcHypNoChange (tmcUserMin + 6)
|
||||
|
||||
#pragma pack(push, 2)
|
||||
typedef struct _CABHYPHEN
|
||||
{
|
||||
CABH cabh;
|
||||
@@ -18,7 +17,6 @@ typedef struct _CABHYPHEN
|
||||
BOOL fHypCaps;
|
||||
BOOL fHypConfirm;
|
||||
} CABHYPHEN;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef CABHYPHEN **HCABHYPHEN;
|
||||
#define cabiCABHYPHEN Cabi((sizeof(CABHYPHEN) + sizeof(WORD) - 1) / sizeof(WORD), 1)
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#define tmcIndexBlank (tmcUserMin + 3)
|
||||
#define tmcIndexLetter (tmcUserMin + 4)
|
||||
|
||||
#pragma pack(push, 2)
|
||||
typedef struct _CABINDEX
|
||||
{
|
||||
CABH cabh;
|
||||
@@ -20,7 +19,6 @@ typedef struct _CABINDEX
|
||||
int iHeading;
|
||||
BOOL fReplace;
|
||||
} CABINDEX;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef CABINDEX **HCABINDEX;
|
||||
#define cabiCABINDEX Cabi((sizeof(CABINDEX) + sizeof(WORD) - 1) / sizeof(WORD), 0)
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#define tmcXeBold (tmcUserMin + 3)
|
||||
#define tmcXeItalic (tmcUserMin + 4)
|
||||
|
||||
#pragma pack(push, 2)
|
||||
typedef struct _CABINDEXENTRY
|
||||
{
|
||||
CABH cabh;
|
||||
@@ -16,7 +15,6 @@ typedef struct _CABINDEXENTRY
|
||||
BOOL fBold;
|
||||
BOOL fItalic;
|
||||
} CABINDEXENTRY;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef CABINDEXENTRY **HCABINDEXENTRY;
|
||||
#define cabiCABINDEXENTRY Cabi((sizeof(CABINDEXENTRY) + sizeof(WORD) - 1) / sizeof(WORD), 2)
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
typedef struct _CABINSBREAK
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
int iType;
|
||||
} CABINSBREAK;
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#define tmcIFldHelp (tmcUserMin + 4)
|
||||
#define tmcIFldAdd (tmcUserMin + 5)
|
||||
|
||||
#pragma pack(push, 2)
|
||||
typedef struct _CABINSFIELD
|
||||
{
|
||||
CABH cabh;
|
||||
@@ -17,7 +16,6 @@ typedef struct _CABINSFIELD
|
||||
unsigned uFlt;
|
||||
unsigned uInst;
|
||||
} CABINSFIELD;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef CABINSFIELD **HCABINSFIELD;
|
||||
#define cabiCABINSFIELD Cabi((sizeof(CABINSFIELD) + sizeof(WORD) - 1) / sizeof(WORD), 1)
|
||||
|
||||
@@ -9,9 +9,10 @@
|
||||
typedef struct _CABINSFILE
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
CHAR **hszFile;
|
||||
int iDirectory;
|
||||
CHAR **hszRange;
|
||||
int iDirectory;
|
||||
BOOL fLink;
|
||||
} CABINSFILE;
|
||||
|
||||
|
||||
@@ -6,14 +6,13 @@
|
||||
#define tmcIPDir (tmcUserMin + 2)
|
||||
#define tmcNewPic (tmcUserMin + 3)
|
||||
|
||||
#pragma pack(push, 2)
|
||||
typedef struct _CABINSPIC
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
CHAR **hszFile;
|
||||
int iDirectory;
|
||||
} CABINSPIC;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef CABINSPIC **HCABINSPIC;
|
||||
#define cabiCABINSPIC Cabi((sizeof(CABINSPIC) + sizeof(WORD) - 1) / sizeof(WORD), 1)
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
typedef struct _CABNEWDOC
|
||||
{
|
||||
CABH cabh;
|
||||
BOOL fNewDot;
|
||||
WORD sab;
|
||||
CHAR **hszNewType;
|
||||
BOOL fNewDot;
|
||||
} CABNEWDOC;
|
||||
|
||||
typedef CABNEWDOC **HCABNEWDOC;
|
||||
|
||||
@@ -1,2 +1,7 @@
|
||||
#pragma once
|
||||
static DLG_CONST DLT dltNewDoc = { 0 };
|
||||
/* Regenerated from Opus/dlg/new.des. */
|
||||
extern BOOL FDlgNew(DLM, TMC, WORD, WORD, WORD);
|
||||
static DLG_CONST DLT dltNewDoc = {
|
||||
{ 20, 24, 127, 114 }, IDDNewDoc, tmcNewType, FDlgNew,
|
||||
9, bdrSysMenu, { 0 }
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
typedef struct _CABNEWOPEN
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
CHAR **hszFileName;
|
||||
} CABNEWOPEN;
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
typedef struct _CABOPEN
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
CHAR **hszFile;
|
||||
int iDirectory;
|
||||
BOOL fReadOnly;
|
||||
|
||||
@@ -1,2 +1,7 @@
|
||||
#pragma once
|
||||
static DLG_CONST DLT dltOpen = { 0 };
|
||||
/* Regenerated from Opus/dlg/open.des. */
|
||||
extern BOOL FDlgOpen(DLM, TMC, WORD, WORD, WORD);
|
||||
static DLG_CONST DLT dltOpen = {
|
||||
{ 8, 24, 206, 104 }, IDDOpen, tmcOpenFileName, FDlgOpen,
|
||||
11, bdrSysMenu, { 0 }
|
||||
};
|
||||
|
||||
@@ -88,7 +88,7 @@ int DosxError() {
|
||||
error, static_cast<DWORD>((std::numeric_limits<int>::max)())));
|
||||
}
|
||||
|
||||
void* HpOfBptbExt(const int page_index) {
|
||||
unsigned char* HpOfBptbExt(const int page_index) {
|
||||
if (vbptbExt.pages == nullptr || page_index < 0 ||
|
||||
page_index >= vbptbExt.ibp_max) {
|
||||
return nullptr;
|
||||
@@ -97,7 +97,7 @@ void* HpOfBptbExt(const int page_index) {
|
||||
}
|
||||
|
||||
unsigned char* HpBaseForIbp(const int page_index) {
|
||||
return static_cast<unsigned char*>(HpOfBptbExt(page_index));
|
||||
return HpOfBptbExt(page_index);
|
||||
}
|
||||
|
||||
void OsTime(NativeTime* time) {
|
||||
|
||||
@@ -50,6 +50,7 @@ constexpr UINT kWmDdeData = 0x03e5;
|
||||
constexpr UINT kWmDdeRequest = 0x03e6;
|
||||
constexpr UINT kWmDdePoke = 0x03e7;
|
||||
constexpr UINT kWmDdeExecute = 0x03e8;
|
||||
constexpr UINT kWmOpusX64QuerySelection = WM_APP + 0x351;
|
||||
|
||||
enum class MouseCursor { none, ibeam, arrow };
|
||||
|
||||
@@ -94,7 +95,8 @@ constexpr UINT kMwdMessages[] = {
|
||||
constexpr UINT kWwPaneMessages[] = {
|
||||
WM_CREATE, WM_SIZE, WM_SETFOCUS, WM_KILLFOCUS, WM_PAINT,
|
||||
WM_MOUSEACTIVATE, WM_SETCURSOR, WM_MOUSEMOVE, WM_LBUTTONDOWN,
|
||||
WM_LBUTTONDBLCLK, WM_RBUTTONDOWN, WM_SYSCOMMAND, WM_MENUCHAR};
|
||||
WM_LBUTTONDBLCLK, WM_RBUTTONDOWN, WM_SYSCOMMAND, WM_MENUCHAR,
|
||||
kWmOpusX64QuerySelection};
|
||||
constexpr UINT kDdeMessages[] = {
|
||||
kWmDdeTerminate, kWmDdeAdvise, kWmDdeUnadvise, kWmDdeAck,
|
||||
kWmDdeData, kWmDdeRequest, kWmDdePoke, kWmDdeExecute};
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* Values are generated by Microsoft's original MKCMD implementation
|
||||
* from opuscmd.cmd and the checked-in ibcm.rel compatibility map. */
|
||||
if (bcmCut != 1686 || bcmCopy != 1702)
|
||||
/* Values are generated by Microsoft's original MKCMD parser from
|
||||
* opuscmd.cmd. On x64 these are offsets in the expanded native symbol
|
||||
* table; IBCM remains the original compatibility-map index. */
|
||||
if (bcmCut != 2252 || bcmCopy != 2274)
|
||||
return 1;
|
||||
if (ibcmEditCut != 86 || ibcmEditCopy != 87)
|
||||
return 2;
|
||||
if (bcmMacStd != 8295)
|
||||
if (bcmMacStd != 11247)
|
||||
return 3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -321,6 +321,7 @@ LONG WINAPI ObserveVectoredException(EXCEPTION_POINTERS* exception) {
|
||||
static volatile LONG writing_exception = 0;
|
||||
if (exception != nullptr && exception->ExceptionRecord != nullptr &&
|
||||
(exception->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION ||
|
||||
exception->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO ||
|
||||
exception->ExceptionRecord->ExceptionCode == 0xC0000374u ||
|
||||
exception->ExceptionRecord->ExceptionCode == 0xC0000409u ||
|
||||
exception->ExceptionRecord->ExceptionCode == 0xE0421001u) &&
|
||||
|
||||
@@ -28,6 +28,9 @@ constexpr std::size_t kWordBytes = sizeof(Word);
|
||||
constexpr std::size_t kCabMinWords =
|
||||
(sizeof(CabHeader) + sizeof(Word)) / kWordBytes;
|
||||
constexpr std::size_t kPointerWords = sizeof(void*) / kWordBytes;
|
||||
constexpr std::size_t kHandleBaseWords =
|
||||
(kCabMinWords + kPointerWords - 1) / kPointerWords * kPointerWords;
|
||||
constexpr std::size_t kHandleBaseIag = kHandleBaseWords - kCabMinWords;
|
||||
constexpr Word kNinch = 0xffff;
|
||||
|
||||
std::byte* cab_bytes(const CabHandle cab) noexcept {
|
||||
@@ -45,8 +48,9 @@ std::size_t cab_word_count(const CabHandle cab) noexcept {
|
||||
|
||||
void*** handle_slot(const CabHandle cab, const Word argument_index) noexcept {
|
||||
auto* const header = cab_header(cab);
|
||||
if (header == nullptr || argument_index >= header->handle_words ||
|
||||
argument_index % kPointerWords != 0) {
|
||||
if (header == nullptr || argument_index < kHandleBaseIag ||
|
||||
argument_index >= kHandleBaseIag + header->handle_words ||
|
||||
(argument_index - kHandleBaseIag) % kPointerWords != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<void***>(
|
||||
@@ -58,7 +62,8 @@ void clear_cab_data(const CabHandle cab) noexcept {
|
||||
if (header == nullptr) {
|
||||
return;
|
||||
}
|
||||
for (Word index = 0; index < header->handle_words;
|
||||
for (Word index = static_cast<Word>(kHandleBaseIag);
|
||||
index < kHandleBaseIag + header->handle_words;
|
||||
index = static_cast<Word>(index + kPointerWords)) {
|
||||
void*** const slot = handle_slot(cab, index);
|
||||
if (slot != nullptr && *slot != nullptr) {
|
||||
@@ -77,7 +82,8 @@ bool initialize_cab(const CabHandle cab, const Word initializer) noexcept {
|
||||
const std::size_t total_words = initializer & 0x00ffu;
|
||||
const std::size_t handle_count = initializer >> 8u;
|
||||
if (total_words < kCabMinWords || total_words > cab_word_count(cab) ||
|
||||
handle_count * kPointerWords > total_words - kCabMinWords) {
|
||||
kHandleBaseIag + handle_count * kPointerWords >
|
||||
total_words - kCabMinWords) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -149,7 +155,7 @@ void copy_sz(const CabHandle cab, char* destination,
|
||||
extern "C" {
|
||||
|
||||
CabHandle hcabDlgCur = nullptr;
|
||||
Word wRefDlgCur = 0;
|
||||
std::uintptr_t wRefDlgCur = 0;
|
||||
|
||||
CabHandle HcabAlloc_sdm21(const Word initializer) {
|
||||
const std::size_t total_words = initializer & 0x00ffu;
|
||||
@@ -193,7 +199,11 @@ void NinchCab(const CabHandle cab) {
|
||||
}
|
||||
clear_cab_data(cab);
|
||||
auto* words = reinterpret_cast<Word*>(cab_bytes(cab));
|
||||
for (Word index = header->handle_words; index < header->simple_words;
|
||||
const Word simple_first = header->handle_words == 0
|
||||
? 0
|
||||
: static_cast<Word>(kHandleBaseIag +
|
||||
header->handle_words);
|
||||
for (Word index = simple_first; index < header->simple_words;
|
||||
++index) {
|
||||
words[kCabMinWords + index] = kNinch;
|
||||
}
|
||||
@@ -312,13 +322,18 @@ CabHandle HcabDupeCab(const CabHandle source) {
|
||||
auto* const destination_header = cab_header(duplicate);
|
||||
auto* const source_words = reinterpret_cast<const Word*>(cab_bytes(source));
|
||||
auto* const destination_words = reinterpret_cast<Word*>(cab_bytes(duplicate));
|
||||
for (Word index = source_header->handle_words;
|
||||
const Word simple_first = source_header->handle_words == 0
|
||||
? 0
|
||||
: static_cast<Word>(kHandleBaseIag +
|
||||
source_header->handle_words);
|
||||
for (Word index = simple_first;
|
||||
index < source_header->simple_words; ++index) {
|
||||
destination_words[kCabMinWords + index] =
|
||||
source_words[kCabMinWords + index];
|
||||
}
|
||||
|
||||
for (Word index = 0; index < source_header->handle_words;
|
||||
for (Word index = static_cast<Word>(kHandleBaseIag);
|
||||
index < kHandleBaseIag + source_header->handle_words;
|
||||
index = static_cast<Word>(index + kPointerWords)) {
|
||||
void*** const source_slot = handle_slot(source, index);
|
||||
if (source_slot != nullptr && *source_slot != nullptr &&
|
||||
|
||||
@@ -22,8 +22,12 @@ CabHandle HcabDupeCab(CabHandle);
|
||||
|
||||
constexpr Word kCabMinWords = 3;
|
||||
constexpr Word kPointerWords = sizeof(void*) / sizeof(Word);
|
||||
constexpr Word kHandleBaseIag =
|
||||
((kCabMinWords + kPointerWords - 1) / kPointerWords * kPointerWords) -
|
||||
kCabMinWords;
|
||||
constexpr Word kHandleCount = 2;
|
||||
constexpr Word kArgumentWords = kHandleCount * kPointerWords + 2;
|
||||
constexpr Word kArgumentWords =
|
||||
kHandleBaseIag + kHandleCount * kPointerWords + 2;
|
||||
constexpr Word kTotalWords = kCabMinWords + kArgumentWords;
|
||||
constexpr Word kInitializer = kTotalWords | (kHandleCount << 8);
|
||||
|
||||
@@ -44,43 +48,46 @@ int main() {
|
||||
return fail(__LINE__);
|
||||
}
|
||||
|
||||
if (!FSetCabSz(cab, "Alpha", 0)) {
|
||||
if (!FSetCabSz(cab, "Alpha", kHandleBaseIag)) {
|
||||
return fail(__LINE__);
|
||||
}
|
||||
const unsigned char counted[] = {4, 'B', 'e', 't', 'a'};
|
||||
if (!FSetCabSt(cab, counted, kPointerWords)) {
|
||||
if (!FSetCabSt(cab, counted, kHandleBaseIag + kPointerWords)) {
|
||||
return fail(__LINE__);
|
||||
}
|
||||
|
||||
char sz[16]{};
|
||||
GetCabSz(cab, sz, sizeof(sz), 0);
|
||||
GetCabSz(cab, sz, sizeof(sz), kHandleBaseIag);
|
||||
if (std::strcmp(sz, "Alpha") != 0) {
|
||||
return fail(__LINE__);
|
||||
}
|
||||
unsigned char stz[16]{};
|
||||
GetCabStz(cab, stz, sizeof(stz), kPointerWords);
|
||||
GetCabStz(cab, stz, sizeof(stz), kHandleBaseIag + kPointerWords);
|
||||
if (stz[0] != 4 || std::memcmp(stz + 1, "Beta", 5) != 0) {
|
||||
return fail(__LINE__);
|
||||
}
|
||||
|
||||
auto* mutable_words = static_cast<Word*>(OpusDerefH(cab));
|
||||
mutable_words[kCabMinWords + kHandleCount * kPointerWords] = 42;
|
||||
mutable_words[kCabMinWords + kHandleBaseIag +
|
||||
kHandleCount * kPointerWords] = 42;
|
||||
CabHandle const duplicate = HcabDupeCab(cab);
|
||||
if (duplicate == nullptr) {
|
||||
return fail(__LINE__);
|
||||
}
|
||||
GetCabSz(duplicate, sz, sizeof(sz), 0);
|
||||
GetCabSz(duplicate, sz, sizeof(sz), kHandleBaseIag);
|
||||
if (std::strcmp(sz, "Alpha") != 0 ||
|
||||
static_cast<Word*>(OpusDerefH(duplicate))
|
||||
[kCabMinWords + kHandleCount * kPointerWords] != 42) {
|
||||
[kCabMinWords + kHandleBaseIag +
|
||||
kHandleCount * kPointerWords] != 42) {
|
||||
return fail(__LINE__);
|
||||
}
|
||||
|
||||
NinchCab(cab);
|
||||
GetCabSz(cab, sz, sizeof(sz), 0);
|
||||
GetCabSz(cab, sz, sizeof(sz), kHandleBaseIag);
|
||||
mutable_words = static_cast<Word*>(OpusDerefH(cab));
|
||||
if (sz[0] != '\0' ||
|
||||
mutable_words[kCabMinWords + kHandleCount * kPointerWords] !=
|
||||
mutable_words[kCabMinWords + kHandleBaseIag +
|
||||
kHandleCount * kPointerWords] !=
|
||||
0xffffu) {
|
||||
return fail(__LINE__);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "opus_x64_compat.h"
|
||||
#include "opus_x64_heap.h"
|
||||
extern "C" {
|
||||
#include "dac.h"
|
||||
}
|
||||
@@ -13,7 +14,10 @@ extern "C" {
|
||||
|
||||
extern "C" {
|
||||
extern void** hcabDlgCur;
|
||||
extern std::uint16_t wRefDlgCur;
|
||||
extern std::uintptr_t wRefDlgCur;
|
||||
extern HWND vhWndMsgBoxParent;
|
||||
void GetCabSz(void**, char*, std::uint16_t, std::uint16_t);
|
||||
int FSetCabSz(void**, const char*, std::uint16_t);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -53,7 +57,7 @@ struct Dli {
|
||||
int dx;
|
||||
int dy;
|
||||
Dword flags;
|
||||
Word reference;
|
||||
std::uintptr_t reference;
|
||||
unsigned char* runtime_items;
|
||||
};
|
||||
|
||||
@@ -75,15 +79,21 @@ struct DialogState {
|
||||
DltHeader** template_handle = nullptr;
|
||||
Hcab cab = nullptr;
|
||||
HWND window = nullptr;
|
||||
Word reference = 0;
|
||||
std::uintptr_t reference = 0;
|
||||
Word hid = 0;
|
||||
Word sab = 0;
|
||||
Tmc focus = 0;
|
||||
Tmc default_tmc = 1;
|
||||
Tmc result_tmc = 2;
|
||||
bool visible = false;
|
||||
bool dying = false;
|
||||
bool modal = false;
|
||||
bool native_modal = false;
|
||||
bool commands_active = false;
|
||||
std::string caption;
|
||||
std::string current_directory;
|
||||
std::string file_pattern;
|
||||
HWND directory_label = nullptr;
|
||||
std::unordered_map<Tmc, ControlState> controls;
|
||||
};
|
||||
|
||||
@@ -95,7 +105,39 @@ Hdlg g_focus_dialog = 0;
|
||||
bool g_initialized = false;
|
||||
bool g_noninteractive = false;
|
||||
|
||||
constexpr Word kDlmInit = 0x0001;
|
||||
constexpr Word kDlmTerm = 0x0003;
|
||||
constexpr Word kDlmExit = 0x0004;
|
||||
constexpr Word kDlmChange = 0x0005;
|
||||
constexpr Word kDlmClick = 0x0006;
|
||||
constexpr Word kDlmDblClk = 0x0007;
|
||||
constexpr Word kIddNewDoc = 2;
|
||||
constexpr Word kIddOpen = 3;
|
||||
constexpr Tmc kTmcOk = 1;
|
||||
constexpr Tmc kTmcCancel = 2;
|
||||
constexpr Tmc kTmcSummary = 0x0400;
|
||||
constexpr Tmc kTmcNewDot = 0x0401;
|
||||
constexpr Tmc kTmcRNewDoc = 0x0402;
|
||||
constexpr Tmc kTmcRNewDot = 0x0403;
|
||||
constexpr Tmc kTmcNewType = 0x0404;
|
||||
constexpr Tmc kTmcNewTypeList = 0x0405;
|
||||
constexpr Tmc kTmcOpenFileName = 0x0400;
|
||||
constexpr Tmc kTmcOpenFileList = 0x0401;
|
||||
constexpr Tmc kTmcOpenFileDir = 0x0402;
|
||||
constexpr Tmc kTmcOpenCatalog = 0x0403;
|
||||
constexpr Tmc kTmcOpenReadOnly = 0x0404;
|
||||
constexpr Word kOpenFileNameIag = 1;
|
||||
constexpr std::size_t kOpenCabBytes = 24;
|
||||
constexpr std::size_t kOpenReadOnlyOffset = 20;
|
||||
constexpr Word kNewTypeIag = 1;
|
||||
constexpr std::size_t kNewCabBytes = 24;
|
||||
constexpr std::size_t kNewDotOffset = 16;
|
||||
|
||||
using NativeDialogProc = int (*)(Word, Tmc, Word, Word, Word);
|
||||
|
||||
void set_sds_handle(Hdlg current, Hdlg focus);
|
||||
LRESULT CALLBACK native_dialog_window_proc(HWND, UINT, WPARAM, LPARAM);
|
||||
void handle_dialog_command(Hdlg, WPARAM, LPARAM);
|
||||
|
||||
DialogState* find_dialog(const Hdlg dialog) {
|
||||
const auto found = g_dialogs.find(dialog);
|
||||
@@ -134,7 +176,76 @@ int scaled_y(const int value) {
|
||||
return MulDiv(value, dac.dySysFontChar == 0 ? 16 : dac.dySysFontChar, 8);
|
||||
}
|
||||
|
||||
ATOM ensure_native_dialog_class() {
|
||||
static ATOM atom = 0;
|
||||
if (atom != 0) {
|
||||
return atom;
|
||||
}
|
||||
WNDCLASSEXA cls{};
|
||||
cls.cbSize = sizeof(cls);
|
||||
cls.style = CS_DBLCLKS;
|
||||
cls.lpfnWndProc = native_dialog_window_proc;
|
||||
cls.hInstance = GetModuleHandleW(nullptr);
|
||||
cls.hCursor = LoadCursorA(nullptr, IDC_ARROW);
|
||||
cls.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_BTNFACE + 1);
|
||||
cls.lpszClassName = "OpusSdmDialog";
|
||||
atom = RegisterClassExA(&cls);
|
||||
if (atom == 0 && GetLastError() == ERROR_CLASS_ALREADY_EXISTS) {
|
||||
atom = 1;
|
||||
}
|
||||
return atom;
|
||||
}
|
||||
|
||||
HWND create_dialog_host(const DialogState& dialog, const Dli* initializer) {
|
||||
if (dialog.modal &&
|
||||
(dialog.hid == kIddOpen || dialog.hid == kIddNewDoc)) {
|
||||
if (ensure_native_dialog_class() == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HWND owner = initializer != nullptr ? initializer->hwnd : nullptr;
|
||||
if (owner == nullptr || !IsWindow(owner)) {
|
||||
owner = vhWndMsgBoxParent;
|
||||
}
|
||||
if (owner == nullptr || !IsWindow(owner)) {
|
||||
owner = GetActiveWindow();
|
||||
}
|
||||
|
||||
int client_width = scaled_x(dialog.hid == kIddNewDoc ? 127 : 206);
|
||||
int client_height = scaled_y(dialog.hid == kIddNewDoc ? 114 : 104);
|
||||
if (dialog.template_handle != nullptr &&
|
||||
*dialog.template_handle != nullptr) {
|
||||
client_width = (std::max)(1, scaled_x(
|
||||
(*dialog.template_handle)->rec.dx));
|
||||
client_height = (std::max)(1, scaled_y(
|
||||
(*dialog.template_handle)->rec.dy));
|
||||
}
|
||||
constexpr DWORD style =
|
||||
WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_CLIPCHILDREN;
|
||||
constexpr DWORD extended_style =
|
||||
WS_EX_DLGMODALFRAME | WS_EX_CONTROLPARENT;
|
||||
RECT window_rect{0, 0, client_width, client_height};
|
||||
AdjustWindowRectEx(&window_rect, style, false, extended_style);
|
||||
const int width = window_rect.right - window_rect.left;
|
||||
const int height = window_rect.bottom - window_rect.top;
|
||||
|
||||
RECT anchor{};
|
||||
if (owner == nullptr || !GetWindowRect(owner, &anchor)) {
|
||||
SystemParametersInfoA(SPI_GETWORKAREA, 0, &anchor, 0);
|
||||
}
|
||||
const int x = anchor.left +
|
||||
(anchor.right - anchor.left - width) / 2;
|
||||
const int y = anchor.top +
|
||||
(anchor.bottom - anchor.top - height) / 2;
|
||||
const char* const caption =
|
||||
dialog.hid == kIddNewDoc ? "New" : "Open";
|
||||
return CreateWindowExA(
|
||||
extended_style, "OpusSdmDialog", caption, style, x, y, width,
|
||||
height, owner, nullptr, GetModuleHandleW(nullptr),
|
||||
reinterpret_cast<void*>(static_cast<std::uintptr_t>(
|
||||
dialog.handle)));
|
||||
}
|
||||
|
||||
if (initializer == nullptr || initializer->hwnd == nullptr ||
|
||||
!IsWindow(initializer->hwnd)) {
|
||||
return nullptr;
|
||||
@@ -208,6 +319,377 @@ void create_static_text(DialogState& dialog, const char* caption,
|
||||
}
|
||||
}
|
||||
|
||||
void create_untracked_control(DialogState& dialog, const char* window_class,
|
||||
const char* caption, const Rec& rectangle,
|
||||
const DWORD control_style) {
|
||||
if (dialog.window == nullptr || !IsWindow(dialog.window)) {
|
||||
return;
|
||||
}
|
||||
const HWND window = CreateWindowExA(
|
||||
0, window_class, caption,
|
||||
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | control_style,
|
||||
scaled_x(rectangle.x), scaled_y(rectangle.y),
|
||||
(std::max)(1, scaled_x(rectangle.dx)),
|
||||
(std::max)(1, scaled_y(rectangle.dy)), dialog.window, nullptr,
|
||||
GetModuleHandleW(nullptr), nullptr);
|
||||
if (window != nullptr) {
|
||||
SendMessageA(window, WM_SETFONT,
|
||||
reinterpret_cast<WPARAM>(GetStockObject(DEFAULT_GUI_FONT)),
|
||||
false);
|
||||
}
|
||||
}
|
||||
|
||||
std::string join_path(const std::string& directory,
|
||||
const std::string& leaf) {
|
||||
if (directory.empty()) {
|
||||
return leaf;
|
||||
}
|
||||
const char last = directory.back();
|
||||
return directory + (last == '\\' || last == '/' ? "" : "\\") + leaf;
|
||||
}
|
||||
|
||||
void add_native_list_entry(DialogState& dialog, const Tmc tmc,
|
||||
const std::string& entry) {
|
||||
auto& state = dialog.controls[tmc];
|
||||
state.entries.push_back(entry);
|
||||
if (state.window != nullptr && IsWindow(state.window)) {
|
||||
SendMessageA(state.window, LB_ADDSTRING, 0,
|
||||
reinterpret_cast<LPARAM>(entry.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
void set_open_directory_label(DialogState& dialog) {
|
||||
if (dialog.directory_label != nullptr &&
|
||||
IsWindow(dialog.directory_label)) {
|
||||
SetWindowTextA(dialog.directory_label,
|
||||
dialog.current_directory.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void establish_open_directory(DialogState& dialog,
|
||||
const std::string& specification) {
|
||||
char full_path[32768] = {};
|
||||
char* file_part = nullptr;
|
||||
const std::string source = specification.empty() ? "*.*" : specification;
|
||||
const DWORD length = GetFullPathNameA(
|
||||
source.c_str(), static_cast<DWORD>(sizeof(full_path)), full_path,
|
||||
&file_part);
|
||||
if (length == 0 || length >= sizeof(full_path)) {
|
||||
GetCurrentDirectoryA(static_cast<DWORD>(sizeof(full_path)), full_path);
|
||||
dialog.current_directory = full_path;
|
||||
dialog.file_pattern = "*.*";
|
||||
return;
|
||||
}
|
||||
|
||||
const DWORD attributes = GetFileAttributesA(full_path);
|
||||
if (attributes != INVALID_FILE_ATTRIBUTES &&
|
||||
(attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
|
||||
dialog.current_directory = full_path;
|
||||
dialog.file_pattern = "*.*";
|
||||
return;
|
||||
}
|
||||
|
||||
dialog.file_pattern =
|
||||
file_part == nullptr || *file_part == '\0' ? "*.*" : file_part;
|
||||
if (file_part == nullptr || file_part == full_path) {
|
||||
GetCurrentDirectoryA(static_cast<DWORD>(sizeof(full_path)), full_path);
|
||||
dialog.current_directory = full_path;
|
||||
} else {
|
||||
std::size_t directory_length =
|
||||
static_cast<std::size_t>(file_part - full_path);
|
||||
while (directory_length > 3 &&
|
||||
(full_path[directory_length - 1] == '\\' ||
|
||||
full_path[directory_length - 1] == '/')) {
|
||||
--directory_length;
|
||||
}
|
||||
dialog.current_directory.assign(full_path, directory_length);
|
||||
}
|
||||
}
|
||||
|
||||
void populate_open_lists(DialogState& dialog) {
|
||||
auto& files = dialog.controls[kTmcOpenFileList];
|
||||
auto& directories = dialog.controls[kTmcOpenFileDir];
|
||||
files.entries.clear();
|
||||
directories.entries.clear();
|
||||
if (files.window != nullptr) {
|
||||
SendMessageA(files.window, LB_RESETCONTENT, 0, 0);
|
||||
}
|
||||
if (directories.window != nullptr) {
|
||||
SendMessageA(directories.window, LB_RESETCONTENT, 0, 0);
|
||||
}
|
||||
|
||||
WIN32_FIND_DATAA find_data{};
|
||||
HANDLE find = FindFirstFileA(
|
||||
join_path(dialog.current_directory, dialog.file_pattern).c_str(),
|
||||
&find_data);
|
||||
if (find != INVALID_HANDLE_VALUE) {
|
||||
do {
|
||||
if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
|
||||
add_native_list_entry(dialog, kTmcOpenFileList,
|
||||
find_data.cFileName);
|
||||
}
|
||||
} while (FindNextFileA(find, &find_data));
|
||||
FindClose(find);
|
||||
}
|
||||
|
||||
char parent_path[32768] = {};
|
||||
const std::string parent_spec = join_path(dialog.current_directory, "..");
|
||||
if (GetFullPathNameA(parent_spec.c_str(),
|
||||
static_cast<DWORD>(sizeof(parent_path)), parent_path,
|
||||
nullptr) != 0 &&
|
||||
_stricmp(parent_path, dialog.current_directory.c_str()) != 0) {
|
||||
add_native_list_entry(dialog, kTmcOpenFileDir, "[..]");
|
||||
}
|
||||
find = FindFirstFileA(join_path(dialog.current_directory, "*.*").c_str(),
|
||||
&find_data);
|
||||
if (find != INVALID_HANDLE_VALUE) {
|
||||
do {
|
||||
if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 &&
|
||||
std::strcmp(find_data.cFileName, ".") != 0 &&
|
||||
std::strcmp(find_data.cFileName, "..") != 0) {
|
||||
add_native_list_entry(
|
||||
dialog, kTmcOpenFileDir,
|
||||
"[" + std::string(find_data.cFileName) + "]");
|
||||
}
|
||||
} while (FindNextFileA(find, &find_data));
|
||||
FindClose(find);
|
||||
}
|
||||
set_open_directory_label(dialog);
|
||||
}
|
||||
|
||||
void read_open_cab(DialogState& dialog) {
|
||||
char file_name[32768] = {};
|
||||
if (dialog.cab != nullptr) {
|
||||
GetCabSz(dialog.cab, file_name,
|
||||
static_cast<Word>((std::min)(sizeof(file_name),
|
||||
std::size_t{0xffffu})),
|
||||
kOpenFileNameIag);
|
||||
}
|
||||
auto& edit = dialog.controls[kTmcOpenFileName];
|
||||
edit.text = file_name;
|
||||
if (edit.window != nullptr) {
|
||||
SetWindowTextA(edit.window, edit.text.c_str());
|
||||
}
|
||||
|
||||
if (dialog.cab != nullptr && *dialog.cab != nullptr &&
|
||||
OpusCbOfH(dialog.cab) >= kOpenCabBytes) {
|
||||
int read_only = 0;
|
||||
std::memcpy(&read_only,
|
||||
static_cast<const unsigned char*>(*dialog.cab) +
|
||||
kOpenReadOnlyOffset,
|
||||
sizeof(read_only));
|
||||
auto& checkbox = dialog.controls[kTmcOpenReadOnly];
|
||||
checkbox.value = read_only != 0;
|
||||
if (checkbox.window != nullptr) {
|
||||
SendMessageA(checkbox.window, BM_SETCHECK,
|
||||
checkbox.value ? BST_CHECKED : BST_UNCHECKED, 0);
|
||||
}
|
||||
}
|
||||
establish_open_directory(dialog, edit.text);
|
||||
populate_open_lists(dialog);
|
||||
}
|
||||
|
||||
void sync_open_cab(DialogState& dialog) {
|
||||
auto edit = dialog.controls.find(kTmcOpenFileName);
|
||||
if (edit != dialog.controls.end() && edit->second.window != nullptr) {
|
||||
const int length = GetWindowTextLengthA(edit->second.window);
|
||||
std::vector<char> buffer(static_cast<std::size_t>(length) + 1);
|
||||
GetWindowTextA(edit->second.window, buffer.data(),
|
||||
static_cast<int>(buffer.size()));
|
||||
edit->second.text = buffer.data();
|
||||
}
|
||||
if (dialog.cab != nullptr && edit != dialog.controls.end()) {
|
||||
FSetCabSz(dialog.cab, edit->second.text.c_str(), kOpenFileNameIag);
|
||||
}
|
||||
|
||||
auto checkbox = dialog.controls.find(kTmcOpenReadOnly);
|
||||
if (checkbox != dialog.controls.end() && checkbox->second.window != nullptr) {
|
||||
checkbox->second.value = static_cast<Word>(
|
||||
SendMessageA(checkbox->second.window, BM_GETCHECK, 0, 0) ==
|
||||
BST_CHECKED);
|
||||
}
|
||||
if (dialog.cab != nullptr && *dialog.cab != nullptr &&
|
||||
OpusCbOfH(dialog.cab) >= kOpenCabBytes &&
|
||||
checkbox != dialog.controls.end()) {
|
||||
const int read_only = checkbox->second.value != 0;
|
||||
std::memcpy(static_cast<unsigned char*>(*dialog.cab) +
|
||||
kOpenReadOnlyOffset,
|
||||
&read_only, sizeof(read_only));
|
||||
}
|
||||
}
|
||||
|
||||
void populate_new_type_list(DialogState& dialog) {
|
||||
auto& list = dialog.controls[kTmcNewTypeList];
|
||||
list.entries.clear();
|
||||
if (list.window != nullptr) {
|
||||
SendMessageA(list.window, LB_RESETCONTENT, 0, 0);
|
||||
}
|
||||
|
||||
const std::string initial = dialog.controls[kTmcNewType].text;
|
||||
add_native_list_entry(dialog, kTmcNewTypeList,
|
||||
initial.empty() ? "NORMAL" : initial);
|
||||
|
||||
WIN32_FIND_DATAA find_data{};
|
||||
HANDLE find = FindFirstFileA("*.DOT", &find_data);
|
||||
if (find == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
}
|
||||
do {
|
||||
if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
|
||||
continue;
|
||||
}
|
||||
const auto duplicate = std::find_if(
|
||||
list.entries.begin(), list.entries.end(),
|
||||
[&find_data](const std::string& entry) {
|
||||
return _stricmp(entry.c_str(), find_data.cFileName) == 0;
|
||||
});
|
||||
if (duplicate == list.entries.end()) {
|
||||
add_native_list_entry(dialog, kTmcNewTypeList,
|
||||
find_data.cFileName);
|
||||
}
|
||||
} while (FindNextFileA(find, &find_data));
|
||||
FindClose(find);
|
||||
}
|
||||
|
||||
void read_new_cab(DialogState& dialog) {
|
||||
char type_name[32768] = {};
|
||||
if (dialog.cab != nullptr) {
|
||||
GetCabSz(dialog.cab, type_name,
|
||||
static_cast<Word>((std::min)(sizeof(type_name),
|
||||
std::size_t{0xffffu})),
|
||||
kNewTypeIag);
|
||||
}
|
||||
auto& edit = dialog.controls[kTmcNewType];
|
||||
edit.text = type_name;
|
||||
if (edit.window != nullptr) {
|
||||
SetWindowTextA(edit.window, edit.text.c_str());
|
||||
}
|
||||
|
||||
bool new_template = false;
|
||||
if (dialog.cab != nullptr && *dialog.cab != nullptr &&
|
||||
OpusCbOfH(dialog.cab) >= kNewCabBytes) {
|
||||
int value = 0;
|
||||
std::memcpy(&value,
|
||||
static_cast<const unsigned char*>(*dialog.cab) +
|
||||
kNewDotOffset,
|
||||
sizeof(value));
|
||||
new_template = value != 0;
|
||||
}
|
||||
dialog.controls[kTmcNewDot].value = new_template;
|
||||
dialog.controls[kTmcRNewDoc].value = !new_template;
|
||||
dialog.controls[kTmcRNewDot].value = new_template;
|
||||
if (dialog.controls[kTmcRNewDoc].window != nullptr) {
|
||||
SendMessageA(dialog.controls[kTmcRNewDoc].window, BM_SETCHECK,
|
||||
new_template ? BST_UNCHECKED : BST_CHECKED, 0);
|
||||
}
|
||||
if (dialog.controls[kTmcRNewDot].window != nullptr) {
|
||||
SendMessageA(dialog.controls[kTmcRNewDot].window, BM_SETCHECK,
|
||||
new_template ? BST_CHECKED : BST_UNCHECKED, 0);
|
||||
}
|
||||
populate_new_type_list(dialog);
|
||||
}
|
||||
|
||||
void sync_new_cab(DialogState& dialog) {
|
||||
auto edit = dialog.controls.find(kTmcNewType);
|
||||
if (edit != dialog.controls.end() && edit->second.window != nullptr) {
|
||||
const int length = GetWindowTextLengthA(edit->second.window);
|
||||
std::vector<char> buffer(static_cast<std::size_t>(length) + 1);
|
||||
GetWindowTextA(edit->second.window, buffer.data(),
|
||||
static_cast<int>(buffer.size()));
|
||||
edit->second.text = buffer.data();
|
||||
}
|
||||
if (dialog.cab != nullptr && edit != dialog.controls.end()) {
|
||||
FSetCabSz(dialog.cab, edit->second.text.c_str(), kNewTypeIag);
|
||||
}
|
||||
|
||||
bool new_template = false;
|
||||
const auto radio = dialog.controls.find(kTmcRNewDot);
|
||||
if (radio != dialog.controls.end() && radio->second.window != nullptr) {
|
||||
new_template = SendMessageA(radio->second.window, BM_GETCHECK, 0, 0) ==
|
||||
BST_CHECKED;
|
||||
}
|
||||
dialog.controls[kTmcNewDot].value = new_template;
|
||||
if (dialog.cab != nullptr && *dialog.cab != nullptr &&
|
||||
OpusCbOfH(dialog.cab) >= kNewCabBytes) {
|
||||
const int value = new_template;
|
||||
std::memcpy(static_cast<unsigned char*>(*dialog.cab) + kNewDotOffset,
|
||||
&value, sizeof(value));
|
||||
}
|
||||
}
|
||||
|
||||
void materialize_new_template(DialogState& dialog) {
|
||||
if (dialog.hid != kIddNewDoc || dialog.window == nullptr) {
|
||||
return;
|
||||
}
|
||||
create_native_control(dialog, kTmcOk, "BUTTON", "OK", {74, 6, 46, 14},
|
||||
WS_TABSTOP | BS_DEFPUSHBUTTON);
|
||||
create_native_control(dialog, kTmcCancel, "BUTTON", "Cancel",
|
||||
{74, 23, 46, 14}, WS_TABSTOP | BS_PUSHBUTTON);
|
||||
create_native_control(dialog, kTmcSummary, "BUTTON", "&Summary...",
|
||||
{74, 39, 46, 14}, WS_TABSTOP | BS_PUSHBUTTON);
|
||||
create_untracked_control(dialog, "BUTTON", "New", {5, 2, 61, 36},
|
||||
BS_GROUPBOX);
|
||||
create_native_control(dialog, kTmcRNewDoc, "BUTTON", "&Document",
|
||||
{9, 12, 42, 10},
|
||||
WS_TABSTOP | WS_GROUP | BS_AUTORADIOBUTTON);
|
||||
create_native_control(dialog, kTmcRNewDot, "BUTTON", "&Template",
|
||||
{9, 24, 42, 10},
|
||||
WS_TABSTOP | BS_AUTORADIOBUTTON);
|
||||
create_static_text(dialog, "&Use Template:", {5, 42, 53, 10});
|
||||
create_native_control(dialog, kTmcNewType, "EDIT", "",
|
||||
{5, 53, 61, 12},
|
||||
WS_TABSTOP | WS_BORDER | ES_AUTOHSCROLL);
|
||||
create_native_control(dialog, kTmcNewTypeList, "LISTBOX", "",
|
||||
{8, 65, 68, 40},
|
||||
WS_TABSTOP | WS_BORDER | WS_VSCROLL | LBS_NOTIFY |
|
||||
LBS_SORT | LBS_NOINTEGRALHEIGHT);
|
||||
dialog.caption = "New";
|
||||
dialog.native_modal = true;
|
||||
read_new_cab(dialog);
|
||||
}
|
||||
|
||||
void materialize_open_template(DialogState& dialog) {
|
||||
if (dialog.hid != kIddOpen || dialog.window == nullptr) {
|
||||
return;
|
||||
}
|
||||
create_native_control(dialog, kTmcOpenFileName, "EDIT", "",
|
||||
{67, 6, 83, 12},
|
||||
WS_TABSTOP | WS_BORDER | ES_AUTOHSCROLL);
|
||||
create_static_text(dialog, "Open File &Name:", {5, 7, 61, 9});
|
||||
create_native_control(dialog, kTmcOpenFileList, "LISTBOX", "",
|
||||
{5, 32, 60, 64},
|
||||
WS_TABSTOP | WS_BORDER | WS_VSCROLL | LBS_NOTIFY |
|
||||
LBS_SORT | LBS_NOINTEGRALHEIGHT);
|
||||
create_static_text(dialog, "&Files:", {5, 21, 25, 9});
|
||||
create_native_control(dialog, kTmcOpenFileDir, "LISTBOX", "",
|
||||
{71, 48, 65, 48},
|
||||
WS_TABSTOP | WS_BORDER | WS_VSCROLL | LBS_NOTIFY |
|
||||
LBS_SORT | LBS_NOINTEGRALHEIGHT);
|
||||
create_static_text(dialog, "&Directories:", {71, 38, 50, 9});
|
||||
dialog.directory_label = CreateWindowExA(
|
||||
0, "STATIC", "", WS_CHILD | WS_VISIBLE | SS_LEFT,
|
||||
scaled_x(70), scaled_y(24), scaled_x(78), scaled_y(8),
|
||||
dialog.window, nullptr, GetModuleHandleW(nullptr), nullptr);
|
||||
if (dialog.directory_label != nullptr) {
|
||||
SendMessageA(dialog.directory_label, WM_SETFONT,
|
||||
reinterpret_cast<WPARAM>(GetStockObject(DEFAULT_GUI_FONT)),
|
||||
false);
|
||||
}
|
||||
create_native_control(dialog, kTmcOk, "BUTTON", "OK",
|
||||
{154, 5, 47, 14},
|
||||
WS_TABSTOP | BS_DEFPUSHBUTTON);
|
||||
create_native_control(dialog, kTmcCancel, "BUTTON", "Cancel",
|
||||
{154, 22, 47, 14}, WS_TABSTOP | BS_PUSHBUTTON);
|
||||
create_native_control(dialog, kTmcOpenCatalog, "BUTTON", "F&ind...",
|
||||
{154, 39, 47, 14}, WS_TABSTOP | BS_PUSHBUTTON);
|
||||
create_native_control(dialog, kTmcOpenReadOnly, "BUTTON", "&Read Only",
|
||||
{155, 56, 47, 12},
|
||||
WS_TABSTOP | BS_AUTOCHECKBOX);
|
||||
dialog.caption = "Open";
|
||||
dialog.native_modal = true;
|
||||
read_open_cab(dialog);
|
||||
}
|
||||
|
||||
void materialize_icon_bar_template(DialogState& dialog) {
|
||||
constexpr Word cxtRibbonIconBar = 0x8005;
|
||||
constexpr Word cxtRulerIconBar = 0x8006;
|
||||
@@ -290,6 +772,244 @@ std::string counted_or_zero_terminated(const char* text) {
|
||||
return std::string(text);
|
||||
}
|
||||
|
||||
bool invoke_dialog_proc(DialogState& dialog, const Word message,
|
||||
const Tmc tmc, const Word new_value = 0,
|
||||
const Word old_value = 0,
|
||||
const Word parameter = 0) {
|
||||
if (dialog.template_handle == nullptr ||
|
||||
*dialog.template_handle == nullptr ||
|
||||
(*dialog.template_handle)->dialog_proc == nullptr) {
|
||||
return true;
|
||||
}
|
||||
const auto proc = reinterpret_cast<NativeDialogProc>(
|
||||
(*dialog.template_handle)->dialog_proc);
|
||||
return proc(message, tmc, new_value, old_value, parameter) != 0;
|
||||
}
|
||||
|
||||
std::string selected_list_text(const ControlState& control_state) {
|
||||
if (control_state.window == nullptr) {
|
||||
return {};
|
||||
}
|
||||
const LRESULT selection =
|
||||
SendMessageA(control_state.window, LB_GETCURSEL, 0, 0);
|
||||
if (selection == LB_ERR) {
|
||||
return {};
|
||||
}
|
||||
const LRESULT length =
|
||||
SendMessageA(control_state.window, LB_GETTEXTLEN, selection, 0);
|
||||
if (length == LB_ERR) {
|
||||
return {};
|
||||
}
|
||||
std::vector<char> text(static_cast<std::size_t>(length) + 1);
|
||||
SendMessageA(control_state.window, LB_GETTEXT, selection,
|
||||
reinterpret_cast<LPARAM>(text.data()));
|
||||
return text.data();
|
||||
}
|
||||
|
||||
void select_new_type(DialogState& dialog) {
|
||||
const auto found = dialog.controls.find(kTmcNewTypeList);
|
||||
if (found == dialog.controls.end()) {
|
||||
return;
|
||||
}
|
||||
const std::string selected = selected_list_text(found->second);
|
||||
if (selected.empty()) {
|
||||
return;
|
||||
}
|
||||
auto& edit = dialog.controls[kTmcNewType];
|
||||
edit.text = selected;
|
||||
if (edit.window != nullptr) {
|
||||
SetWindowTextA(edit.window, edit.text.c_str());
|
||||
SendMessageA(edit.window, EM_SETSEL, 0, static_cast<LPARAM>(-1));
|
||||
}
|
||||
}
|
||||
|
||||
void select_open_file(DialogState& dialog) {
|
||||
const auto found = dialog.controls.find(kTmcOpenFileList);
|
||||
if (found == dialog.controls.end()) {
|
||||
return;
|
||||
}
|
||||
const std::string selected = selected_list_text(found->second);
|
||||
if (selected.empty()) {
|
||||
return;
|
||||
}
|
||||
auto& edit = dialog.controls[kTmcOpenFileName];
|
||||
edit.text = join_path(dialog.current_directory, selected);
|
||||
if (edit.window != nullptr) {
|
||||
SetWindowTextA(edit.window, edit.text.c_str());
|
||||
SendMessageA(edit.window, EM_SETSEL, 0,
|
||||
static_cast<LPARAM>(-1));
|
||||
}
|
||||
}
|
||||
|
||||
void enter_open_directory(DialogState& dialog) {
|
||||
const auto found = dialog.controls.find(kTmcOpenFileDir);
|
||||
if (found == dialog.controls.end()) {
|
||||
return;
|
||||
}
|
||||
std::string selected = selected_list_text(found->second);
|
||||
if (selected.size() < 2 || selected.front() != '[' ||
|
||||
selected.back() != ']') {
|
||||
return;
|
||||
}
|
||||
selected = selected.substr(1, selected.size() - 2);
|
||||
char full_path[32768] = {};
|
||||
const std::string destination =
|
||||
join_path(dialog.current_directory, selected);
|
||||
if (GetFullPathNameA(destination.c_str(),
|
||||
static_cast<DWORD>(sizeof(full_path)), full_path,
|
||||
nullptr) == 0) {
|
||||
return;
|
||||
}
|
||||
dialog.current_directory = full_path;
|
||||
populate_open_lists(dialog);
|
||||
auto& edit = dialog.controls[kTmcOpenFileName];
|
||||
edit.text = join_path(dialog.current_directory, dialog.file_pattern);
|
||||
if (edit.window != nullptr) {
|
||||
SetWindowTextA(edit.window, edit.text.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void finish_native_dialog(DialogState& dialog, const Tmc result) {
|
||||
if (dialog.hid == kIddOpen) {
|
||||
sync_open_cab(dialog);
|
||||
} else if (dialog.hid == kIddNewDoc) {
|
||||
sync_new_cab(dialog);
|
||||
}
|
||||
if (!invoke_dialog_proc(dialog, kDlmTerm, result)) {
|
||||
return;
|
||||
}
|
||||
dialog.result_tmc = result;
|
||||
dialog.dying = true;
|
||||
dialog.visible = false;
|
||||
if (dialog.window != nullptr && IsWindow(dialog.window)) {
|
||||
ShowWindow(dialog.window, SW_HIDE);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_dialog_command(const Hdlg handle, const WPARAM w_param,
|
||||
const LPARAM l_param) {
|
||||
auto* dialog = find_dialog(handle);
|
||||
if (dialog == nullptr || dialog->dying) {
|
||||
return;
|
||||
}
|
||||
const Tmc tmc = static_cast<Tmc>(LOWORD(w_param) & ~0x8000u);
|
||||
const Word notification = HIWORD(w_param);
|
||||
auto found = dialog->controls.find(tmc);
|
||||
if (found != dialog->controls.end() &&
|
||||
found->second.window == reinterpret_cast<HWND>(l_param)) {
|
||||
if (tmc == kTmcOpenFileName && dialog->hid == kIddOpen) {
|
||||
const int length = GetWindowTextLengthA(found->second.window);
|
||||
std::vector<char> text(static_cast<std::size_t>(length) + 1);
|
||||
GetWindowTextA(found->second.window, text.data(),
|
||||
static_cast<int>(text.size()));
|
||||
found->second.text = text.data();
|
||||
} else if (tmc == kTmcOpenReadOnly && dialog->hid == kIddOpen) {
|
||||
found->second.value = static_cast<Word>(
|
||||
SendMessageA(found->second.window, BM_GETCHECK, 0, 0) ==
|
||||
BST_CHECKED);
|
||||
} else if (tmc == kTmcNewType && dialog->hid == kIddNewDoc) {
|
||||
const int length = GetWindowTextLengthA(found->second.window);
|
||||
std::vector<char> text(static_cast<std::size_t>(length) + 1);
|
||||
GetWindowTextA(found->second.window, text.data(),
|
||||
static_cast<int>(text.size()));
|
||||
found->second.text = text.data();
|
||||
}
|
||||
}
|
||||
if (!dialog->commands_active) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_current_dialog = handle;
|
||||
g_focus_dialog = handle;
|
||||
sync_current_dialog_globals();
|
||||
|
||||
if (dialog->hid == kIddNewDoc && tmc == kTmcNewType &&
|
||||
notification == EN_CHANGE) {
|
||||
invoke_dialog_proc(*dialog, kDlmChange, tmc);
|
||||
return;
|
||||
}
|
||||
if (dialog->hid == kIddNewDoc && tmc == kTmcNewTypeList &&
|
||||
(notification == LBN_SELCHANGE || notification == LBN_DBLCLK)) {
|
||||
select_new_type(*dialog);
|
||||
invoke_dialog_proc(*dialog,
|
||||
notification == LBN_DBLCLK ? kDlmDblClk
|
||||
: kDlmClick,
|
||||
tmc);
|
||||
return;
|
||||
}
|
||||
if (dialog->hid == kIddOpen && tmc == kTmcOpenFileName &&
|
||||
notification == EN_CHANGE) {
|
||||
invoke_dialog_proc(*dialog, kDlmChange, tmc);
|
||||
return;
|
||||
}
|
||||
if (dialog->hid == kIddOpen && tmc == kTmcOpenFileList &&
|
||||
(notification == LBN_SELCHANGE || notification == LBN_DBLCLK)) {
|
||||
select_open_file(*dialog);
|
||||
invoke_dialog_proc(*dialog,
|
||||
notification == LBN_DBLCLK ? kDlmDblClk
|
||||
: kDlmClick,
|
||||
tmc);
|
||||
if (notification == LBN_DBLCLK && !dialog->dying) {
|
||||
finish_native_dialog(*dialog, kTmcOk);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (dialog->hid == kIddOpen && tmc == kTmcOpenFileDir &&
|
||||
(notification == LBN_SELCHANGE || notification == LBN_DBLCLK)) {
|
||||
invoke_dialog_proc(*dialog,
|
||||
notification == LBN_DBLCLK ? kDlmDblClk
|
||||
: kDlmClick,
|
||||
tmc);
|
||||
if (notification == LBN_DBLCLK && !dialog->dying) {
|
||||
enter_open_directory(*dialog);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (notification == BN_CLICKED) {
|
||||
if (dialog->hid == kIddNewDoc &&
|
||||
(tmc == kTmcRNewDoc || tmc == kTmcRNewDot)) {
|
||||
const bool new_template = tmc == kTmcRNewDot;
|
||||
dialog->controls[kTmcNewDot].value = new_template;
|
||||
dialog->controls[kTmcRNewDoc].value = !new_template;
|
||||
dialog->controls[kTmcRNewDot].value = new_template;
|
||||
invoke_dialog_proc(*dialog, kDlmClick, tmc);
|
||||
} else if (tmc == kTmcOk || tmc == kTmcCancel ||
|
||||
(dialog->hid == kIddOpen &&
|
||||
tmc == kTmcOpenCatalog) ||
|
||||
(dialog->hid == kIddNewDoc &&
|
||||
tmc == kTmcSummary)) {
|
||||
finish_native_dialog(*dialog, tmc);
|
||||
} else {
|
||||
invoke_dialog_proc(*dialog, kDlmClick, tmc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT CALLBACK native_dialog_window_proc(const HWND window,
|
||||
const UINT message,
|
||||
const WPARAM w_param,
|
||||
const LPARAM l_param) {
|
||||
if (message == WM_NCCREATE) {
|
||||
const auto* create = reinterpret_cast<const CREATESTRUCTA*>(l_param);
|
||||
SetWindowLongPtrA(window, GWLP_USERDATA,
|
||||
reinterpret_cast<LONG_PTR>(create->lpCreateParams));
|
||||
}
|
||||
const Hdlg handle = static_cast<Hdlg>(reinterpret_cast<std::uintptr_t>(
|
||||
reinterpret_cast<void*>(GetWindowLongPtrA(window, GWLP_USERDATA))));
|
||||
switch (message) {
|
||||
case WM_COMMAND:
|
||||
handle_dialog_command(handle, w_param, l_param);
|
||||
return 0;
|
||||
case WM_CLOSE:
|
||||
if (auto* dialog = find_dialog(handle); dialog != nullptr) {
|
||||
finish_native_dialog(*dialog, kTmcCancel);
|
||||
}
|
||||
return 0;
|
||||
default:
|
||||
return DefWindowProcA(window, message, w_param, l_param);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
@@ -306,7 +1026,7 @@ DAC_SDM dac = {};
|
||||
HWND vhWndMsgBoxParent = nullptr;
|
||||
|
||||
extern void** hcabDlgCur;
|
||||
extern Word wRefDlgCur;
|
||||
extern std::uintptr_t wRefDlgCur;
|
||||
|
||||
int FInitSdm_sdm21(void*) {
|
||||
g_initialized = true;
|
||||
@@ -374,19 +1094,97 @@ Hdlg HdlgStartDlg(DltHeader** dialog_template, Hcab cab, Dli* initializer) {
|
||||
}
|
||||
dialog.window = create_dialog_host(dialog, initializer);
|
||||
g_dialogs.emplace(handle, std::move(dialog));
|
||||
materialize_icon_bar_template(g_dialogs.at(handle));
|
||||
g_current_dialog = handle;
|
||||
g_focus_dialog = handle;
|
||||
sync_current_dialog_globals();
|
||||
materialize_icon_bar_template(g_dialogs.at(handle));
|
||||
materialize_new_template(g_dialogs.at(handle));
|
||||
materialize_open_template(g_dialogs.at(handle));
|
||||
return handle;
|
||||
}
|
||||
|
||||
Tmc TmcDoDlgDli(DltHeader** dialog_template, Hcab cab, Dli* initializer) {
|
||||
if (HdlgStartDlg(dialog_template, cab, initializer) == 0) {
|
||||
const Hdlg previous_current = g_current_dialog;
|
||||
const Hdlg previous_focus = g_focus_dialog;
|
||||
const Hdlg handle = HdlgStartDlg(dialog_template, cab, initializer);
|
||||
if (handle == 0) {
|
||||
return static_cast<Tmc>(-1);
|
||||
}
|
||||
g_dialog.visible = true;
|
||||
return 2; // tmcCancel until native template materialization is active.
|
||||
auto* dialog = find_dialog(handle);
|
||||
if (dialog == nullptr) {
|
||||
return static_cast<Tmc>(-1);
|
||||
}
|
||||
|
||||
Tmc result = kTmcCancel;
|
||||
HWND owner = nullptr;
|
||||
bool restore_owner = false;
|
||||
if (dialog->modal && dialog->native_modal && dialog->window != nullptr) {
|
||||
owner = GetWindow(dialog->window, GW_OWNER);
|
||||
restore_owner = owner != nullptr && IsWindow(owner) &&
|
||||
IsWindowEnabled(owner) != 0;
|
||||
if (restore_owner) {
|
||||
EnableWindow(owner, false);
|
||||
}
|
||||
|
||||
invoke_dialog_proc(*dialog, kDlmInit, dialog->focus);
|
||||
dialog->commands_active = true;
|
||||
if (!dialog->dying) {
|
||||
dialog->visible = true;
|
||||
ShowWindow(dialog->window, SW_SHOW);
|
||||
UpdateWindow(dialog->window);
|
||||
SetActiveWindow(dialog->window);
|
||||
const auto focus = dialog->controls.find(
|
||||
static_cast<Tmc>(dialog->focus & ~0x8000u));
|
||||
if (focus != dialog->controls.end() &&
|
||||
focus->second.window != nullptr) {
|
||||
SetFocus(focus->second.window);
|
||||
}
|
||||
}
|
||||
|
||||
MSG message{};
|
||||
while (!dialog->dying) {
|
||||
const int status = GetMessageA(&message, nullptr, 0, 0);
|
||||
if (status <= 0) {
|
||||
if (status == 0) {
|
||||
PostQuitMessage(static_cast<int>(message.wParam));
|
||||
}
|
||||
dialog->result_tmc = kTmcCancel;
|
||||
dialog->dying = true;
|
||||
break;
|
||||
}
|
||||
if (!IsDialogMessageA(dialog->window, &message)) {
|
||||
TranslateMessage(&message);
|
||||
DispatchMessageA(&message);
|
||||
}
|
||||
dialog = find_dialog(handle);
|
||||
if (dialog == nullptr) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dialog != nullptr) {
|
||||
result = dialog->result_tmc;
|
||||
dialog->commands_active = false;
|
||||
invoke_dialog_proc(*dialog, kDlmExit, result);
|
||||
}
|
||||
}
|
||||
|
||||
dialog = find_dialog(handle);
|
||||
if (dialog != nullptr && dialog->window != nullptr &&
|
||||
IsWindow(dialog->window)) {
|
||||
DestroyWindow(dialog->window);
|
||||
}
|
||||
g_dialogs.erase(handle);
|
||||
if (restore_owner && owner != nullptr && IsWindow(owner)) {
|
||||
EnableWindow(owner, true);
|
||||
SetActiveWindow(owner);
|
||||
}
|
||||
g_current_dialog = find_dialog(previous_current) == nullptr
|
||||
? 0
|
||||
: previous_current;
|
||||
g_focus_dialog = find_dialog(previous_focus) == nullptr ? 0
|
||||
: previous_focus;
|
||||
sync_current_dialog_globals();
|
||||
return result;
|
||||
}
|
||||
|
||||
Tmc TmcDoDlg_sdm21(DltHeader** dialog_template, Hcab cab,
|
||||
@@ -397,7 +1195,8 @@ Tmc TmcDoDlg_sdm21(DltHeader** dialog_template, Hcab cab,
|
||||
return TmcDoDlgDli(dialog_template, cab, &initializer);
|
||||
}
|
||||
|
||||
void EndDlg(Tmc) {
|
||||
void EndDlg(Tmc result) {
|
||||
g_dialog.result_tmc = result;
|
||||
g_dialog.dying = true;
|
||||
g_dialog.visible = false;
|
||||
if (g_dialog.window != nullptr && IsWindow(g_dialog.window)) {
|
||||
@@ -701,7 +1500,7 @@ Hcab HcabFromDlg(int dialog) {
|
||||
}
|
||||
void SaveCabs(void (*callback)(Hcab, Word, Tmc, int), int save) {
|
||||
if (callback != nullptr && g_dialog.cab != nullptr) {
|
||||
callback(g_dialog.cab, g_dialog.reference, 0, save);
|
||||
callback(g_dialog.cab, g_dialog.hid, 0, save);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ struct TestDli {
|
||||
int dx;
|
||||
int dy;
|
||||
std::uint32_t flags;
|
||||
TestWord reference;
|
||||
std::uintptr_t reference;
|
||||
unsigned char* runtime_items;
|
||||
};
|
||||
|
||||
@@ -83,9 +83,51 @@ extern "C" HWND HwndFromDlg(TestWord);
|
||||
extern "C" HWND HwndOfTmc(TestWord);
|
||||
extern "C" int FModalDlg(TestWord);
|
||||
extern "C" int FFreeDlg();
|
||||
extern "C" TestWord TmcDoDlgDli(TestDltHeader**, void**, TestDli*);
|
||||
extern "C" void EndDlg(TestWord);
|
||||
extern "C" void SetTmcText_sdm21(TestWord, char*);
|
||||
extern "C" void GetTmcText_sdm21(TestWord, char*, TestWord);
|
||||
|
||||
int modal_init_count = 0;
|
||||
int modal_exit_count = 0;
|
||||
int new_modal_init_count = 0;
|
||||
int new_modal_exit_count = 0;
|
||||
bool new_modal_controls_present = false;
|
||||
|
||||
bool WindowHasClass(const HWND window, const char* expected) {
|
||||
char class_name[32] = {};
|
||||
return window != nullptr &&
|
||||
GetClassNameA(window, class_name, sizeof(class_name)) != 0 &&
|
||||
std::strcmp(class_name, expected) == 0;
|
||||
}
|
||||
|
||||
int ModalRuntimeProbe(TestWord message, TestWord, TestWord, TestWord,
|
||||
TestWord) {
|
||||
if (message == 1) {
|
||||
++modal_init_count;
|
||||
EndDlg(2);
|
||||
} else if (message == 4) {
|
||||
++modal_exit_count;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int NewModalRuntimeProbe(TestWord message, TestWord, TestWord, TestWord,
|
||||
TestWord) {
|
||||
if (message == 1) {
|
||||
++new_modal_init_count;
|
||||
new_modal_controls_present =
|
||||
WindowHasClass(HwndOfTmc(0x0402), "Button") &&
|
||||
WindowHasClass(HwndOfTmc(0x0403), "Button") &&
|
||||
WindowHasClass(HwndOfTmc(0x0404), "Edit") &&
|
||||
WindowHasClass(HwndOfTmc(0x0405), "ListBox");
|
||||
EndDlg(2);
|
||||
} else if (message == 4) {
|
||||
++new_modal_exit_count;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::array offsets{2, 8, 13, 3};
|
||||
AddDcbToLprgbst(offsets.data(), static_cast<int>(offsets.size()), 5, 8);
|
||||
@@ -283,6 +325,30 @@ int main() {
|
||||
EndSdm();
|
||||
return 23;
|
||||
}
|
||||
TestDltHeader modal_template{{8, 24, 206, 104}, 3, 0x8400,
|
||||
reinterpret_cast<void*>(ModalRuntimeProbe),
|
||||
11, 4};
|
||||
auto* modal_template_pointer = &modal_template;
|
||||
TestDli modal_initializer{parent, 0, 0, 1, 0, nullptr};
|
||||
if (TmcDoDlgDli(&modal_template_pointer, nullptr, &modal_initializer) != 2 ||
|
||||
modal_init_count != 1 || modal_exit_count != 1 ||
|
||||
!IsWindow(second_host)) {
|
||||
DestroyWindow(parent);
|
||||
EndSdm();
|
||||
return 24;
|
||||
}
|
||||
TestDltHeader new_modal_template{
|
||||
{20, 24, 127, 114}, 2, 0x8404,
|
||||
reinterpret_cast<void*>(NewModalRuntimeProbe), 9, 4};
|
||||
auto* new_modal_template_pointer = &new_modal_template;
|
||||
if (TmcDoDlgDli(&new_modal_template_pointer, nullptr,
|
||||
&modal_initializer) != 2 ||
|
||||
new_modal_init_count != 1 || new_modal_exit_count != 1 ||
|
||||
!new_modal_controls_present || !IsWindow(second_host)) {
|
||||
DestroyWindow(parent);
|
||||
EndSdm();
|
||||
return 25;
|
||||
}
|
||||
EndSdm();
|
||||
DestroyWindow(parent);
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ typedef struct _CABPARALOOKS
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
CHAR **hszStyle;
|
||||
int iJustify;
|
||||
int wParLeftIn;
|
||||
int wParRightIn;
|
||||
@@ -31,7 +32,6 @@ typedef struct _CABPARALOOKS
|
||||
int wParDyaBefore;
|
||||
int wParDyaAfter;
|
||||
int wParDyaLine;
|
||||
CHAR **hszStyle;
|
||||
BOOL fParKeepLT;
|
||||
BOOL fParKeepNP;
|
||||
int iBrcp;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
typedef struct _CABFILEPSWD
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
CHAR **hszPswd;
|
||||
} CABFILEPSWD;
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ typedef struct _CABPASTELINK
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
BOOL fHot;
|
||||
CHAR **hszLink;
|
||||
BOOL fHot;
|
||||
} CABPASTELINK;
|
||||
typedef CABPASTELINK **HCABPASTELINK;
|
||||
#define cabiCABPASTELINK Cabi((sizeof(CABPASTELINK) + sizeof(WORD) - 1) / sizeof(WORD), 1)
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
#define tmcCropBottom (tmcUserMin + 5)
|
||||
#define tmcCropRight (tmcUserMin + 6)
|
||||
|
||||
#pragma pack(push, 2)
|
||||
typedef struct _CABFORMATPIC
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
CHAR **hszOrigSize;
|
||||
int iPicBrcl;
|
||||
int wScaleMy;
|
||||
@@ -22,7 +22,6 @@ typedef struct _CABFORMATPIC
|
||||
int wCropBottom;
|
||||
int wCropRight;
|
||||
} CABFORMATPIC;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef CABFORMATPIC **HCABFORMATPIC;
|
||||
|
||||
|
||||
@@ -29,11 +29,12 @@ typedef struct _CABPRINT
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
CHAR **hszPrPrinter;
|
||||
CHAR **hszPrFrom;
|
||||
CHAR **hszPrTo;
|
||||
CHAR **hszFileName;
|
||||
int istPrSrc;
|
||||
int cCopies;
|
||||
int prng;
|
||||
CHAR **hszPrFrom;
|
||||
CHAR **hszPrTo;
|
||||
BOOL fReverse;
|
||||
BOOL fDraft;
|
||||
BOOL fRefresh;
|
||||
@@ -42,7 +43,6 @@ typedef struct _CABPRINT
|
||||
BOOL fAnnotations;
|
||||
BOOL fSeeHidden;
|
||||
BOOL fShowInst;
|
||||
CHAR **hszFileName;
|
||||
} CABPRINT;
|
||||
|
||||
typedef CABPRINT **HCABPRINT;
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#define tmcPMPrint (tmcUserMin + 5)
|
||||
#define tmcPMNewDoc (tmcUserMin + 6)
|
||||
|
||||
#pragma pack(push, 2)
|
||||
typedef struct _CABPRINTMERGE
|
||||
{
|
||||
CABH cabh;
|
||||
@@ -18,7 +17,6 @@ typedef struct _CABPRINTMERGE
|
||||
int iRecFrom;
|
||||
int iRecTo;
|
||||
} CABPRINTMERGE;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef CABPRINTMERGE **HCABPRINTMERGE;
|
||||
#define cabiCABPRINTMERGE Cabi((sizeof(CABPRINTMERGE) + sizeof(WORD) - 1) / sizeof(WORD), 0)
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
typedef struct _CABPROMPT
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
CHAR **hszPrompt;
|
||||
CHAR **hszInput;
|
||||
} CABPROMPT;
|
||||
|
||||
@@ -10,8 +10,8 @@ typedef struct _CABRECORDER
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
CHAR **pszName;
|
||||
int iContext;
|
||||
CHAR **pszDesc;
|
||||
int iContext;
|
||||
} CABRECORDER;
|
||||
|
||||
typedef CABRECORDER **HCABRECORDER;
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#define tmcRPAllLevels (tmcUserMin + 8)
|
||||
#define tmcRPFormat ((tmcUserMin + 9) | ftmcGrouped)
|
||||
|
||||
#pragma pack(push, 2)
|
||||
typedef struct _CABRENUMPARAS
|
||||
{
|
||||
CABH cabh;
|
||||
@@ -23,7 +22,6 @@ typedef struct _CABRENUMPARAS
|
||||
BOOL fManual;
|
||||
BOOL fShowAllLev;
|
||||
} CABRENUMPARAS;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef CABRENUMPARAS **HCABRENUMPARAS;
|
||||
#define cabiCABRENUMPARAS Cabi((sizeof(CABRENUMPARAS) + sizeof(WORD) - 1) / sizeof(WORD), 2)
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
typedef struct _CABRIBBON
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
int ftc;
|
||||
int hps;
|
||||
} CABRIBBON;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
typedef struct _CABRIBBON3
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
int ftc;
|
||||
int hps;
|
||||
} CABRIBBON3;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
typedef struct _CABRULER
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
CHAR **hszStyle;
|
||||
} CABRULER;
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
typedef struct _CABRULER3
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
CHAR **hszStyle;
|
||||
} CABRULER3;
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
typedef struct _CABRUNMACRO
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
CHAR **hszName;
|
||||
BOOL fShowAll;
|
||||
} CABRUNMACRO;
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#define tmcSortCol (tmcUserMin + 7)
|
||||
#define tmcSortCase (tmcUserMin + 8)
|
||||
|
||||
#pragma pack(push, 2)
|
||||
typedef struct _CABSORT
|
||||
{
|
||||
CABH cabh;
|
||||
@@ -23,7 +22,6 @@ typedef struct _CABSORT
|
||||
BOOL fSortCol;
|
||||
BOOL fCase;
|
||||
} CABSORT;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef CABSORT **HCABSORT;
|
||||
#define cabiCABSORT Cabi((sizeof(CABSORT) + sizeof(WORD) - 1) / sizeof(WORD), 0)
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#define tmcSplIgnoreCaps (tmcUserMin + 8)
|
||||
#define tmcSplAutoSugg (tmcUserMin + 9)
|
||||
|
||||
#pragma pack(push, 2)
|
||||
typedef struct _CABSPELLER
|
||||
{
|
||||
CABH cabh;
|
||||
@@ -23,7 +22,6 @@ typedef struct _CABSPELLER
|
||||
BOOL fSplIgnoreCaps;
|
||||
BOOL fSplAutoSugg;
|
||||
} CABSPELLER;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef CABSPELLER **HCABSPELLER;
|
||||
#define cabiCABSPELLER Cabi((sizeof(CABSPELLER) + sizeof(WORD) - 1) / sizeof(WORD), 3)
|
||||
|
||||
@@ -13,17 +13,16 @@
|
||||
#define tmcSplMMIgnoreCaps (tmcUserMin + 9)
|
||||
#define tmcSplMMAutoSugg (tmcUserMin + 10)
|
||||
|
||||
#pragma pack(push, 2)
|
||||
typedef struct _CABSPELLERMM
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
CHAR **hszSplMMChangeTo;
|
||||
unsigned uSplMMSuggList;
|
||||
int iSplMMAddToBox;
|
||||
BOOL fSplMMIgnoreCaps;
|
||||
BOOL fSplMMAutoSugg;
|
||||
} CABSPELLERMM;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef CABSPELLERMM **HCABSPELLERMM;
|
||||
#define cabiCABSPELLERMM Cabi((sizeof(CABSPELLERMM) + sizeof(WORD) - 1) / sizeof(WORD), 1)
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#define tmcMergeCells (tmcUserMin + 10)
|
||||
#define tmcSplitCells (tmcUserMin + 11)
|
||||
|
||||
#pragma pack(push, 2)
|
||||
typedef struct _CABEDITTABLE
|
||||
{
|
||||
CABH cabh;
|
||||
@@ -23,7 +22,6 @@ typedef struct _CABEDITTABLE
|
||||
int cmdtype;
|
||||
BOOL fShiftVert;
|
||||
} CABEDITTABLE;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef CABEDITTABLE **HCABEDITTABLE;
|
||||
#define cabiCABEDITTABLE Cabi((sizeof(CABEDITTABLE) + sizeof(WORD) - 1) / sizeof(WORD), 0)
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#define tmcNextColumn (tmcUserMin + 20)
|
||||
#define tmcPrevColumn (tmcUserMin + 21)
|
||||
|
||||
#pragma pack(push, 2)
|
||||
typedef struct _CABFORMATTABLE
|
||||
{
|
||||
CABH cabh;
|
||||
@@ -46,7 +45,6 @@ typedef struct _CABFORMATTABLE
|
||||
int jc;
|
||||
BOOL fWholeTable;
|
||||
} CABFORMATTABLE;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef CABFORMATTABLE **HCABFORMATTABLE;
|
||||
#define cabiCABFORMATTABLE Cabi((sizeof(CABFORMATTABLE) + sizeof(WORD) - 1) / sizeof(WORD), 1)
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#define tmcFormatComma (tmcUserMin + 6)
|
||||
#define tmcTunnelFormat (tmcUserMin + 7)
|
||||
|
||||
#pragma pack(push, 2)
|
||||
typedef struct _CABINSERTTABLE
|
||||
{
|
||||
CABH cabh;
|
||||
@@ -20,7 +19,6 @@ typedef struct _CABINSERTTABLE
|
||||
int dxaWidth;
|
||||
int tblfmt;
|
||||
} CABINSERTTABLE;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef CABINSERTTABLE **HCABINSERTTABLE;
|
||||
#define cabiCABINSERTTABLE Cabi((sizeof(CABINSERTTABLE) + sizeof(WORD) - 1) / sizeof(WORD), 0)
|
||||
|
||||
@@ -6,14 +6,12 @@
|
||||
#define tmcCommaDelim (tmcUserMin + 2)
|
||||
#define tmcTabDelim (tmcUserMin + 3)
|
||||
|
||||
#pragma pack(push, 2)
|
||||
typedef struct _CABTABLETOTEXT
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
int tblfmt;
|
||||
} CABTABLETOTEXT;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef CABTABLETOTEXT **HCABTABLETOTEXT;
|
||||
#define cabiCABTABLETOTEXT Cabi((sizeof(CABTABLETOTEXT) + sizeof(WORD) - 1) / sizeof(WORD), 0)
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#define tmcPartOfSpeech (tmcUserMin + 6)
|
||||
#define tmcDefinition (tmcUserMin + 7)
|
||||
|
||||
#pragma pack(push, 2)
|
||||
typedef struct _CABTHESAURUS
|
||||
{
|
||||
CABH cabh;
|
||||
@@ -19,7 +18,6 @@ typedef struct _CABTHESAURUS
|
||||
int uSynList;
|
||||
int uDefList;
|
||||
} CABTHESAURUS;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef CABTHESAURUS **HCABTHESAURUS;
|
||||
#define cabiCABTHESAURUS Cabi((sizeof(CABTHESAURUS) + sizeof(WORD) - 1) / sizeof(WORD), 1)
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#define tmcTocFrom (tmcUserMin + 5)
|
||||
#define tmcTocTo (tmcUserMin + 6)
|
||||
|
||||
#pragma pack(push, 2)
|
||||
typedef struct _CABTOC
|
||||
{
|
||||
CABH cabh;
|
||||
@@ -22,7 +21,6 @@ typedef struct _CABTOC
|
||||
unsigned wTocTo;
|
||||
BOOL fReplace;
|
||||
} CABTOC;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef CABTOC **HCABTOC;
|
||||
#define cabiCABTOC Cabi((sizeof(CABTOC) + sizeof(WORD) - 1) / sizeof(WORD), 0)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
typedef struct _CABUSERNAME
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
CHAR **hszName;
|
||||
CHAR **hszInitials;
|
||||
} CABUSERNAME;
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
/* Compile contract reconstructed from Opus/dlg/usrdlg.des. */
|
||||
#pragma pack(push, 2)
|
||||
typedef struct _CABUSRDLG
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
CHAR **foo;
|
||||
} CABUSRDLG;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef CABUSRDLG **HCABUSRDLG;
|
||||
#define cabiCABUSRDLG Cabi((sizeof(CABUSRDLG) + sizeof(WORD) - 1) / sizeof(WORD), 1)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
typedef struct _CABEXCR
|
||||
{
|
||||
CABH cabh;
|
||||
WORD sab;
|
||||
int iFmt;
|
||||
} CABEXCR;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity version="0.1.0.0" processorArchitecture="amd64"
|
||||
name="Microsoft.Opus.WordX64Port" type="win32"/>
|
||||
<description>Microsoft Word for Windows x64 port</description>
|
||||
<description>Microsoft Word for Windows x64 port by Justin Marshall</description>
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls"
|
||||
|
||||
@@ -1,919 +0,0 @@
|
||||
#include "legacy_asm_port.h"
|
||||
|
||||
#include <Windows.h>
|
||||
#include <CommCtrl.h>
|
||||
#include <CommDlg.h>
|
||||
#include <Richedit.h>
|
||||
#include <Shellapi.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <cwctype>
|
||||
#include <filesystem>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr wchar_t kWindowClass[] = L"OpusWordX64Window";
|
||||
constexpr wchar_t kProductName[] = L"Microsoft Word x64 Port";
|
||||
constexpr UINT_PTR kStatusTimer = 1;
|
||||
|
||||
enum Command : UINT {
|
||||
cmd_file_new = 100,
|
||||
cmd_file_open,
|
||||
cmd_file_save,
|
||||
cmd_file_save_as,
|
||||
cmd_file_print,
|
||||
cmd_file_exit,
|
||||
cmd_edit_undo = 200,
|
||||
cmd_edit_redo,
|
||||
cmd_edit_cut,
|
||||
cmd_edit_copy,
|
||||
cmd_edit_paste,
|
||||
cmd_edit_select_all,
|
||||
cmd_edit_find,
|
||||
cmd_edit_find_next,
|
||||
cmd_format_bold = 300,
|
||||
cmd_format_italic,
|
||||
cmd_format_underline,
|
||||
cmd_format_font,
|
||||
cmd_format_left,
|
||||
cmd_format_center,
|
||||
cmd_format_right,
|
||||
cmd_format_justify,
|
||||
cmd_tools_word_count = 400,
|
||||
cmd_help_about = 500,
|
||||
};
|
||||
|
||||
[[nodiscard]] std::wstring system_error_message(const DWORD error) {
|
||||
wchar_t* buffer = nullptr;
|
||||
const DWORD count = FormatMessageW(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
nullptr, error, 0, reinterpret_cast<wchar_t*>(&buffer), 0, nullptr);
|
||||
std::wstring message = count != 0 && buffer != nullptr
|
||||
? std::wstring(buffer, count)
|
||||
: L"Unknown error (" + std::to_wstring(error) + L")";
|
||||
if (buffer != nullptr) {
|
||||
LocalFree(buffer);
|
||||
}
|
||||
while (!message.empty() && (message.back() == L'\r' || message.back() == L'\n')) {
|
||||
message.pop_back();
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::wstring lower_extension(const std::wstring& path) {
|
||||
auto extension = std::filesystem::path(path).extension().wstring();
|
||||
std::ranges::transform(extension, extension.begin(),
|
||||
[](const wchar_t ch) { return static_cast<wchar_t>(std::towlower(ch)); });
|
||||
return extension;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::byte> read_file(const std::wstring& path) {
|
||||
const HANDLE file = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (file == INVALID_HANDLE_VALUE) {
|
||||
throw std::runtime_error("CreateFileW failed: " + std::to_string(GetLastError()));
|
||||
}
|
||||
|
||||
LARGE_INTEGER size{};
|
||||
if (!GetFileSizeEx(file, &size) || size.QuadPart < 0 ||
|
||||
size.QuadPart > static_cast<LONGLONG>(std::numeric_limits<DWORD>::max())) {
|
||||
const DWORD error = GetLastError();
|
||||
CloseHandle(file);
|
||||
throw std::runtime_error("GetFileSizeEx failed: " + std::to_string(error));
|
||||
}
|
||||
|
||||
std::vector<std::byte> bytes(static_cast<std::size_t>(size.QuadPart));
|
||||
DWORD read = 0;
|
||||
const BOOL succeeded = bytes.empty() ||
|
||||
ReadFile(file, bytes.data(), static_cast<DWORD>(bytes.size()), &read, nullptr);
|
||||
const DWORD error = succeeded ? ERROR_SUCCESS : GetLastError();
|
||||
CloseHandle(file);
|
||||
if (!succeeded || read != bytes.size()) {
|
||||
throw std::runtime_error("ReadFile failed: " + std::to_string(error));
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
void write_file(const std::wstring& path, const std::vector<std::byte>& bytes) {
|
||||
const HANDLE file = CreateFileW(path.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (file == INVALID_HANDLE_VALUE) {
|
||||
throw std::runtime_error("CreateFileW failed: " + std::to_string(GetLastError()));
|
||||
}
|
||||
DWORD written = 0;
|
||||
const BOOL succeeded = bytes.empty() ||
|
||||
WriteFile(file, bytes.data(), static_cast<DWORD>(bytes.size()), &written, nullptr);
|
||||
const DWORD error = succeeded ? ERROR_SUCCESS : GetLastError();
|
||||
CloseHandle(file);
|
||||
if (!succeeded || written != bytes.size()) {
|
||||
throw std::runtime_error("WriteFile failed: " + std::to_string(error));
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::wstring decode_text(const std::vector<std::byte>& bytes) {
|
||||
if (bytes.size() >= 2 && bytes[0] == std::byte{0xff} && bytes[1] == std::byte{0xfe}) {
|
||||
std::wstring result((bytes.size() - 2) / sizeof(wchar_t), L'\0');
|
||||
std::memcpy(result.data(), bytes.data() + 2, result.size() * sizeof(wchar_t));
|
||||
return result;
|
||||
}
|
||||
|
||||
std::size_t offset = 0;
|
||||
if (bytes.size() >= 3 && bytes[0] == std::byte{0xef} && bytes[1] == std::byte{0xbb} &&
|
||||
bytes[2] == std::byte{0xbf}) {
|
||||
offset = 3;
|
||||
}
|
||||
const auto* text = reinterpret_cast<const char*>(bytes.data() + offset);
|
||||
const int byte_count = static_cast<int>(bytes.size() - offset);
|
||||
if (byte_count == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
UINT code_page = CP_UTF8;
|
||||
DWORD flags = MB_ERR_INVALID_CHARS;
|
||||
int character_count = MultiByteToWideChar(code_page, flags, text, byte_count, nullptr, 0);
|
||||
if (character_count == 0) {
|
||||
code_page = CP_ACP;
|
||||
flags = 0;
|
||||
character_count = MultiByteToWideChar(code_page, flags, text, byte_count, nullptr, 0);
|
||||
}
|
||||
if (character_count == 0) {
|
||||
throw std::runtime_error("The text encoding could not be decoded");
|
||||
}
|
||||
std::wstring result(static_cast<std::size_t>(character_count), L'\0');
|
||||
MultiByteToWideChar(code_page, flags, text, byte_count, result.data(), character_count);
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::byte> encode_utf8(const std::wstring_view text) {
|
||||
const int byte_count = WideCharToMultiByte(CP_UTF8, 0, text.data(),
|
||||
static_cast<int>(text.size()), nullptr, 0,
|
||||
nullptr, nullptr);
|
||||
std::vector<std::byte> result{
|
||||
std::byte{0xef}, std::byte{0xbb}, std::byte{0xbf},
|
||||
};
|
||||
const auto initial_size = result.size();
|
||||
result.resize(initial_size + static_cast<std::size_t>(byte_count));
|
||||
if (byte_count != 0) {
|
||||
WideCharToMultiByte(CP_UTF8, 0, text.data(), static_cast<int>(text.size()),
|
||||
reinterpret_cast<char*>(result.data() + initial_size), byte_count,
|
||||
nullptr, nullptr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] HMENU build_menu() {
|
||||
const HMENU menu = CreateMenu();
|
||||
const HMENU file = CreatePopupMenu();
|
||||
AppendMenuW(file, MF_STRING, cmd_file_new, L"&New\tCtrl+N");
|
||||
AppendMenuW(file, MF_STRING, cmd_file_open, L"&Open...\tCtrl+O");
|
||||
AppendMenuW(file, MF_STRING, cmd_file_save, L"&Save\tCtrl+S");
|
||||
AppendMenuW(file, MF_STRING, cmd_file_save_as, L"Save &As...");
|
||||
AppendMenuW(file, MF_SEPARATOR, 0, nullptr);
|
||||
AppendMenuW(file, MF_STRING, cmd_file_print, L"&Print...\tCtrl+P");
|
||||
AppendMenuW(file, MF_SEPARATOR, 0, nullptr);
|
||||
AppendMenuW(file, MF_STRING, cmd_file_exit, L"E&xit");
|
||||
AppendMenuW(menu, MF_POPUP, reinterpret_cast<UINT_PTR>(file), L"&File");
|
||||
|
||||
const HMENU edit = CreatePopupMenu();
|
||||
AppendMenuW(edit, MF_STRING, cmd_edit_undo, L"&Undo\tCtrl+Z");
|
||||
AppendMenuW(edit, MF_STRING, cmd_edit_redo, L"&Redo\tCtrl+Y");
|
||||
AppendMenuW(edit, MF_SEPARATOR, 0, nullptr);
|
||||
AppendMenuW(edit, MF_STRING, cmd_edit_cut, L"Cu&t\tCtrl+X");
|
||||
AppendMenuW(edit, MF_STRING, cmd_edit_copy, L"&Copy\tCtrl+C");
|
||||
AppendMenuW(edit, MF_STRING, cmd_edit_paste, L"&Paste\tCtrl+V");
|
||||
AppendMenuW(edit, MF_STRING, cmd_edit_select_all, L"Select &All\tCtrl+A");
|
||||
AppendMenuW(edit, MF_SEPARATOR, 0, nullptr);
|
||||
AppendMenuW(edit, MF_STRING, cmd_edit_find, L"&Find...\tCtrl+F");
|
||||
AppendMenuW(edit, MF_STRING, cmd_edit_find_next, L"Find &Next\tF3");
|
||||
AppendMenuW(menu, MF_POPUP, reinterpret_cast<UINT_PTR>(edit), L"&Edit");
|
||||
|
||||
const HMENU format = CreatePopupMenu();
|
||||
AppendMenuW(format, MF_STRING, cmd_format_bold, L"&Bold\tCtrl+B");
|
||||
AppendMenuW(format, MF_STRING, cmd_format_italic, L"&Italic\tCtrl+I");
|
||||
AppendMenuW(format, MF_STRING, cmd_format_underline, L"&Underline\tCtrl+U");
|
||||
AppendMenuW(format, MF_STRING, cmd_format_font, L"&Font...");
|
||||
AppendMenuW(format, MF_SEPARATOR, 0, nullptr);
|
||||
AppendMenuW(format, MF_STRING, cmd_format_left, L"Align &Left\tCtrl+L");
|
||||
AppendMenuW(format, MF_STRING, cmd_format_center, L"&Center\tCtrl+E");
|
||||
AppendMenuW(format, MF_STRING, cmd_format_right, L"Align &Right\tCtrl+R");
|
||||
AppendMenuW(format, MF_STRING, cmd_format_justify, L"&Justify\tCtrl+J");
|
||||
AppendMenuW(menu, MF_POPUP, reinterpret_cast<UINT_PTR>(format), L"F&ormat");
|
||||
|
||||
const HMENU tools = CreatePopupMenu();
|
||||
AppendMenuW(tools, MF_STRING, cmd_tools_word_count, L"&Word Count...");
|
||||
AppendMenuW(menu, MF_POPUP, reinterpret_cast<UINT_PTR>(tools), L"&Tools");
|
||||
|
||||
const HMENU help = CreatePopupMenu();
|
||||
AppendMenuW(help, MF_STRING, cmd_help_about, L"&About Microsoft Word x64 Port");
|
||||
AppendMenuW(menu, MF_POPUP, reinterpret_cast<UINT_PTR>(help), L"&Help");
|
||||
return menu;
|
||||
}
|
||||
|
||||
class WordApplication {
|
||||
public:
|
||||
explicit WordApplication(const HINSTANCE instance) : instance_(instance) {}
|
||||
|
||||
int run(const int show_command) {
|
||||
int argument_count = 0;
|
||||
wchar_t** arguments = CommandLineToArgvW(GetCommandLineW(), &argument_count);
|
||||
if (arguments != nullptr && argument_count > 1 &&
|
||||
std::wstring_view(arguments[1]) == L"--self-test") {
|
||||
std::array<std::uint8_t, 7> pattern_buffer{};
|
||||
constexpr std::array<std::uint8_t, 3> pattern{1, 2, 3};
|
||||
opus::x64::fill_byte_pattern(pattern_buffer.data(), pattern_buffer.size(),
|
||||
pattern.data(), pattern.size());
|
||||
const bool passed = opus::x64::assembly_manifest_complete() &&
|
||||
opus::x64::assembly_manifest_count() == 59 &&
|
||||
opus::x64::multiply_divide(10, 3, 2) == 15 &&
|
||||
opus::x64::legacy_square(4.0) == 16.0 &&
|
||||
pattern_buffer == std::array<std::uint8_t, 7>{1, 2, 3, 1, 2, 3, 1} &&
|
||||
opus::x64::is_ascii_alphanumeric('Z') &&
|
||||
opus::x64::ascii_upper('q') == 'Q';
|
||||
LocalFree(arguments);
|
||||
return passed ? 0 : 2;
|
||||
}
|
||||
|
||||
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
||||
INITCOMMONCONTROLSEX controls{sizeof(controls), ICC_BAR_CLASSES | ICC_STANDARD_CLASSES};
|
||||
InitCommonControlsEx(&controls);
|
||||
|
||||
rich_edit_library_ = LoadLibraryW(L"Msftedit.dll");
|
||||
if (rich_edit_library_ == nullptr) {
|
||||
show_error(L"Microsoft Rich Edit could not be loaded", GetLastError());
|
||||
if (arguments != nullptr) {
|
||||
LocalFree(arguments);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
WNDCLASSEXW window_class{sizeof(window_class)};
|
||||
window_class.style = CS_HREDRAW | CS_VREDRAW;
|
||||
window_class.lpfnWndProc = &WordApplication::window_proc;
|
||||
window_class.hInstance = instance_;
|
||||
window_class.hIcon = LoadIconW(nullptr, IDI_APPLICATION);
|
||||
window_class.hCursor = LoadCursorW(nullptr, IDC_ARROW);
|
||||
window_class.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_APPWORKSPACE + 1);
|
||||
window_class.lpszClassName = kWindowClass;
|
||||
window_class.hIconSm = window_class.hIcon;
|
||||
if (RegisterClassExW(&window_class) == 0) {
|
||||
show_error(L"The application window could not be registered", GetLastError());
|
||||
FreeLibrary(rich_edit_library_);
|
||||
if (arguments != nullptr) {
|
||||
LocalFree(arguments);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
main_window_ = CreateWindowExW(
|
||||
0, kWindowClass, kProductName, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, 1100, 800, nullptr, nullptr, instance_, this);
|
||||
if (main_window_ == nullptr) {
|
||||
show_error(L"The application window could not be created", GetLastError());
|
||||
FreeLibrary(rich_edit_library_);
|
||||
if (arguments != nullptr) {
|
||||
LocalFree(arguments);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (arguments != nullptr && argument_count > 1 &&
|
||||
std::wstring_view(arguments[1]) == L"--ui-self-test") {
|
||||
SetWindowTextW(editor_, L"Word x64 UI smoke test");
|
||||
const bool passed = IsWindow(main_window_) != FALSE && IsWindow(editor_) != FALSE &&
|
||||
IsWindow(status_) != FALSE && GetWindowTextLengthW(editor_) == 22;
|
||||
DestroyWindow(main_window_);
|
||||
LocalFree(arguments);
|
||||
FreeLibrary(rich_edit_library_);
|
||||
return passed ? 0 : 3;
|
||||
}
|
||||
|
||||
if (arguments != nullptr && argument_count > 1) {
|
||||
open_path(arguments[1]);
|
||||
}
|
||||
if (arguments != nullptr) {
|
||||
LocalFree(arguments);
|
||||
}
|
||||
|
||||
ShowWindow(main_window_, show_command);
|
||||
UpdateWindow(main_window_);
|
||||
|
||||
const std::array accelerators{
|
||||
ACCEL{FVIRTKEY | FCONTROL, 'N', cmd_file_new},
|
||||
ACCEL{FVIRTKEY | FCONTROL, 'O', cmd_file_open},
|
||||
ACCEL{FVIRTKEY | FCONTROL, 'S', cmd_file_save},
|
||||
ACCEL{FVIRTKEY | FCONTROL, 'P', cmd_file_print},
|
||||
ACCEL{FVIRTKEY | FCONTROL, 'Z', cmd_edit_undo},
|
||||
ACCEL{FVIRTKEY | FCONTROL, 'Y', cmd_edit_redo},
|
||||
ACCEL{FVIRTKEY | FCONTROL, 'X', cmd_edit_cut},
|
||||
ACCEL{FVIRTKEY | FCONTROL, 'C', cmd_edit_copy},
|
||||
ACCEL{FVIRTKEY | FCONTROL, 'V', cmd_edit_paste},
|
||||
ACCEL{FVIRTKEY | FCONTROL, 'A', cmd_edit_select_all},
|
||||
ACCEL{FVIRTKEY | FCONTROL, 'F', cmd_edit_find},
|
||||
ACCEL{FVIRTKEY, VK_F3, cmd_edit_find_next},
|
||||
ACCEL{FVIRTKEY | FCONTROL, 'B', cmd_format_bold},
|
||||
ACCEL{FVIRTKEY | FCONTROL, 'I', cmd_format_italic},
|
||||
ACCEL{FVIRTKEY | FCONTROL, 'U', cmd_format_underline},
|
||||
ACCEL{FVIRTKEY | FCONTROL, 'L', cmd_format_left},
|
||||
ACCEL{FVIRTKEY | FCONTROL, 'E', cmd_format_center},
|
||||
ACCEL{FVIRTKEY | FCONTROL, 'R', cmd_format_right},
|
||||
ACCEL{FVIRTKEY | FCONTROL, 'J', cmd_format_justify},
|
||||
};
|
||||
const HACCEL accelerator_table = CreateAcceleratorTableW(
|
||||
const_cast<LPACCEL>(accelerators.data()), static_cast<int>(accelerators.size()));
|
||||
|
||||
MSG message{};
|
||||
while (GetMessageW(&message, nullptr, 0, 0) > 0) {
|
||||
if (find_dialog_ != nullptr && IsDialogMessageW(find_dialog_, &message)) {
|
||||
continue;
|
||||
}
|
||||
if (!TranslateAcceleratorW(main_window_, accelerator_table, &message)) {
|
||||
TranslateMessage(&message);
|
||||
DispatchMessageW(&message);
|
||||
}
|
||||
}
|
||||
DestroyAcceleratorTable(accelerator_table);
|
||||
FreeLibrary(rich_edit_library_);
|
||||
return static_cast<int>(message.wParam);
|
||||
}
|
||||
|
||||
private:
|
||||
static LRESULT CALLBACK window_proc(const HWND window, const UINT message,
|
||||
const WPARAM wparam, const LPARAM lparam) {
|
||||
WordApplication* app = reinterpret_cast<WordApplication*>(
|
||||
GetWindowLongPtrW(window, GWLP_USERDATA));
|
||||
if (message == WM_NCCREATE) {
|
||||
const auto* create = reinterpret_cast<const CREATESTRUCTW*>(lparam);
|
||||
app = static_cast<WordApplication*>(create->lpCreateParams);
|
||||
app->main_window_ = window;
|
||||
SetWindowLongPtrW(window, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(app));
|
||||
}
|
||||
return app != nullptr ? app->dispatch(message, wparam, lparam)
|
||||
: DefWindowProcW(window, message, wparam, lparam);
|
||||
}
|
||||
|
||||
LRESULT dispatch(const UINT message, const WPARAM wparam, const LPARAM lparam) {
|
||||
if (message == find_message_) {
|
||||
on_find_message(*reinterpret_cast<const FINDREPLACEW*>(lparam));
|
||||
return 0;
|
||||
}
|
||||
switch (message) {
|
||||
case WM_CREATE:
|
||||
return create_children() ? 0 : -1;
|
||||
case WM_SIZE:
|
||||
layout(LOWORD(lparam), HIWORD(lparam));
|
||||
return 0;
|
||||
case WM_SETFOCUS:
|
||||
SetFocus(editor_);
|
||||
return 0;
|
||||
case WM_COMMAND:
|
||||
if (reinterpret_cast<HWND>(lparam) == editor_ && HIWORD(wparam) == EN_CHANGE) {
|
||||
status_dirty_ = true;
|
||||
update_title();
|
||||
return 0;
|
||||
}
|
||||
execute_command(LOWORD(wparam));
|
||||
return 0;
|
||||
case WM_NOTIFY:
|
||||
if (reinterpret_cast<const NMHDR*>(lparam)->hwndFrom == editor_) {
|
||||
status_dirty_ = true;
|
||||
}
|
||||
return 0;
|
||||
case WM_INITMENU:
|
||||
update_menu(reinterpret_cast<HMENU>(wparam));
|
||||
return 0;
|
||||
case WM_TIMER:
|
||||
if (wparam == kStatusTimer && status_dirty_) {
|
||||
update_status();
|
||||
}
|
||||
return 0;
|
||||
case WM_DROPFILES:
|
||||
open_drop(reinterpret_cast<HDROP>(wparam));
|
||||
return 0;
|
||||
case WM_CLOSE:
|
||||
if (prompt_save_changes()) {
|
||||
DestroyWindow(main_window_);
|
||||
}
|
||||
return 0;
|
||||
case WM_DESTROY:
|
||||
KillTimer(main_window_, kStatusTimer);
|
||||
PostQuitMessage(0);
|
||||
return 0;
|
||||
default:
|
||||
return DefWindowProcW(main_window_, message, wparam, lparam);
|
||||
}
|
||||
}
|
||||
|
||||
bool create_children() {
|
||||
SetMenu(main_window_, build_menu());
|
||||
editor_ = CreateWindowExW(
|
||||
WS_EX_CLIENTEDGE, MSFTEDIT_CLASS, L"",
|
||||
WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_MULTILINE | ES_WANTRETURN |
|
||||
ES_NOHIDESEL | ES_AUTOVSCROLL,
|
||||
0, 0, 0, 0, main_window_, reinterpret_cast<HMENU>(1), instance_, nullptr);
|
||||
status_ = CreateWindowExW(0, STATUSCLASSNAMEW, nullptr, WS_CHILD | WS_VISIBLE,
|
||||
0, 0, 0, 0, main_window_, reinterpret_cast<HMENU>(2),
|
||||
instance_, nullptr);
|
||||
if (editor_ == nullptr || status_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SendMessageW(editor_, EM_SETEVENTMASK, 0, ENM_CHANGE | ENM_SELCHANGE);
|
||||
SendMessageW(editor_, EM_SETBKGNDCOLOR, 0, RGB(255, 255, 255));
|
||||
SendMessageW(editor_, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(16, 16));
|
||||
SendMessageW(editor_, EM_EXLIMITTEXT, 0, std::numeric_limits<LONG>::max());
|
||||
|
||||
CHARFORMAT2W format{sizeof(format)};
|
||||
format.dwMask = CFM_FACE | CFM_SIZE | CFM_CHARSET;
|
||||
format.yHeight = 220;
|
||||
format.bCharSet = DEFAULT_CHARSET;
|
||||
wcscpy_s(format.szFaceName, L"Segoe UI");
|
||||
SendMessageW(editor_, EM_SETCHARFORMAT, SCF_ALL, reinterpret_cast<LPARAM>(&format));
|
||||
|
||||
find_message_ = RegisterWindowMessageW(FINDMSGSTRINGW);
|
||||
DragAcceptFiles(main_window_, TRUE);
|
||||
SetTimer(main_window_, kStatusTimer, 250, nullptr);
|
||||
set_clean();
|
||||
update_status();
|
||||
SetFocus(editor_);
|
||||
return true;
|
||||
}
|
||||
|
||||
void layout(const int width, const int height) {
|
||||
SendMessageW(status_, WM_SIZE, 0, 0);
|
||||
RECT status_rect{};
|
||||
GetWindowRect(status_, &status_rect);
|
||||
const int status_height = status_rect.bottom - status_rect.top;
|
||||
MoveWindow(editor_, 0, 0, width, std::max(0, height - status_height), TRUE);
|
||||
const int parts[]{std::max(200, width - 330), std::max(320, width - 150), -1};
|
||||
SendMessageW(status_, SB_SETPARTS, std::size(parts), reinterpret_cast<LPARAM>(parts));
|
||||
status_dirty_ = true;
|
||||
}
|
||||
|
||||
void execute_command(const UINT command) {
|
||||
switch (command) {
|
||||
case cmd_file_new: new_document(); break;
|
||||
case cmd_file_open: open_dialog(); break;
|
||||
case cmd_file_save: save(false); break;
|
||||
case cmd_file_save_as: save(true); break;
|
||||
case cmd_file_print: print_document(); break;
|
||||
case cmd_file_exit: SendMessageW(main_window_, WM_CLOSE, 0, 0); break;
|
||||
case cmd_edit_undo: SendMessageW(editor_, EM_UNDO, 0, 0); break;
|
||||
case cmd_edit_redo: SendMessageW(editor_, EM_REDO, 0, 0); break;
|
||||
case cmd_edit_cut: SendMessageW(editor_, WM_CUT, 0, 0); break;
|
||||
case cmd_edit_copy: SendMessageW(editor_, WM_COPY, 0, 0); break;
|
||||
case cmd_edit_paste: SendMessageW(editor_, WM_PASTE, 0, 0); break;
|
||||
case cmd_edit_select_all: SendMessageW(editor_, EM_SETSEL, 0, -1); break;
|
||||
case cmd_edit_find: show_find(); break;
|
||||
case cmd_edit_find_next: find_next(find_flags_); break;
|
||||
case cmd_format_bold: toggle_effect(CFM_BOLD, CFE_BOLD); break;
|
||||
case cmd_format_italic: toggle_effect(CFM_ITALIC, CFE_ITALIC); break;
|
||||
case cmd_format_underline: toggle_effect(CFM_UNDERLINE, CFE_UNDERLINE); break;
|
||||
case cmd_format_font: choose_font(); break;
|
||||
case cmd_format_left: set_alignment(PFA_LEFT); break;
|
||||
case cmd_format_center: set_alignment(PFA_CENTER); break;
|
||||
case cmd_format_right: set_alignment(PFA_RIGHT); break;
|
||||
case cmd_format_justify: set_alignment(PFA_JUSTIFY); break;
|
||||
case cmd_tools_word_count: show_word_count(); break;
|
||||
case cmd_help_about: show_about(); break;
|
||||
default: break;
|
||||
}
|
||||
status_dirty_ = true;
|
||||
}
|
||||
|
||||
void update_menu(const HMENU menu) const {
|
||||
const bool can_undo = SendMessageW(editor_, EM_CANUNDO, 0, 0) != 0;
|
||||
const bool can_redo = SendMessageW(editor_, EM_CANREDO, 0, 0) != 0;
|
||||
CHARRANGE selection{};
|
||||
SendMessageW(editor_, EM_EXGETSEL, 0, reinterpret_cast<LPARAM>(&selection));
|
||||
EnableMenuItem(menu, cmd_edit_undo, MF_BYCOMMAND | (can_undo ? MF_ENABLED : MF_GRAYED));
|
||||
EnableMenuItem(menu, cmd_edit_redo, MF_BYCOMMAND | (can_redo ? MF_ENABLED : MF_GRAYED));
|
||||
EnableMenuItem(menu, cmd_edit_cut,
|
||||
MF_BYCOMMAND | (selection.cpMin != selection.cpMax ? MF_ENABLED : MF_GRAYED));
|
||||
EnableMenuItem(menu, cmd_edit_copy,
|
||||
MF_BYCOMMAND | (selection.cpMin != selection.cpMax ? MF_ENABLED : MF_GRAYED));
|
||||
EnableMenuItem(menu, cmd_edit_paste,
|
||||
MF_BYCOMMAND | (SendMessageW(editor_, EM_CANPASTE, 0, 0) ? MF_ENABLED : MF_GRAYED));
|
||||
}
|
||||
|
||||
void new_document() {
|
||||
if (!prompt_save_changes()) {
|
||||
return;
|
||||
}
|
||||
loading_ = true;
|
||||
SetWindowTextW(editor_, L"");
|
||||
current_path_.clear();
|
||||
loading_ = false;
|
||||
set_clean();
|
||||
}
|
||||
|
||||
void open_dialog() {
|
||||
if (!prompt_save_changes()) {
|
||||
return;
|
||||
}
|
||||
std::array<wchar_t, 32768> path{};
|
||||
OPENFILENAMEW dialog{sizeof(dialog)};
|
||||
dialog.hwndOwner = main_window_;
|
||||
dialog.lpstrFilter = L"Rich Text Format (*.rtf)\0*.rtf\0Text files (*.txt)\0*.txt\0All files (*.*)\0*.*\0";
|
||||
dialog.lpstrFile = path.data();
|
||||
dialog.nMaxFile = static_cast<DWORD>(path.size());
|
||||
dialog.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
|
||||
if (GetOpenFileNameW(&dialog)) {
|
||||
open_path(path.data());
|
||||
}
|
||||
}
|
||||
|
||||
void open_drop(const HDROP drop) {
|
||||
std::array<wchar_t, 32768> path{};
|
||||
if (DragQueryFileW(drop, 0, path.data(), static_cast<UINT>(path.size())) != 0 &&
|
||||
prompt_save_changes()) {
|
||||
open_path(path.data());
|
||||
}
|
||||
DragFinish(drop);
|
||||
}
|
||||
|
||||
void open_path(const std::wstring& path) {
|
||||
if (lower_extension(path) == L".doc") {
|
||||
MessageBoxW(main_window_,
|
||||
L"The native x64 document engine does not read binary Word 1.x .doc files yet. "
|
||||
L"Open an RTF or text document for this migration milestone.",
|
||||
kProductName, MB_OK | MB_ICONINFORMATION);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
loading_ = true;
|
||||
if (lower_extension(path) == L".rtf") {
|
||||
const HANDLE file = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (file == INVALID_HANDLE_VALUE) {
|
||||
throw std::runtime_error("CreateFileW failed: " + std::to_string(GetLastError()));
|
||||
}
|
||||
EDITSTREAM stream{reinterpret_cast<DWORD_PTR>(file), 0, &stream_in};
|
||||
SetWindowTextW(editor_, L"");
|
||||
SendMessageW(editor_, EM_STREAMIN, SF_RTF, reinterpret_cast<LPARAM>(&stream));
|
||||
CloseHandle(file);
|
||||
if (stream.dwError != 0) {
|
||||
throw std::runtime_error("Rich Edit could not read the RTF stream");
|
||||
}
|
||||
} else {
|
||||
const auto text = decode_text(read_file(path));
|
||||
SetWindowTextW(editor_, text.c_str());
|
||||
}
|
||||
current_path_ = path;
|
||||
loading_ = false;
|
||||
set_clean();
|
||||
} catch (const std::exception& error) {
|
||||
loading_ = false;
|
||||
const std::wstring detail(error.what(), error.what() + std::strlen(error.what()));
|
||||
MessageBoxW(main_window_, (L"Could not open the document.\n\n" + detail).c_str(),
|
||||
kProductName, MB_OK | MB_ICONERROR);
|
||||
}
|
||||
}
|
||||
|
||||
bool save(const bool save_as) {
|
||||
std::wstring path = current_path_;
|
||||
if (save_as || path.empty()) {
|
||||
std::array<wchar_t, 32768> selected{};
|
||||
if (!path.empty()) {
|
||||
wcsncpy_s(selected.data(), selected.size(), path.c_str(), _TRUNCATE);
|
||||
} else {
|
||||
wcscpy_s(selected.data(), selected.size(), L"Document.rtf");
|
||||
}
|
||||
OPENFILENAMEW dialog{sizeof(dialog)};
|
||||
dialog.hwndOwner = main_window_;
|
||||
dialog.lpstrFilter = L"Rich Text Format (*.rtf)\0*.rtf\0UTF-8 text (*.txt)\0*.txt\0";
|
||||
dialog.lpstrFile = selected.data();
|
||||
dialog.nMaxFile = static_cast<DWORD>(selected.size());
|
||||
dialog.lpstrDefExt = L"rtf";
|
||||
dialog.Flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
|
||||
if (!GetSaveFileNameW(&dialog)) {
|
||||
return false;
|
||||
}
|
||||
path = selected.data();
|
||||
}
|
||||
|
||||
try {
|
||||
if (lower_extension(path) == L".rtf") {
|
||||
const HANDLE file = CreateFileW(path.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (file == INVALID_HANDLE_VALUE) {
|
||||
throw std::runtime_error("CreateFileW failed: " + std::to_string(GetLastError()));
|
||||
}
|
||||
EDITSTREAM stream{reinterpret_cast<DWORD_PTR>(file), 0, &stream_out};
|
||||
SendMessageW(editor_, EM_STREAMOUT, SF_RTF, reinterpret_cast<LPARAM>(&stream));
|
||||
CloseHandle(file);
|
||||
if (stream.dwError != 0) {
|
||||
throw std::runtime_error("Rich Edit could not write the RTF stream");
|
||||
}
|
||||
} else {
|
||||
write_file(path, encode_utf8(document_text()));
|
||||
}
|
||||
current_path_ = path;
|
||||
set_clean();
|
||||
return true;
|
||||
} catch (const std::exception& error) {
|
||||
const std::wstring detail(error.what(), error.what() + std::strlen(error.what()));
|
||||
MessageBoxW(main_window_, (L"Could not save the document.\n\n" + detail).c_str(),
|
||||
kProductName, MB_OK | MB_ICONERROR);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool prompt_save_changes() {
|
||||
if (SendMessageW(editor_, EM_GETMODIFY, 0, 0) == 0) {
|
||||
return true;
|
||||
}
|
||||
const int answer = MessageBoxW(main_window_, L"Save changes to this document?",
|
||||
kProductName, MB_YESNOCANCEL | MB_ICONQUESTION);
|
||||
return answer == IDNO || (answer == IDYES && save(false));
|
||||
}
|
||||
|
||||
void set_clean() {
|
||||
SendMessageW(editor_, EM_SETMODIFY, FALSE, 0);
|
||||
update_title();
|
||||
status_dirty_ = true;
|
||||
}
|
||||
|
||||
void update_title() const {
|
||||
std::wstring name = current_path_.empty()
|
||||
? L"Document"
|
||||
: std::filesystem::path(current_path_).filename().wstring();
|
||||
if (!loading_ && SendMessageW(editor_, EM_GETMODIFY, 0, 0) != 0) {
|
||||
name += L" *";
|
||||
}
|
||||
SetWindowTextW(main_window_, (name + L" - " + kProductName).c_str());
|
||||
}
|
||||
|
||||
[[nodiscard]] std::wstring document_text() const {
|
||||
const int length = GetWindowTextLengthW(editor_);
|
||||
std::wstring text(static_cast<std::size_t>(length) + 1, L'\0');
|
||||
if (length != 0) {
|
||||
GetWindowTextW(editor_, text.data(), length + 1);
|
||||
}
|
||||
text.resize(static_cast<std::size_t>(length));
|
||||
return text;
|
||||
}
|
||||
|
||||
[[nodiscard]] static std::pair<std::size_t, std::size_t> count_text(
|
||||
const std::wstring_view text) {
|
||||
std::size_t words = 0;
|
||||
std::size_t characters = 0;
|
||||
bool in_word = false;
|
||||
for (const wchar_t character : text) {
|
||||
if (character != L'\r' && character != L'\n') {
|
||||
++characters;
|
||||
}
|
||||
const bool word_character = std::iswalnum(character) != 0 || character == L'_';
|
||||
if (word_character && !in_word) {
|
||||
++words;
|
||||
}
|
||||
in_word = word_character;
|
||||
}
|
||||
return {words, characters};
|
||||
}
|
||||
|
||||
void update_status() {
|
||||
const auto text = document_text();
|
||||
const auto [words, characters] = count_text(text);
|
||||
CHARRANGE selection{};
|
||||
SendMessageW(editor_, EM_EXGETSEL, 0, reinterpret_cast<LPARAM>(&selection));
|
||||
const LRESULT line = SendMessageW(editor_, EM_LINEFROMCHAR, selection.cpMin, 0);
|
||||
const LRESULT line_start = SendMessageW(editor_, EM_LINEINDEX, line, 0);
|
||||
|
||||
const std::wstring file = current_path_.empty() ? L" New document" : L" " + current_path_;
|
||||
const std::wstring counts = L"Words: " + std::to_wstring(words) +
|
||||
L" Characters: " + std::to_wstring(characters);
|
||||
const std::wstring position = L"Ln " + std::to_wstring(line + 1) +
|
||||
L", Col " + std::to_wstring(selection.cpMin - line_start + 1);
|
||||
SendMessageW(status_, SB_SETTEXTW, 0, reinterpret_cast<LPARAM>(file.c_str()));
|
||||
SendMessageW(status_, SB_SETTEXTW, 1, reinterpret_cast<LPARAM>(counts.c_str()));
|
||||
SendMessageW(status_, SB_SETTEXTW, 2, reinterpret_cast<LPARAM>(position.c_str()));
|
||||
status_dirty_ = false;
|
||||
}
|
||||
|
||||
void show_word_count() const {
|
||||
const auto [words, characters] = count_text(document_text());
|
||||
const std::wstring message = L"Words: " + std::to_wstring(words) +
|
||||
L"\nCharacters (excluding paragraph marks): " +
|
||||
std::to_wstring(characters);
|
||||
MessageBoxW(main_window_, message.c_str(), L"Word Count", MB_OK | MB_ICONINFORMATION);
|
||||
}
|
||||
|
||||
void toggle_effect(const DWORD mask, const DWORD effect) const {
|
||||
CHARFORMAT2W format{sizeof(format)};
|
||||
format.dwMask = mask;
|
||||
SendMessageW(editor_, EM_GETCHARFORMAT, SCF_SELECTION, reinterpret_cast<LPARAM>(&format));
|
||||
format.dwEffects ^= effect;
|
||||
SendMessageW(editor_, EM_SETCHARFORMAT, SCF_SELECTION, reinterpret_cast<LPARAM>(&format));
|
||||
}
|
||||
|
||||
void set_alignment(const WORD alignment) const {
|
||||
PARAFORMAT2 format{sizeof(format)};
|
||||
format.dwMask = PFM_ALIGNMENT;
|
||||
format.wAlignment = alignment;
|
||||
SendMessageW(editor_, EM_SETPARAFORMAT, 0, reinterpret_cast<LPARAM>(&format));
|
||||
}
|
||||
|
||||
void choose_font() const {
|
||||
CHARFORMAT2W current{sizeof(current)};
|
||||
SendMessageW(editor_, EM_GETCHARFORMAT, SCF_SELECTION, reinterpret_cast<LPARAM>(¤t));
|
||||
HDC dc = GetDC(editor_);
|
||||
LOGFONTW font{};
|
||||
font.lfHeight = -MulDiv(current.yHeight / 20, GetDeviceCaps(dc, LOGPIXELSY), 72);
|
||||
font.lfWeight = (current.dwEffects & CFE_BOLD) != 0 ? FW_BOLD : FW_NORMAL;
|
||||
font.lfItalic = (current.dwEffects & CFE_ITALIC) != 0;
|
||||
font.lfUnderline = (current.dwEffects & CFE_UNDERLINE) != 0;
|
||||
wcsncpy_s(font.lfFaceName, current.szFaceName, _TRUNCATE);
|
||||
ReleaseDC(editor_, dc);
|
||||
|
||||
CHOOSEFONTW dialog{sizeof(dialog)};
|
||||
dialog.hwndOwner = main_window_;
|
||||
dialog.lpLogFont = &font;
|
||||
dialog.iPointSize = current.yHeight / 2;
|
||||
dialog.rgbColors = current.crTextColor;
|
||||
dialog.Flags = CF_SCREENFONTS | CF_INITTOLOGFONTSTRUCT | CF_EFFECTS;
|
||||
if (!ChooseFontW(&dialog)) {
|
||||
return;
|
||||
}
|
||||
|
||||
CHARFORMAT2W selected{sizeof(selected)};
|
||||
selected.dwMask = CFM_FACE | CFM_SIZE | CFM_COLOR | CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE;
|
||||
selected.yHeight = dialog.iPointSize * 2;
|
||||
selected.crTextColor = dialog.rgbColors;
|
||||
selected.dwEffects = (font.lfWeight >= FW_BOLD ? CFE_BOLD : 0) |
|
||||
(font.lfItalic ? CFE_ITALIC : 0) |
|
||||
(font.lfUnderline ? CFE_UNDERLINE : 0);
|
||||
wcsncpy_s(selected.szFaceName, font.lfFaceName, _TRUNCATE);
|
||||
SendMessageW(editor_, EM_SETCHARFORMAT, SCF_SELECTION, reinterpret_cast<LPARAM>(&selected));
|
||||
}
|
||||
|
||||
void show_find() {
|
||||
if (find_dialog_ != nullptr) {
|
||||
SetForegroundWindow(find_dialog_);
|
||||
return;
|
||||
}
|
||||
find_state_ = {};
|
||||
find_state_.lStructSize = sizeof(find_state_);
|
||||
find_state_.hwndOwner = main_window_;
|
||||
find_state_.Flags = FR_DOWN;
|
||||
find_state_.lpstrFindWhat = find_text_.data();
|
||||
find_state_.wFindWhatLen = static_cast<WORD>(find_text_.size());
|
||||
find_dialog_ = FindTextW(&find_state_);
|
||||
}
|
||||
|
||||
void on_find_message(const FINDREPLACEW& message) {
|
||||
if ((message.Flags & FR_DIALOGTERM) != 0) {
|
||||
find_dialog_ = nullptr;
|
||||
return;
|
||||
}
|
||||
if ((message.Flags & FR_FINDNEXT) != 0) {
|
||||
find_flags_ = message.Flags;
|
||||
find_next(message.Flags);
|
||||
}
|
||||
}
|
||||
|
||||
void find_next(const DWORD flags) {
|
||||
if (find_text_[0] == L'\0') {
|
||||
show_find();
|
||||
return;
|
||||
}
|
||||
CHARRANGE selection{};
|
||||
SendMessageW(editor_, EM_EXGETSEL, 0, reinterpret_cast<LPARAM>(&selection));
|
||||
const LONG text_length = GetWindowTextLengthW(editor_);
|
||||
FINDTEXTEXW search{};
|
||||
search.lpstrText = find_text_.data();
|
||||
const bool down = (flags & FR_DOWN) != 0;
|
||||
search.chrg = down ? CHARRANGE{selection.cpMax, text_length}
|
||||
: CHARRANGE{selection.cpMin - 1, 0};
|
||||
DWORD options = down ? FR_DOWN : 0;
|
||||
options |= flags & (FR_MATCHCASE | FR_WHOLEWORD);
|
||||
LRESULT result = SendMessageW(editor_, EM_FINDTEXTEXW, options,
|
||||
reinterpret_cast<LPARAM>(&search));
|
||||
if (result == -1) {
|
||||
search.chrg = down ? CHARRANGE{0, text_length} : CHARRANGE{text_length, 0};
|
||||
result = SendMessageW(editor_, EM_FINDTEXTEXW, options,
|
||||
reinterpret_cast<LPARAM>(&search));
|
||||
}
|
||||
if (result == -1) {
|
||||
MessageBeep(MB_ICONINFORMATION);
|
||||
return;
|
||||
}
|
||||
SendMessageW(editor_, EM_EXSETSEL, 0, reinterpret_cast<LPARAM>(&search.chrgText));
|
||||
SendMessageW(editor_, EM_SCROLLCARET, 0, 0);
|
||||
SetFocus(editor_);
|
||||
}
|
||||
|
||||
void print_document() const {
|
||||
PRINTDLGW dialog{sizeof(dialog)};
|
||||
dialog.hwndOwner = main_window_;
|
||||
dialog.Flags = PD_RETURNDC | PD_NOSELECTION | PD_USEDEVMODECOPIESANDCOLLATE;
|
||||
if (!PrintDlgW(&dialog)) {
|
||||
return;
|
||||
}
|
||||
DOCINFOW document{sizeof(document)};
|
||||
document.lpszDocName = current_path_.empty() ? L"Document" : current_path_.c_str();
|
||||
if (StartDocW(dialog.hDC, &document) <= 0) {
|
||||
DeleteDC(dialog.hDC);
|
||||
return;
|
||||
}
|
||||
|
||||
FORMATRANGE range{};
|
||||
range.hdc = dialog.hDC;
|
||||
range.hdcTarget = dialog.hDC;
|
||||
const int dpi_x = GetDeviceCaps(dialog.hDC, LOGPIXELSX);
|
||||
const int dpi_y = GetDeviceCaps(dialog.hDC, LOGPIXELSY);
|
||||
range.rcPage = {0, 0,
|
||||
MulDiv(GetDeviceCaps(dialog.hDC, PHYSICALWIDTH), 1440, dpi_x),
|
||||
MulDiv(GetDeviceCaps(dialog.hDC, PHYSICALHEIGHT), 1440, dpi_y)};
|
||||
constexpr LONG margin = 1080;
|
||||
range.rc = {range.rcPage.left + margin, range.rcPage.top + margin,
|
||||
range.rcPage.right - margin, range.rcPage.bottom - margin};
|
||||
range.chrg = {0, -1};
|
||||
|
||||
LONG next = 0;
|
||||
const LONG length = GetWindowTextLengthW(editor_);
|
||||
bool failed = false;
|
||||
while (next < length && !failed) {
|
||||
if (StartPage(dialog.hDC) <= 0) {
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
range.chrg.cpMin = next;
|
||||
next = static_cast<LONG>(SendMessageW(editor_, EM_FORMATRANGE, TRUE,
|
||||
reinterpret_cast<LPARAM>(&range)));
|
||||
if (EndPage(dialog.hDC) <= 0 || next <= range.chrg.cpMin) {
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
SendMessageW(editor_, EM_FORMATRANGE, FALSE, 0);
|
||||
failed ? AbortDoc(dialog.hDC) : EndDoc(dialog.hDC);
|
||||
DeleteDC(dialog.hDC);
|
||||
}
|
||||
|
||||
void show_about() const {
|
||||
const std::wstring message =
|
||||
L"Microsoft Word for Windows — native x64 migration\n\n"
|
||||
L"This executable is AMD64 and contains no 16-bit assembly.\n"
|
||||
L"Legacy assembly modules accounted for: " +
|
||||
std::to_wstring(opus::x64::assembly_manifest_count()) +
|
||||
L"\n\nCurrent document formats: RTF and UTF-8 text.";
|
||||
MessageBoxW(main_window_, message.c_str(), L"About", MB_OK | MB_ICONINFORMATION);
|
||||
}
|
||||
|
||||
static DWORD CALLBACK stream_in(const DWORD_PTR cookie, const LPBYTE buffer,
|
||||
const LONG byte_count, LONG* bytes_read) {
|
||||
DWORD read = 0;
|
||||
if (!ReadFile(reinterpret_cast<HANDLE>(cookie), buffer,
|
||||
static_cast<DWORD>(byte_count), &read, nullptr)) {
|
||||
*bytes_read = 0;
|
||||
return GetLastError();
|
||||
}
|
||||
*bytes_read = static_cast<LONG>(read);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DWORD CALLBACK stream_out(const DWORD_PTR cookie, const LPBYTE buffer,
|
||||
const LONG byte_count, LONG* bytes_written) {
|
||||
DWORD written = 0;
|
||||
if (!WriteFile(reinterpret_cast<HANDLE>(cookie), buffer,
|
||||
static_cast<DWORD>(byte_count), &written, nullptr)) {
|
||||
*bytes_written = 0;
|
||||
return GetLastError();
|
||||
}
|
||||
*bytes_written = static_cast<LONG>(written);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void show_error(const std::wstring_view message, const DWORD error) const {
|
||||
const std::wstring detail = std::wstring(message) + L".\n\n" + system_error_message(error);
|
||||
MessageBoxW(main_window_, detail.c_str(), kProductName, MB_OK | MB_ICONERROR);
|
||||
}
|
||||
|
||||
HINSTANCE instance_{};
|
||||
HMODULE rich_edit_library_{};
|
||||
HWND main_window_{};
|
||||
HWND editor_{};
|
||||
HWND status_{};
|
||||
HWND find_dialog_{};
|
||||
UINT find_message_{};
|
||||
FINDREPLACEW find_state_{};
|
||||
std::array<wchar_t, 256> find_text_{};
|
||||
DWORD find_flags_{FR_DOWN};
|
||||
std::wstring current_path_;
|
||||
bool loading_{};
|
||||
bool status_dirty_{true};
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
int WINAPI wWinMain(const HINSTANCE instance, HINSTANCE, PWSTR, const int show_command) {
|
||||
WordApplication application(instance);
|
||||
return application.run(show_command);
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
#include <windows.h>
|
||||
|
||||
1 RT_MANIFEST "winword.manifest"
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 0,1,0,0
|
||||
PRODUCTVERSION 0,1,0,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS VS_FF_DEBUG
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS VOS_NT_WINDOWS32
|
||||
FILETYPE VFT_APP
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Microsoft (historical source)"
|
||||
VALUE "FileDescription", "Microsoft Word for Windows x64 port"
|
||||
VALUE "FileVersion", "0.1.0"
|
||||
VALUE "InternalName", "WINWORD"
|
||||
VALUE "OriginalFilename", "WINWORD_BOOTSTRAP.exe"
|
||||
VALUE "ProductName", "Microsoft Word x64 Port"
|
||||
VALUE "ProductVersion", "0.1.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x0409, 1200
|
||||
END
|
||||
END
|
||||
Reference in New Issue
Block a user