ciderpress/app/Preferences.cpp
Andy McFadden 2cb5bf0dcc Fix file viewer layout
If scaling > 100% is enabled on the primary display, the controls in
the dialog window get bigger.  The file viewer uses hard-coded values
for the initial and minimum window size, which causes the expanded
controls to draw on top of each other.  (Issue #53.)

We now query the system for the scale factor, and adjust the window
size accordingly.
2022-01-07 11:34:46 -08:00

572 lines
22 KiB
C++

/*
* CiderPress
* Copyright (C) 2007, 2008 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Save and restore preferences from the config file.
*/
#include "stdafx.h"
#include "Preferences.h"
#include "NufxArchive.h"
#include "MyApp.h"
#include "../util/UtilLib.h"
static const WCHAR kDefaultTempPath[] = L".";
/* registry section for columns */
static const WCHAR kColumnSect[] = L"columns";
/* registry section for file add options */
static const WCHAR kAddSect[] = L"add";
/* registry section for extraction options */
static const WCHAR kExtractSect[] = L"extract";
/* registry section for view options */
static const WCHAR kViewSect[] = L"view";
/* registry section for logical/physical volume operations */
static const WCHAR kVolumeSect[] = L"volume";
/* registry section for file-to-disk options */
//static const WCHAR kConvDiskSect[] = L"conv-disk";
/* registry section for disk-to-file options */
static const WCHAR kConvFileSect[] = L"conv-file";
/* registry section for folders */
static const WCHAR kFolderSect[] = L"folders";
/* registry section for preferences on property pages */
static const WCHAR kPrefsSect[] = L"prefs";
/* registry section for miscellaneous settings */
static const WCHAR kMiscSect[] = L"misc";
/*
* Map PrefNum to type and registry string.
*
* To make life easier, we require that the PrefNum (first entry) match the
* offset in the table. That way instead of searching for a match we can just
* index into the table.
*/
const Preferences::PrefMap Preferences::fPrefMaps[kPrefNumLastEntry] = {
/**/ { kPrefNumUnknown, kPTNone, NULL, NULL },
{ kPrAddIncludeSubFolders, kBool, kAddSect, L"include-sub-folders" },
{ kPrAddStripFolderNames, kBool, kAddSect, L"strip-folder-names" },
{ kPrAddOverwriteExisting, kBool, kAddSect, L"overwrite-existing" },
{ kPrAddTypePreservation, kLong, kAddSect, L"type-preservation" },
{ kPrAddConvEOL, kLong, kAddSect, L"conv-eol" },
// { kPrExtractPath, kString, kExtractSect, L"path" },
{ kPrExtractConvEOL, kLong, kExtractSect, L"conv-eol" },
{ kPrExtractConvHighASCII, kBool, kExtractSect, L"conv-high-ascii" },
{ kPrExtractIncludeData, kBool, kExtractSect, L"include-data" },
{ kPrExtractIncludeRsrc, kBool, kExtractSect, L"include-rsrc" },
{ kPrExtractIncludeDisk, kBool, kExtractSect, L"include-disk" },
{ kPrExtractEnableReformat, kBool, kExtractSect, L"enable-reformat" },
{ kPrExtractDiskTo2MG, kBool, kExtractSect, L"disk-to-2mg" },
{ kPrExtractAddTypePreservation, kBool, kExtractSect, L"add-type-preservation" },
{ kPrExtractAddExtension, kBool, kExtractSect, L"add-extension" },
{ kPrExtractStripFolderNames, kBool, kExtractSect, L"strip-folder-names" },
{ kPrExtractOverwriteExisting, kBool, kExtractSect, L"overwrite-existing" },
// { kPrViewIncludeDataForks, kBool, kViewSect, L"include-data-forks" },
// { kPrViewIncludeRsrcForks, kBool, kViewSect, L"include-rsrc-forks" },
// { kPrViewIncludeDiskImages, kBool, kViewSect, L"include-disk-images" },
// { kPrViewIncludeComments, kBool, kViewSect, L"include-comments" },
{ kPrConvFileEmptyFolders, kBool, kConvFileSect, L"preserve-empty-folders" },
{ kPrOpenArchiveFolder, kString, kFolderSect, L"open-archive" },
{ kPrConvertArchiveFolder, kString, kFolderSect, L"convert-archive" },
{ kPrAddFileFolder, kString, kFolderSect, L"add-file" },
{ kPrExtractFileFolder, kString, kFolderSect, L"extract-file" },
{ kPrVolumeFilter, kLong, kVolumeSect, L"open-filter" },
//{ kPrVolumeReadOnly, kBool, kVolumeSect, L"read-only" },
{ kPrCassetteAlgorithm, kLong, kVolumeSect, L"cassette-algorithm" },
{ kPrOpenWAVFolder, kString, kFolderSect, L"open-wav" },
{ kPrMimicShrinkIt, kBool, kPrefsSect, L"mimic-shrinkit" },
{ kPrBadMacSHK, kBool, kPrefsSect, L"bad-mac-shk" },
{ kPrReduceSHKErrorChecks, kBool, kPrefsSect, L"reduce-shk-error-checks" },
{ kPrCoerceDOSFilenames, kBool, kPrefsSect, L"coerce-dos-filenames" },
{ kPrSpacesToUnder, kBool, kPrefsSect, L"spaces-to-under" },
{ kPrPasteJunkPaths, kBool, kPrefsSect, L"paste-junk-paths" },
{ kPrBeepOnSuccess, kBool, kPrefsSect, L"beep-on-success" },
{ kPrQueryImageFormat, kBool, kPrefsSect, L"query-image-format" },
{ kPrOpenVolumeRO, kBool, kPrefsSect, L"open-volume-ro" },
{ kPrOpenVolumePhys0, kBool, kPrefsSect, L"open-volume-phys0" },
{ kPrProDOSAllowLower, kBool, kPrefsSect, L"prodos-allow-lower" },
{ kPrProDOSUseSparse, kBool, kPrefsSect, L"prodos-use-sparse" },
{ kPrCompressionType, kLong, kPrefsSect, L"compression-type" },
{ kPrMaxViewFileSize, kLong, kPrefsSect, L"max-view-file-size" },
{ kPrNoWrapText, kBool, kPrefsSect, L"no-wrap-text" },
{ kPrHighlightHexDump, kBool, kPrefsSect, L"highlight-hex-dump" },
{ kPrHighlightBASIC, kBool, kPrefsSect, L"highlight-basic" },
{ kPrConvHiResBlackWhite, kBool, kPrefsSect, L"conv-hi-res-black-white" },
{ kPrConvDHRAlgorithm, kLong, kPrefsSect, L"dhr-algorithm" },
{ kPrRelaxGfxTypeCheck, kBool, kPrefsSect, L"relax-gfx-type-check" },
{ kPrDisasmOneByteBrkCop, kBool, kPrefsSect, L"disasm-onebytebrkcop" },
{ kPrConvMouseTextToASCII, kBool, kPrefsSect, L"conv-mouse-text-to-ascii" },
//{ kPrEOLConvRaw, kBool, kPrefsSect, L"eol-conv-raw" },
{ kPrConvTextEOL_HA, kBool, kPrefsSect, L"conv-eol-ha" },
{ kPrConvPascalText, kBool, kPrefsSect, L"conv-pascal-text" },
{ kPrConvPascalCode, kBool, kPrefsSect, L"conv-pascal-code" },
{ kPrConvCPMText, kBool, kPrefsSect, L"conv-cpm-text" },
{ kPrConvApplesoft, kBool, kPrefsSect, L"conv-applesoft" },
{ kPrConvInteger, kBool, kPrefsSect, L"conv-integer" },
{ kPrConvBusiness, kBool, kPrefsSect, L"conv-business" },
{ kPrConvGWP, kBool, kPrefsSect, L"conv-gwp" },
{ kPrConvText8, kBool, kPrefsSect, L"conv-text8" },
{ kPrConvGutenberg, kBool, kPrefsSect, L"conv-gutenberg" },
{ kPrConvAWP, kBool, kPrefsSect, L"conv-awp" },
{ kPrConvADB, kBool, kPrefsSect, L"conv-adb" },
{ kPrConvASP, kBool, kPrefsSect, L"conv-asp" },
{ kPrConvSCAssem, kBool, kPrefsSect, L"conv-scassem" },
{ kPrConvDisasm, kBool, kPrefsSect, L"conv-disasm" },
{ kPrConvHiRes, kBool, kPrefsSect, L"conv-hi-res" },
{ kPrConvDHR, kBool, kPrefsSect, L"conv-dhr" },
{ kPrConvSHR, kBool, kPrefsSect, L"conv-shr" },
{ kPrConvPrintShop, kBool, kPrefsSect, L"conv-print-shop" },
{ kPrConvMacPaint, kBool, kPrefsSect, L"conv-mac-paint" },
{ kPrConvProDOSFolder, kBool, kPrefsSect, L"conv-prodos-folder" },
{ kPrConvResources, kBool, kPrefsSect, L"conv-resources" },
{ kPrTempPath, kString, kPrefsSect, L"temp-path" },
{ kPrExtViewerExts, kString, kPrefsSect, L"extviewer-exts" },
{ kPrLastOpenFilterIndex, kLong, kMiscSect, L"open-filter-index" },
/**/ { kPrefNumLastRegistry, kPTNone, NULL, NULL },
{ kPrViewTextTypeFace, kString, NULL, NULL },
{ kPrViewTextPointSize, kLong, NULL, NULL },
{ kPrFileViewerWidth, kLong, NULL, NULL },
{ kPrFileViewerHeight, kLong, NULL, NULL },
{ kPrDiskImageCreateFormat, kLong, NULL, NULL },
};
Preferences::Preferences(void)
{
LOGI("Initializing Preferences");
ScanPrefMaps(); // sanity-check the table
memset(fValues, 0, sizeof(fValues));
SetPrefBool(kPrAddIncludeSubFolders, true);
SetPrefBool(kPrAddStripFolderNames, false);
SetPrefBool(kPrAddOverwriteExisting, false);
SetPrefLong(kPrAddTypePreservation, 1); // kPreserveTypes
SetPrefLong(kPrAddConvEOL, 1); // kConvEOLType
InitFolders(); // set default add/extract folders; overriden by reg
SetPrefLong(kPrExtractConvEOL, 0); // kConvEOLNone
SetPrefBool(kPrExtractConvHighASCII, true);
SetPrefBool(kPrExtractIncludeData, true);
SetPrefBool(kPrExtractIncludeRsrc, false);
SetPrefBool(kPrExtractIncludeDisk, true);
SetPrefBool(kPrExtractEnableReformat, false);
SetPrefBool(kPrExtractDiskTo2MG, false);
SetPrefBool(kPrExtractAddTypePreservation, true);
SetPrefBool(kPrExtractAddExtension, false);
SetPrefBool(kPrExtractStripFolderNames, false);
SetPrefBool(kPrExtractOverwriteExisting, false);
// SetPrefBool(kPrViewIncludeDataForks, true);
// SetPrefBool(kPrViewIncludeRsrcForks, false);
// SetPrefBool(kPrViewIncludeDiskImages, false);
// SetPrefBool(kPrViewIncludeComments, false);
SetPrefBool(kPrConvFileEmptyFolders, true);
// string kPrOpenArchiveFolder
// string kPrAddFileFolder
// string kPrExtractFileFolder
SetPrefLong(kPrVolumeFilter, 0);
//SetPrefBool(kPrVolumeReadOnly, true);
SetPrefLong(kPrCassetteAlgorithm, 0);
// string kPrOpenWAVFolder
SetPrefBool(kPrMimicShrinkIt, false);
SetPrefBool(kPrBadMacSHK, false);
SetPrefBool(kPrReduceSHKErrorChecks, false);
SetPrefBool(kPrCoerceDOSFilenames, false);
SetPrefBool(kPrSpacesToUnder, false);
SetPrefBool(kPrPasteJunkPaths, true);
SetPrefBool(kPrBeepOnSuccess, true);
SetPrefBool(kPrQueryImageFormat, false);
SetPrefBool(kPrOpenVolumeRO, true);
SetPrefBool(kPrOpenVolumePhys0, false);
SetPrefBool(kPrProDOSAllowLower, false);
SetPrefBool(kPrProDOSUseSparse, true);
SetPrefLong(kPrCompressionType, DefaultCompressionType());
SetPrefLong(kPrMaxViewFileSize, 1024*1024); // 1MB
SetPrefBool(kPrNoWrapText, false);
SetPrefBool(kPrHighlightHexDump, false);
SetPrefBool(kPrHighlightBASIC, false);
SetPrefBool(kPrConvHiResBlackWhite, false);
SetPrefLong(kPrConvDHRAlgorithm, 1); // latched
SetPrefBool(kPrRelaxGfxTypeCheck, true);
SetPrefBool(kPrDisasmOneByteBrkCop, false);
SetPrefBool(kPrConvMouseTextToASCII, false);
//SetPrefBool(kPrEOLConvRaw, true);
SetPrefBool(kPrConvTextEOL_HA, true);
SetPrefBool(kPrConvPascalText, true);
SetPrefBool(kPrConvPascalCode, true);
SetPrefBool(kPrConvCPMText, true);
SetPrefBool(kPrConvApplesoft, true);
SetPrefBool(kPrConvInteger, true);
SetPrefBool(kPrConvBusiness, true);
SetPrefBool(kPrConvGWP, true);
SetPrefBool(kPrConvText8, true);
SetPrefBool(kPrConvGutenberg, true);
SetPrefBool(kPrConvAWP, true);
SetPrefBool(kPrConvADB, true);
SetPrefBool(kPrConvASP, true);
SetPrefBool(kPrConvSCAssem, true);
SetPrefBool(kPrConvDisasm, true);
SetPrefBool(kPrConvHiRes, true);
SetPrefBool(kPrConvDHR, true);
SetPrefBool(kPrConvSHR, true);
SetPrefBool(kPrConvPrintShop, true);
SetPrefBool(kPrConvMacPaint, true);
SetPrefBool(kPrConvProDOSFolder, true);
SetPrefBool(kPrConvResources, true);
InitTempPath(); // set default for kPrTempPath
SetPrefString(kPrExtViewerExts, L"gif; jpg; jpeg");
SetPrefLong(kPrLastOpenFilterIndex, 0);
SetPrefString(kPrViewTextTypeFace, L"Courier New");
SetPrefLong(kPrViewTextPointSize, 10);
float scaleFactor = GetDesktopScaleFactor();
/* exact width for 80-column text; note text is scaled up */
long width = (int)(680 * scaleFactor) + ::GetSystemMetrics(SM_CXVSCROLL);
/* exact height for file viewer to show IIgs graphic at 1.0x */
long height = (int)(516 * scaleFactor);
if (GetSystemMetrics(SM_CXSCREEN) < width)
width = GetSystemMetrics(SM_CXSCREEN);
if (GetSystemMetrics(SM_CYSCREEN) < height)
height = GetSystemMetrics(SM_CYSCREEN); // may overlap system bar
SetPrefLong(kPrFileViewerWidth, width);
SetPrefLong(kPrFileViewerHeight, height);
SetPrefLong(kPrDiskImageCreateFormat, -1);
}
/*
* ==========================================================================
* ColumnLayout
* ==========================================================================
*/
void ColumnLayout::LoadFromRegistry(const WCHAR* section)
{
WCHAR numBuf[8];
int i;
for (i = 0; i < kNumVisibleColumns; i++) {
wsprintf(numBuf, L"%d", i);
fColumnWidth[i] = gMyApp.GetProfileInt(section, numBuf,
fColumnWidth[i]);
fColumnWidth[i] = gMyApp.GetProfileInt(section, numBuf,
fColumnWidth[i]);
}
fSortColumn = gMyApp.GetProfileInt(section, L"sort-column", fSortColumn);
fAscending = (gMyApp.GetProfileInt(section, L"ascending", fAscending) != 0);
}
void ColumnLayout::SaveToRegistry(const WCHAR* section)
{
WCHAR numBuf[8];
int i;
for (i = 0; i < kNumVisibleColumns; i++) {
wsprintf(numBuf, L"%d", i);
gMyApp.WriteProfileInt(section, numBuf, fColumnWidth[i]);
}
gMyApp.WriteProfileInt(section, L"sort-column", fSortColumn);
gMyApp.WriteProfileInt(section, L"ascending", fAscending);
}
/*
* ==========================================================================
* Preferences
* ==========================================================================
*/
void Preferences::InitTempPath(void)
{
WCHAR buf[MAX_PATH];
DWORD len;
CString tempPath;
len = ::GetTempPath(NELEM(buf), buf);
if (len == 0) {
DWORD err = ::GetLastError();
LOGI("GetTempPath failed, err=%d", err);
tempPath = kDefaultTempPath;
} else if (len >= NELEM(buf)) {
/* sheesh! */
LOGI("GetTempPath wants a %d-unit buffer", len);
tempPath = kDefaultTempPath;
} else {
tempPath = buf;
}
PathName path(tempPath);
LOGD("Temp path is '%ls'", (LPCWSTR) tempPath);
path.SFNToLFN();
tempPath = path.GetPathName();
LOGD("Temp path (long form) is '%ls'", (LPCWSTR) tempPath);
SetPrefString(kPrTempPath, tempPath);
// ::GetFullPathName(fTempPath, sizeof(buf), buf, &foo);
// ::SetCurrentDirectory(buf);
// ::GetCurrentDirectory(sizeof(buf2), buf2);
}
void Preferences::InitFolders(void)
{
CString path;
if (GetMyDocuments(&path)) {
SetPrefString(kPrOpenArchiveFolder, path);
SetPrefString(kPrConvertArchiveFolder, path);
SetPrefString(kPrAddFileFolder, path);
SetPrefString(kPrExtractFileFolder, path);
SetPrefString(kPrOpenWAVFolder, path);
} else {
WCHAR buf[MAX_PATH];
::GetCurrentDirectory(NELEM(buf), buf);
SetPrefString(kPrOpenArchiveFolder, buf);
SetPrefString(kPrConvertArchiveFolder, buf);
SetPrefString(kPrAddFileFolder, buf);
SetPrefString(kPrExtractFileFolder, buf);
SetPrefString(kPrOpenWAVFolder, buf);
}
LOGD("Default folder is '%ls'", GetPrefString(kPrExtractFileFolder));
}
bool Preferences::GetMyDocuments(CString* pPath)
{
LPITEMIDLIST pidl = NULL;
LPMALLOC lpMalloc = NULL;
HRESULT hr;
bool result = false;
hr = ::SHGetMalloc(&lpMalloc);
if (FAILED(hr))
return NULL;
hr = SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &pidl);
if (FAILED(hr)) {
LOGW("WARNING: unable to get CSIDL_PERSONAL");
goto bail;
}
result = (Pidl::GetPath(pidl, pPath) != FALSE);
if (!result) {
LOGW("WARNING: unable to convert CSIDL_PERSONAL to path");
/* fall through with "result" */
}
bail:
lpMalloc->Free(pidl);
lpMalloc->Release();
return result;
}
int Preferences::DefaultCompressionType(void)
{
if (NufxArchive::IsCompressionSupported(kNuThreadFormatLZW2))
return kNuThreadFormatLZW2;
else
return kNuThreadFormatUncompressed;
}
bool Preferences::GetPrefBool(PrefNum num) const
{
if (!ValidateEntry(num, kBool))
return false;
//return (bool) (fValues[num]);
return (bool) ((long) (fValues[num]) != 0);
}
void Preferences::SetPrefBool(PrefNum num, bool val)
{
if (!ValidateEntry(num, kBool))
return;
fValues[num] = (void*) val;
}
long Preferences::GetPrefLong(PrefNum num) const
{
if (!ValidateEntry(num, kLong))
return -1;
return (long) fValues[num];
}
void Preferences::SetPrefLong(PrefNum num, long val)
{
if (!ValidateEntry(num, kLong))
return;
fValues[num] = (void*) val;
}
const WCHAR* Preferences::GetPrefString(PrefNum num) const
{
if (!ValidateEntry(num, kString))
return NULL;
return (const WCHAR*) fValues[num];
}
void Preferences::SetPrefString(PrefNum num, const WCHAR* str)
{
if (!ValidateEntry(num, kString))
return;
free(fValues[num]);
if (str == NULL) {
fValues[num] = NULL;
} else {
fValues[num] = wcsdup(str);
}
}
void Preferences::FreeStringValues(void)
{
for (int i = 0; i < kPrefNumLastEntry; i++) {
if (fPrefMaps[i].type == kString) {
delete[] fValues[i];
}
}
}
void Preferences::ScanPrefMaps(void)
{
int i, j;
/* scan PrefNum */
for (i = 0; i < kPrefNumLastEntry; i++) {
if (fPrefMaps[i].num != i) {
LOGE("HEY: PrefMaps[%d] has num=%d", i, fPrefMaps[i].num);
ASSERT(false);
break;
}
}
/* look for duplicate strings */
for (i = 0; i < kPrefNumLastEntry; i++) {
for (j = i+1; j < kPrefNumLastEntry; j++) {
if (fPrefMaps[i].registryKey == NULL ||
fPrefMaps[j].registryKey == NULL)
{
continue;
}
if (wcsicmp(fPrefMaps[i].registryKey,
fPrefMaps[j].registryKey) == 0 &&
wcsicmp(fPrefMaps[i].registrySection,
fPrefMaps[j].registrySection) == 0)
{
LOGE("HEY: PrefMaps[%d] and [%d] both have '%ls'/'%ls'",
i, j, fPrefMaps[i].registrySection,
fPrefMaps[i].registryKey);
ASSERT(false);
break;
}
}
}
}
int Preferences::LoadFromRegistry(void)
{
CString sval;
bool bval;
long lval;
LOGI("Loading preferences from registry");
fColumnLayout.LoadFromRegistry(kColumnSect);
int i;
for (i = 0; i < kPrefNumLastRegistry; i++) {
if (fPrefMaps[i].registryKey == NULL)
continue;
switch (fPrefMaps[i].type) {
case kBool:
bval = GetPrefBool(fPrefMaps[i].num);
SetPrefBool(fPrefMaps[i].num,
GetBool(fPrefMaps[i].registrySection, fPrefMaps[i].registryKey, bval));
break;
case kLong:
lval = GetPrefLong(fPrefMaps[i].num);
SetPrefLong(fPrefMaps[i].num,
GetInt(fPrefMaps[i].registrySection, fPrefMaps[i].registryKey, lval));
break;
case kString:
sval = GetPrefString(fPrefMaps[i].num);
SetPrefString(fPrefMaps[i].num,
GetString(fPrefMaps[i].registrySection, fPrefMaps[i].registryKey, sval));
break;
default:
LOGW("Invalid type %d on num=%d", fPrefMaps[i].type, i);
ASSERT(false);
break;
}
}
return 0;
}
int Preferences::SaveToRegistry(void)
{
LOGI("Saving preferences to registry");
fColumnLayout.SaveToRegistry(kColumnSect);
int i;
for (i = 0; i < kPrefNumLastRegistry; i++) {
if (fPrefMaps[i].registryKey == NULL)
continue;
switch (fPrefMaps[i].type) {
case kBool:
WriteBool(fPrefMaps[i].registrySection, fPrefMaps[i].registryKey,
GetPrefBool(fPrefMaps[i].num));
break;
case kLong:
WriteInt(fPrefMaps[i].registrySection, fPrefMaps[i].registryKey,
GetPrefLong(fPrefMaps[i].num));
break;
case kString:
WriteString(fPrefMaps[i].registrySection, fPrefMaps[i].registryKey,
GetPrefString(fPrefMaps[i].num));
break;
default:
LOGW("Invalid type %d on num=%d", fPrefMaps[i].type, i);
ASSERT(false);
break;
}
}
return 0;
}