ciderpress/app/Main.cpp
Andy McFadden 2adbe9591f Add optional MouseText-to-ASCII conversion
The AWP5 MouseText output is okay for some things but not others.
This adds an ASCII conversion, enabled through a preference.
2017-11-13 14:24:58 -08:00

2490 lines
87 KiB
C++

/*
* CiderPress
* Copyright (C) 2007, 2008 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Main window management.
*/
#include "stdafx.h"
#include "Main.h"
#include "MyApp.h"
#include "AboutDialog.h"
#include "NufxArchive.h"
#include "DiskArchive.h"
#include "BNYArchive.h"
#include "ACUArchive.h"
#include "AppleSingleArchive.h"
#include "ArchiveInfoDialog.h"
#include "PrefsDialog.h"
#include "EnterRegDialog.h"
#include "OpenVolumeDialog.h"
#include "Print.h"
#include "../util/UtilLib.h"
#include "resource.h"
/* use MFC's fancy version of new for debugging */
//#define new DEBUG_NEW
static const WCHAR kWebSiteURL[] = L"http://www.a2ciderpress.com/";
/* custom class name for main frame */
static const WCHAR kMainWindowClassName[] = L"faddenSoft.CiderPress.4";
/*
* Filters for the "open file" command. In some cases a file may be opened
* in more than one format, so it's necessary to keep track of what the
* file filter was set to when the file was opened.
*
* With Vista-style dialogs, the second part of the string (the filespec)
* will sometimes be included in the pop-up. Sometimes not. It's
* deterministic but I haven't been able to figure out what the pattern is --
* it's not simply length of a given filter or of the entire string, or based
* on the presence of certain characters. The filter works correctly, so it
* doesn't seem to be malformed. It's just ugly to have the open dialog
* popup show an enormous, redundant filter string.
*
* I tried substituting '\0' for '|' and placing the string directly into
* the dialog; no change.
*
* CFileDialog::ApplyOFNToShellDialog in {VisualStudio}\VC\atlmfc\src\mfc\dlgfile.cpp
* appears to be doing the parsing. Single-stepping through the code shows
* that it's working fine, so something later on is choosing to merge
* pszName and pszSpec when generating the pop-up menu.
*
* The good news is that if I exclude the list of extensions from the name
* section, the popup will (so far) always includes the spec. The bad news
* is that this means we won't display the list of extensions on WinXP, which
* uses the older style of dialog. We could switch from public constants to
* a function that generates the filter based on a bit mask and the current
* OS version, but that might be more trouble than it's worth.
*/
const WCHAR MainWindow::kOpenNuFX[] =
L"ShrinkIt Archives|*.shk;*.sdk;*.bxy;*.sea;*.bse|";
const WCHAR MainWindow::kOpenBinaryII[] =
L"Binary II Archives|*.bny;*.bqy;*.bxy|";
const WCHAR MainWindow::kOpenACU[] =
L"ACU Archives|*.acu|";
const WCHAR MainWindow::kOpenAppleSingle[] =
L"AppleSingle files|*.as|";
const WCHAR MainWindow::kOpenDiskImage[] =
L"Disk Images|"
L"*.shk;*.sdk;*.dsk;*.po;*.do;*.d13;*.2mg;*.img;*.nib;*.nb2;*.raw;*.hdv;*.dc;*.dc6;*.ddd;*.app;*.fdi;*.iso;*.gz;*.zip|";
const WCHAR MainWindow::kOpenAll[] =
L"All Files|*.*|";
const WCHAR MainWindow::kOpenEnd[] =
L"|";
/*
* Used when guessing archive type from extension when no "-mode" argument
* was specified.
*
* This does *not* apply to files double-clicked from the content list; see
* HandleDoubleClick() for that.
*/
static const struct {
WCHAR extension[4];
FilterIndex idx;
} gExtensionToIndex[] = {
{ L"shk", kFilterIndexDiskImage }, // DiskImage probably better than NuFX
{ L"bxy", kFilterIndexNuFX },
{ L"bse", kFilterIndexNuFX },
{ L"sea", kFilterIndexNuFX },
{ L"bny", kFilterIndexBinaryII },
{ L"bqy", kFilterIndexBinaryII },
{ L"acu", kFilterIndexACU },
{ L"as", kFilterIndexAppleSingle },
{ L"sdk", kFilterIndexDiskImage },
{ L"dsk", kFilterIndexDiskImage },
{ L"po", kFilterIndexDiskImage },
{ L"do", kFilterIndexDiskImage },
{ L"d13", kFilterIndexDiskImage },
{ L"2mg", kFilterIndexDiskImage },
{ L"img", kFilterIndexDiskImage },
{ L"nib", kFilterIndexDiskImage },
{ L"nb2", kFilterIndexDiskImage },
{ L"raw", kFilterIndexDiskImage },
{ L"hdv", kFilterIndexDiskImage },
{ L"dc", kFilterIndexDiskImage },
{ L"dc6", kFilterIndexDiskImage },
{ L"ddd", kFilterIndexDiskImage },
{ L"app", kFilterIndexDiskImage },
{ L"fdi", kFilterIndexDiskImage },
{ L"iso", kFilterIndexDiskImage },
{ L"gz", kFilterIndexDiskImage }, // assume disk image inside
{ L"zip", kFilterIndexDiskImage }, // assume disk image inside
};
const WCHAR MainWindow::kModeNuFX[] = L"nufx";
const WCHAR MainWindow::kModeBinaryII[] = L"bin2";
const WCHAR MainWindow::kModeACU[] = L"acu";
const WCHAR MainWindow::kModeAppleSingle[] = L"as";
const WCHAR MainWindow::kModeDiskImage[] = L"disk";
/*
* ===========================================================================
* MainWindow
* ===========================================================================
*/
static const UINT gFindReplaceID = RegisterWindowMessage(FINDMSGSTRING);
BEGIN_MESSAGE_MAP(MainWindow, CFrameWnd)
ON_WM_CREATE()
ON_MESSAGE(WMU_LATE_INIT, OnLateInit)
//ON_MESSAGE(WMU_CLOSE_MAIN_DIALOG, OnCloseMainDialog)
ON_WM_SIZE()
ON_WM_GETMINMAXINFO()
ON_WM_PAINT()
//ON_WM_MOUSEWHEEL()
ON_WM_SETFOCUS()
ON_WM_HELPINFO()
ON_WM_QUERYENDSESSION()
ON_WM_ENDSESSION()
ON_REGISTERED_MESSAGE(gFindReplaceID, OnFindDialogMessage)
ON_COMMAND( IDM_FILE_NEW_ARCHIVE, OnFileNewArchive)
ON_COMMAND( IDM_FILE_OPEN, OnFileOpen)
ON_COMMAND( IDM_FILE_OPEN_VOLUME, OnFileOpenVolume)
ON_UPDATE_COMMAND_UI(IDM_FILE_OPEN_VOLUME, OnUpdateFileOpenVolume)
ON_COMMAND( IDM_FILE_REOPEN, OnFileReopen)
ON_UPDATE_COMMAND_UI(IDM_FILE_REOPEN, OnUpdateFileReopen)
ON_COMMAND( IDM_FILE_SAVE, OnFileSave)
ON_UPDATE_COMMAND_UI(IDM_FILE_SAVE, OnUpdateFileSave)
ON_COMMAND( IDM_FILE_CLOSE, OnFileClose)
ON_UPDATE_COMMAND_UI(IDM_FILE_CLOSE, OnUpdateFileClose)
ON_COMMAND( IDM_FILE_ARCHIVEINFO, OnFileArchiveInfo)
ON_UPDATE_COMMAND_UI(IDM_FILE_ARCHIVEINFO, OnUpdateFileArchiveInfo)
ON_COMMAND( IDM_FILE_PRINT, OnFilePrint)
ON_UPDATE_COMMAND_UI(IDM_FILE_PRINT, OnUpdateFilePrint)
ON_COMMAND( IDM_FILE_EXIT, OnFileExit)
ON_COMMAND( IDM_EDIT_COPY, OnEditCopy)
ON_UPDATE_COMMAND_UI(IDM_EDIT_COPY, OnUpdateEditCopy)
ON_COMMAND( IDM_EDIT_PASTE, OnEditPaste)
ON_UPDATE_COMMAND_UI(IDM_EDIT_PASTE, OnUpdateEditPaste)
ON_COMMAND( IDM_EDIT_PASTE_SPECIAL, OnEditPasteSpecial)
ON_UPDATE_COMMAND_UI(IDM_EDIT_PASTE_SPECIAL, OnUpdateEditPasteSpecial)
ON_COMMAND( IDM_EDIT_FIND, OnEditFind)
ON_UPDATE_COMMAND_UI(IDM_EDIT_FIND, OnUpdateEditFind)
ON_COMMAND( IDM_EDIT_SELECT_ALL, OnEditSelectAll)
ON_UPDATE_COMMAND_UI(IDM_EDIT_SELECT_ALL, OnUpdateEditSelectAll)
ON_COMMAND( IDM_EDIT_INVERT_SELECTION, OnEditInvertSelection)
ON_UPDATE_COMMAND_UI(IDM_EDIT_INVERT_SELECTION, OnUpdateEditInvertSelection)
ON_COMMAND( IDM_EDIT_PREFERENCES, OnEditPreferences)
ON_COMMAND_RANGE( IDM_SORT_PATHNAME, IDM_SORT_ORIGINAL, OnEditSort)
ON_UPDATE_COMMAND_UI_RANGE(IDM_SORT_PATHNAME, IDM_SORT_ORIGINAL, OnUpdateEditSort)
ON_COMMAND( IDM_ACTIONS_VIEW, OnActionsView)
ON_UPDATE_COMMAND_UI(IDM_ACTIONS_VIEW, OnUpdateActionsView)
ON_COMMAND( IDM_ACTIONS_ADD_FILES, OnActionsAddFiles)
ON_UPDATE_COMMAND_UI(IDM_ACTIONS_ADD_FILES, OnUpdateActionsAddFiles)
ON_COMMAND( IDM_ACTIONS_ADD_DISKS, OnActionsAddDisks)
ON_UPDATE_COMMAND_UI(IDM_ACTIONS_ADD_DISKS, OnUpdateActionsAddDisks)
ON_COMMAND( IDM_ACTIONS_CREATE_SUBDIR, OnActionsCreateSubdir)
ON_UPDATE_COMMAND_UI(IDM_ACTIONS_CREATE_SUBDIR, OnUpdateActionsCreateSubdir)
ON_COMMAND( IDM_ACTIONS_EXTRACT, OnActionsExtract)
ON_UPDATE_COMMAND_UI(IDM_ACTIONS_EXTRACT, OnUpdateActionsExtract)
ON_COMMAND( IDM_ACTIONS_TEST, OnActionsTest)
ON_UPDATE_COMMAND_UI(IDM_ACTIONS_TEST, OnUpdateActionsTest)
ON_COMMAND( IDM_ACTIONS_DELETE, OnActionsDelete)
ON_UPDATE_COMMAND_UI(IDM_ACTIONS_DELETE, OnUpdateActionsDelete)
ON_COMMAND( IDM_ACTIONS_RENAME, OnActionsRename)
ON_UPDATE_COMMAND_UI(IDM_ACTIONS_RENAME, OnUpdateActionsRename)
ON_COMMAND( IDM_ACTIONS_RECOMPRESS, OnActionsRecompress)
ON_UPDATE_COMMAND_UI(IDM_ACTIONS_RECOMPRESS, OnUpdateActionsRecompress)
ON_COMMAND( IDM_ACTIONS_OPENASDISK, OnActionsOpenAsDisk)
ON_UPDATE_COMMAND_UI(IDM_ACTIONS_OPENASDISK, OnUpdateActionsOpenAsDisk)
ON_COMMAND( IDM_ACTIONS_EDIT_COMMENT, OnActionsEditComment)
ON_UPDATE_COMMAND_UI(IDM_ACTIONS_EDIT_COMMENT, OnUpdateActionsEditComment)
ON_COMMAND( IDM_ACTIONS_EDIT_PROPS, OnActionsEditProps)
ON_UPDATE_COMMAND_UI(IDM_ACTIONS_EDIT_PROPS, OnUpdateActionsEditProps)
ON_COMMAND( IDM_ACTIONS_RENAME_VOLUME, OnActionsRenameVolume)
ON_UPDATE_COMMAND_UI(IDM_ACTIONS_RENAME_VOLUME, OnUpdateActionsRenameVolume)
ON_COMMAND( IDM_ACTIONS_CONV_DISK, OnActionsConvDisk)
ON_UPDATE_COMMAND_UI(IDM_ACTIONS_CONV_DISK, OnUpdateActionsConvDisk)
ON_COMMAND( IDM_ACTIONS_CONV_FILE, OnActionsConvFile)
ON_UPDATE_COMMAND_UI(IDM_ACTIONS_CONV_FILE, OnUpdateActionsConvFile)
ON_COMMAND( IDM_ACTIONS_CONV_TOWAV, OnActionsConvToWav)
ON_UPDATE_COMMAND_UI(IDM_ACTIONS_CONV_TOWAV, OnUpdateActionsConvToWav)
ON_COMMAND( IDM_ACTIONS_CONV_FROMWAV, OnActionsConvFromWav)
ON_UPDATE_COMMAND_UI(IDM_ACTIONS_CONV_FROMWAV, OnUpdateActionsConvFromWav)
ON_COMMAND( IDM_ACTIONS_IMPORT_BAS, OnActionsImportBAS)
ON_UPDATE_COMMAND_UI(IDM_ACTIONS_IMPORT_BAS, OnUpdateActionsImportBAS)
ON_COMMAND( IDM_TOOLS_DISKEDIT, OnToolsDiskEdit)
ON_COMMAND( IDM_TOOLS_IMAGECREATOR, OnToolsDiskImageCreator)
ON_COMMAND( IDM_TOOLS_DISKCONV, OnToolsDiskConv)
ON_COMMAND( IDM_TOOLS_BULKDISKCONV, OnToolsBulkDiskConv)
ON_COMMAND( IDM_TOOLS_SST_MERGE, OnToolsSSTMerge)
ON_COMMAND( IDM_TOOLS_VOLUMECOPIER_VOLUME, OnToolsVolumeCopierVolume)
ON_COMMAND( IDM_TOOLS_VOLUMECOPIER_FILE, OnToolsVolumeCopierFile)
ON_COMMAND( IDM_TOOLS_EOLSCANNER, OnToolsEOLScanner)
ON_COMMAND( IDM_TOOLS_TWOIMGPROPS, OnToolsTwoImgProps)
ON_COMMAND( IDM_HELP_CONTENTS, OnHelpContents)
ON_COMMAND( IDM_HELP_WEBSITE, OnHelpWebSite)
ON_COMMAND( IDM_HELP_ORDERING, OnHelpOrdering)
ON_COMMAND( IDM_HELP_ABOUT, OnHelpAbout)
// ON_COMMAND( IDM_RTCLK_DEFAULT, OnRtClkDefault)
ON_COMMAND(ID_HELP_FINDER, CFrameWnd::OnHelpFinder)
ON_COMMAND(ID_HELP, CFrameWnd::OnHelp)
ON_COMMAND(ID_CONTEXT_HELP, CFrameWnd::OnContextHelp)
ON_COMMAND(ID_DEFAULT_HELP, CFrameWnd::OnHelpFinder)
END_MESSAGE_MAP()
/*
* MainWindow constructor. Creates the main window and sets
* its properties.
*/
MainWindow::MainWindow()
{
static const WCHAR kAppName[] = L"CiderPress";
fpContentList = NULL;
fpOpenArchive = NULL;
//fpSelSet = NULL;
fpActionProgress = NULL;
fpProgressCounter = NULL;
fpFindDialog = NULL;
fFindDown = true;
fFindMatchCase = false;
fFindMatchWholeWord = false;
fAbortPrinting = false;
fhDevMode = NULL;
fhDevNames = NULL;
fNeedReopen = false;
CString wndClass = AfxRegisterWndClass(
CS_DBLCLKS /*| CS_HREDRAW | CS_VREDRAW*/,
gMyApp.LoadStandardCursor(IDC_ARROW),
NULL /*(HBRUSH) (COLOR_WINDOW + 1)*/,
gMyApp.LoadIcon(IDR_MAINFRAME) );
Create(wndClass, kAppName, WS_OVERLAPPEDWINDOW /*| WS_CLIPCHILDREN*/,
rectDefault, NULL, MAKEINTRESOURCE(IDR_MAINFRAME));
LoadAccelTable(MAKEINTRESOURCE(IDR_MAINFRAME));
// initialize some OLE garbage
AfxOleInit();
// required by MFC if Rich Edit controls are used
AfxInitRichEdit();
// required??
//AfxEnableControlContainer();
SetCPTitle();
int cc = PostMessage(WMU_LATE_INIT, 0, 0);
ASSERT(cc != 0);
}
/*
* MainWindow destructor. Close the archive if one is open, but don't try
* to shut down any controls in child windows. By this point, Windows has
* already snuffed them.
*/
MainWindow::~MainWindow()
{
LOGI("~MainWindow");
//LOGI("MainWindow destructor");
CloseArchiveWOControls();
//int cc;
//cc = ::WinHelp(m_hWnd, ::AfxGetApp()->m_pszHelpFilePath, HELP_QUIT, 0);
//LOGI("Turning off WinHelp returned %d", cc);
::HtmlHelp(NULL, NULL, HH_CLOSE_ALL, 0);
// free stuff used by print dialog
::GlobalFree(fhDevMode);
::GlobalFree(fhDevNames);
fPreferences.SaveToRegistry();
LOGI("MainWindow destructor complete");
}
BOOL MainWindow::PreCreateWindow(CREATESTRUCT& cs)
{
BOOL res = CFrameWnd::PreCreateWindow(cs);
cs.dwExStyle &= ~(WS_EX_CLIENTEDGE);
// This changes the window class name to a value that the installer can
// detect. This allows us to prevent installation while CiderPress is
// running. (If we don't do that, the installation will offer to reboot
// the computer to complete installation.)
WNDCLASS wndCls;
if (!GetClassInfo(AfxGetInstanceHandle(), cs.lpszClass, &wndCls)) {
LOGW("GetClassInfo failed");
} else {
cs.lpszClass = kMainWindowClassName;
wndCls.lpszClassName = kMainWindowClassName;
if (!AfxRegisterClass(&wndCls)) {
LOGW("AfxRegisterClass failed");
}
}
return res;
}
void MainWindow::GetClientRect(LPRECT lpRect) const
{
CRect sizeRect;
int toolBarHeight, statusBarHeight;
fToolBar.GetWindowRect(&sizeRect);
toolBarHeight = sizeRect.bottom - sizeRect.top;
fStatusBar.GetWindowRect(&sizeRect);
statusBarHeight = sizeRect.bottom - sizeRect.top;
//LOGI("HEIGHTS = %d/%d", toolBarHeight, statusBarHeight);
CFrameWnd::GetClientRect(lpRect);
lpRect->top += toolBarHeight;
lpRect->bottom -= statusBarHeight;
}
void MainWindow::DoIdle(void)
{
/*
* Make sure that the filename field in the content list is always
* visible, since that what the user clicks on to select things. Would
* be nice to have a way to prevent it, but for now we'll just shove
* things back where they're supposed to be.
*/
if (fpContentList != NULL) {
/* get the current column 0 width, with current user adjustments */
fpContentList->ExportColumnWidths();
int width = fPreferences.GetColumnLayout()->GetColumnWidth(0);
if (width >= 0 && width < ColumnLayout::kMinCol0Width) {
/* column is too small, but don't change it until user lets mouse up */
if (::GetAsyncKeyState(VK_LBUTTON) >= 0) {
LOGI("Resetting column 0 width");
fPreferences.GetColumnLayout()->SetColumnWidth(0,
ColumnLayout::kMinCol0Width);
fpContentList->NewColumnWidths();
}
}
}
/*
* Put an asterisk at the end of the title if we have an open archive
* and it has pending modifications. Remove it if nothing is pending.
*/
if (fpOpenArchive != NULL) {
CString title;
int len;
GetWindowText(/*ref*/ title);
len = title.GetLength();
if (len > 0 && title.GetAt(len-1) == '*') {
if (!fpOpenArchive->IsModified()) {
/* remove the asterisk and the preceeding space */
title.Delete(len-2, 2);
SetWindowText(title);
}
} else {
if (fpOpenArchive->IsModified()) {
/* add an asterisk */
title += " *";
SetWindowText(title);
}
}
}
}
void MainWindow::ProcessCommandLine(void)
{
/*
* Get the command line and break it down into an argument vector.
*
* Usage:
* CiderPress [[-temparc] [-mode {nufx,bin2,disk}] [-dispname name] filename]
*/
const WCHAR* cmdLine = ::GetCommandLine();
if (cmdLine == NULL || wcslen(cmdLine) == 0)
return;
WCHAR* mangle = wcsdup(cmdLine);
if (mangle == NULL)
return;
LOGI("Mangling '%ls'", mangle);
WCHAR* argv[8];
int argc = 8;
VectorizeString(mangle, argv, &argc);
LOGI("Args:");
for (int i = 0; i < argc; i++) {
LOGI(" %d '%ls'", i, argv[i]);
}
/*
* Figure out what the arguments are.
*/
const WCHAR* filename = NULL;
const WCHAR* dispName = NULL;
FilterIndex filterIndex = kFilterIndexGeneric;
bool temp = false;
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
if (wcsicmp(argv[i], L"-mode") == 0) {
if (i == argc-1) {
LOGI("WARNING: -mode specified without mode");
} else
i++;
if (wcsicmp(argv[i], kModeNuFX) == 0)
filterIndex = kFilterIndexNuFX;
else if (wcsicmp(argv[i], kModeBinaryII) == 0)
filterIndex = kFilterIndexBinaryII;
else if (wcsicmp(argv[i], kModeACU) == 0)
filterIndex = kFilterIndexACU;
else if (wcsicmp(argv[i], kModeAppleSingle) == 0)
filterIndex = kFilterIndexAppleSingle;
else if (wcsicmp(argv[i], kModeDiskImage) == 0)
filterIndex = kFilterIndexDiskImage;
else {
LOGI("WARNING: unrecognized mode '%ls'", argv[i]);
}
} else if (wcsicmp(argv[i], L"-dispname") == 0) {
if (i == argc-1) {
LOGI("WARNING: -dispname specified without name");
} else
i++;
dispName = argv[i];
} else if (wcsicmp(argv[i], L"-temparc") == 0) {
temp = true;
} else if (wcsicmp(argv[i], L"-install") == 0) {
// see MyApp::InitInstance
LOGI("Got '-install' flag, doing nothing");
} else if (wcsicmp(argv[i], L"-uninstall") == 0) {
// see MyApp::InitInstance
LOGI("Got '-uninstall' flag, doing nothing");
} else {
LOGI("WARNING: unrecognized flag '%ls'", argv[i]);
}
} else {
/* must be the filename */
if (i != argc-1) {
LOGI("WARNING: ignoring extra arguments (e.g. '%ls')",
argv[i+1]);
}
filename = argv[i];
break;
}
}
if (argc != 1 && filename == NULL) {
LOGI("WARNING: args specified but no filename found");
}
LOGI("Argument handling:");
LOGI(" index=%d temp=%d filename='%ls'",
filterIndex, temp, filename == NULL ? L"(null)" : filename);
if (filename != NULL) {
PathName path(filename);
CString ext = path.GetExtension();
// drop the leading '.' from the extension
if (ext.Left(1) == ".")
ext.Delete(0, 1);
/* load the archive, mandating read-only if it's a temporary file */
if (LoadArchive(filename, ext, filterIndex, temp) == 0) {
/* success, update title bar */
if (temp)
fOpenArchivePathName = path.GetFileName();
else
fOpenArchivePathName = filename;
if (dispName != NULL)
fOpenArchivePathName = dispName;
SetCPTitle(fOpenArchivePathName, fpOpenArchive);
}
/* if it's a temporary file, arrange to have it deleted before exit */
if (temp) {
int len = wcslen(filename);
if (len > 4 && wcsicmp(filename + (len-4), L".tmp") == 0) {
fDeleteList.Add(filename);
} else {
LOGI("NOT adding '%ls' to DeleteList -- does not end in '.tmp'",
filename);
}
}
}
free(mangle);
}
/*
* ===================================
* Command handlers
* ===================================
*/
const int kProgressPane = 1;
int MainWindow::OnCreate(LPCREATESTRUCT lpcs)
{
// add a toolbar and status bar
LOGI("Now in OnCreate!");
if (CFrameWnd::OnCreate(lpcs) == -1)
return -1;
/*
* Create the tool bar.
*/
#if 0
static UINT buttonList[] = {
IDM_FILE_OPEN,
IDM_FILE_NEW_ARCHIVE,
// spacer
IDM_FILE_PRINT,
};
#endif
fToolBar.Create(this, WS_CHILD | WS_VISIBLE | CBRS_TOP |
CBRS_TOOLTIPS | CBRS_FLYBY);
fToolBar.LoadToolBar(IDR_TOOLBAR1);
/*
* Create the status bar.
*/
static UINT indicators[] = { ID_SEPARATOR, ID_INDICATOR_COMPLETE };
fStatusBar.Create(this);
fStatusBar.SetIndicators(indicators, NELEM(indicators));
//fStatusBar.SetPaneInfo(0, ID_SEPARATOR, SBPS_NOBORDERS | SBPS_STRETCH, 0);
fStatusBar.SetPaneText(kProgressPane, L"");
return 0;
}
LONG MainWindow::OnLateInit(UINT, LONG)
{
/*
* Catch a message sent to inspire us to perform one-time initializations of
* preferences and libraries.
*
* We're doing this the long way around because we want to be able to
* put up a dialog box if the version is bad. If we tried to handle this
* in the constructor we'd be acting before the window was fully created.
*/
CString result;
CString appName;
CString niftyListFile;
CheckedLoadString(&appName, IDS_MB_APP_NAME);
LOGI("----- late init begins -----");
/*
* Handle all other messages. This gives the framework a chance to dim
* all of the toolbar buttons. This is especially useful when opening
* a file from the command line that doesn't exist, causing an error
* dialog and blocking main window messages.
*/
PeekAndPump();
/*
* Initialize libraries. This includes a version check.
*/
result = NufxArchive::AppInit();
if (!result.IsEmpty())
goto fail;
result = DiskArchive::AppInit();
if (!result.IsEmpty())
goto fail;
result = BnyArchive::AppInit();
if (!result.IsEmpty())
goto fail;
niftyListFile = gMyApp.GetExeBaseName();
niftyListFile += "NList.Data";
if (!NiftyList::AppInit(niftyListFile)) {
CString file2 = niftyListFile + ".TXT";
if (!NiftyList::AppInit(file2)) {
CString msg;
msg.Format(IDS_NLIST_DATA_FAILED, niftyListFile, file2);
MessageBox(msg, appName, MB_OK);
}
}
/*
* Read preferences from registry.
*/
fPreferences.LoadFromRegistry();
#if 0
/*
* Check to see if we're registered; if we're not, and we've expired, it's
* time to bail out.
*/
MyRegistry::RegStatus regStatus;
//regStatus = gMyApp.fRegistry.CheckRegistration(&result);
regStatus = MyRegistry::kRegValid;
LOGI("CheckRegistration returned %d", regStatus);
switch (regStatus) {
case MyRegistry::kRegNotSet:
case MyRegistry::kRegValid:
ASSERT(result.IsEmpty());
break;
case MyRegistry::kRegExpired:
case MyRegistry::kRegInvalid:
MessageBox(result, appName, MB_OK|MB_ICONINFORMATION);
LOGI("FORCING REG");
if (EnterRegDialog::GetRegInfo(this) != 0) {
result = "";
goto fail;
}
SetCPTitle(); // update title bar with new reg info
break;
case MyRegistry::kRegFailed:
ASSERT(!result.IsEmpty());
goto fail;
default:
ASSERT(false);
CString confused;
confused.Format(L"Registration check failed. %ls", (LPCWSTR) result);
result = confused;
goto fail;
}
#endif
/*
* Process command-line options, possibly loading an archive.
*/
ProcessCommandLine();
return 0;
fail:
if (!result.IsEmpty())
ShowFailureMsg(this, result, IDS_FAILED);
int cc = PostMessage(WM_CLOSE, 0, 0);
ASSERT(cc != 0);
return 0;
}
BOOL MainWindow::OnQueryEndSession(void)
{
// The system wants to know if we're okay with shutting down.
LOGI("Got QueryEndSession");
return TRUE;
}
void MainWindow::OnEndSession(BOOL bEnding)
{
LOGI("Got EndSession (bEnding=%d)", bEnding);
if (bEnding) {
CloseArchiveWOControls();
fPreferences.SaveToRegistry();
}
}
void MainWindow::OnSize(UINT nType, int cx, int cy)
{
/*
* The main window is resizing. We don't automatically redraw on resize,
* so we will need to update the client region. If it's filled with a
* control, the control's resize & redraw function will take care of it.
* If not, we need to explicitly invalidate the client region so the
* window will repaint itself.
*/
CFrameWnd::OnSize(nType, cx, cy);
ResizeClientArea();
}
void MainWindow::ResizeClientArea(void)
{
CRect sizeRect;
GetClientRect(&sizeRect);
if (fpContentList != NULL)
fpContentList->MoveWindow(sizeRect);
else
Invalidate(false);
}
void MainWindow::OnGetMinMaxInfo(MINMAXINFO* pMMI)
{
// Restrict the minimum window size to something reasonable.
pMMI->ptMinTrackSize.x = 256;
pMMI->ptMinTrackSize.y = 192;
}
void MainWindow::OnPaint(void)
{
// Repaint the main window.
CPaintDC dc(this);
CRect clientRect;
GetClientRect(&clientRect);
/*
* If there's no control in the window, fill in the client area with
* what looks like an empty MDI client rect.
*/
if (fpContentList == NULL) {
DrawEmptyClientArea(&dc, clientRect);
}
#if 0
CPen pen(PS_SOLID, 1, RGB(255, 0, 0)); // red pen, 1 pixel wide
CPen* pOldPen = dc.SelectObject(&pen);
dc.MoveTo(clientRect.left, clientRect.top);
dc.LineTo(clientRect.right-1, clientRect.top);
dc.LineTo(clientRect.right, clientRect.bottom);
dc.LineTo(clientRect.left, clientRect.bottom-1);
dc.LineTo(clientRect.left, clientRect.top);
dc.SelectObject(pOldPen);
#endif
}
#if 0
afx_msg BOOL MainWindow::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
LOGI("MOUSE WHEEL");
return FALSE;
WPARAM wparam;
LPARAM lparam;
wparam = nFlags | (zDelta << 16);
lparam = pt.x | (pt.y << 16);
if (fpContentList != NULL)
fpContentList->SendMessage(WM_MOUSEWHEEL, wparam, lparam);
return CWnd::OnMouseWheel(nFlags, zDelta, pt);
// return TRUE;
}
#endif
void MainWindow::OnSetFocus(CWnd* /*pOldWnd*/)
{
// Make sure open controls keep the input focus.
if (fpContentList != NULL) {
LOGD("Returning focus to ContentList");
fpContentList->SetFocus();
}
}
BOOL MainWindow::OnHelpInfo(HELPINFO* /*lpHelpInfo*/)
{
LOGD("MainWindow::OnHelpInfo");
MyApp::HandleHelp(this, HELP_TOPIC_WELCOME);
return TRUE;
}
void MainWindow::OnEditPreferences(void)
{
// Handle Edit->Preferences by popping up a property sheet.
PrefsSheet ps;
ColumnLayout* pColLayout = fPreferences.GetColumnLayout();
/* pull any user header tweaks out of list so we can configure prefs */
if (fpContentList != NULL)
fpContentList->ExportColumnWidths();
/* set up PrefsGeneralPage */
for (int i = 0; i < kNumVisibleColumns; i++) {
ps.fGeneralPage.fColumn[i] = (pColLayout->GetColumnWidth(i) != 0);
}
ps.fGeneralPage.fMimicShrinkIt = fPreferences.GetPrefBool(kPrMimicShrinkIt);
ps.fGeneralPage.fBadMacSHK = fPreferences.GetPrefBool(kPrBadMacSHK);
ps.fGeneralPage.fReduceSHKErrorChecks = fPreferences.GetPrefBool(kPrReduceSHKErrorChecks);
ps.fGeneralPage.fCoerceDOSFilenames = fPreferences.GetPrefBool(kPrCoerceDOSFilenames);
ps.fGeneralPage.fSpacesToUnder = fPreferences.GetPrefBool(kPrSpacesToUnder);
ps.fGeneralPage.fPasteJunkPaths = fPreferences.GetPrefBool(kPrPasteJunkPaths);
ps.fGeneralPage.fBeepOnSuccess = fPreferences.GetPrefBool(kPrBeepOnSuccess);
/* set up PrefsDiskImagePage */
ps.fDiskImagePage.fQueryImageFormat = fPreferences.GetPrefBool(kPrQueryImageFormat);
ps.fDiskImagePage.fOpenVolumeRO = fPreferences.GetPrefBool(kPrOpenVolumeRO);
ps.fDiskImagePage.fOpenVolumePhys0 = fPreferences.GetPrefBool(kPrOpenVolumePhys0);
ps.fDiskImagePage.fProDOSAllowLower = fPreferences.GetPrefBool(kPrProDOSAllowLower);
ps.fDiskImagePage.fProDOSUseSparse = fPreferences.GetPrefBool(kPrProDOSUseSparse);
/* set up PrefsCompressionPage */
ps.fCompressionPage.fCompressType = fPreferences.GetPrefLong(kPrCompressionType);
/* set up PrefsFviewPage */
ps.fFviewPage.fMaxViewFileSizeKB =
(fPreferences.GetPrefLong(kPrMaxViewFileSize) + 1023) / 1024;
ps.fFviewPage.fNoWrapText = fPreferences.GetPrefBool(kPrNoWrapText);
ps.fFviewPage.fHighlightHexDump = fPreferences.GetPrefBool(kPrHighlightHexDump);
ps.fFviewPage.fHighlightBASIC = fPreferences.GetPrefBool(kPrHighlightBASIC);
ps.fFviewPage.fConvDisasmOneByteBrkCop = fPreferences.GetPrefBool(kPrDisasmOneByteBrkCop);
ps.fFviewPage.fConvMouseTextToASCII = fPreferences.GetPrefBool(kPrConvMouseTextToASCII);
ps.fFviewPage.fConvHiResBlackWhite = fPreferences.GetPrefBool(kPrConvHiResBlackWhite);
ps.fFviewPage.fConvDHRAlgorithm = fPreferences.GetPrefLong(kPrConvDHRAlgorithm);
ps.fFviewPage.fRelaxGfxTypeCheck = fPreferences.GetPrefBool(kPrRelaxGfxTypeCheck);
// ps.fFviewPage.fEOLConvRaw = fPreferences.GetPrefBool(kPrEOLConvRaw);
// ps.fFviewPage.fConvHighASCII = fPreferences.GetPrefBool(kPrConvHighASCII);
ps.fFviewPage.fConvTextEOL_HA = fPreferences.GetPrefBool(kPrConvTextEOL_HA);
ps.fFviewPage.fConvCPMText = fPreferences.GetPrefBool(kPrConvCPMText);
ps.fFviewPage.fConvPascalText = fPreferences.GetPrefBool(kPrConvPascalText);
ps.fFviewPage.fConvPascalCode = fPreferences.GetPrefBool(kPrConvPascalCode);
ps.fFviewPage.fConvApplesoft = fPreferences.GetPrefBool(kPrConvApplesoft);
ps.fFviewPage.fConvInteger = fPreferences.GetPrefBool(kPrConvInteger);
ps.fFviewPage.fConvBusiness = fPreferences.GetPrefBool(kPrConvBusiness);
ps.fFviewPage.fConvGWP = fPreferences.GetPrefBool(kPrConvGWP);
ps.fFviewPage.fConvText8 = fPreferences.GetPrefBool(kPrConvText8);
ps.fFviewPage.fConvAWP = fPreferences.GetPrefBool(kPrConvAWP);
ps.fFviewPage.fConvADB = fPreferences.GetPrefBool(kPrConvADB);
ps.fFviewPage.fConvASP = fPreferences.GetPrefBool(kPrConvASP);
ps.fFviewPage.fConvSCAssem = fPreferences.GetPrefBool(kPrConvSCAssem);
ps.fFviewPage.fConvDisasm = fPreferences.GetPrefBool(kPrConvDisasm);
ps.fFviewPage.fConvHiRes = fPreferences.GetPrefBool(kPrConvHiRes);
ps.fFviewPage.fConvDHR = fPreferences.GetPrefBool(kPrConvDHR);
ps.fFviewPage.fConvSHR = fPreferences.GetPrefBool(kPrConvSHR);
ps.fFviewPage.fConvPrintShop = fPreferences.GetPrefBool(kPrConvPrintShop);
ps.fFviewPage.fConvMacPaint = fPreferences.GetPrefBool(kPrConvMacPaint);
ps.fFviewPage.fConvProDOSFolder = fPreferences.GetPrefBool(kPrConvProDOSFolder);
ps.fFviewPage.fConvResources = fPreferences.GetPrefBool(kPrConvResources);
/* set up PrefsFilesPage */
ps.fFilesPage.fTempPath = fPreferences.GetPrefString(kPrTempPath);
ps.fFilesPage.fExtViewerExts = fPreferences.GetPrefString(kPrExtViewerExts);
if (ps.DoModal() == IDOK)
ApplyNow(&ps);
}
void MainWindow::ApplyNow(PrefsSheet* pPS)
{
// Apply a change from the preferences sheet.
LOGV("APPLY CHANGES");
bool mustReload = false;
ColumnLayout* pColLayout = fPreferences.GetColumnLayout();
if (pPS->fGeneralPage.fDefaultsPushed) {
/* reset all sizes to defaults, then factor in checkboxes */
LOGI(" Resetting all widths to defaults");
/* copy defaults over */
for (int i = 0; i < kNumVisibleColumns; i++)
pColLayout->SetColumnWidth(i, ColumnLayout::kWidthDefaulted);
}
/* handle column checkboxes */
for (int i = 0; i < kNumVisibleColumns; i++) {
if (pColLayout->GetColumnWidth(i) == 0 &&
pPS->fGeneralPage.fColumn[i])
{
/* restore column */
LOGI(" Column %d restored", i);
pColLayout->SetColumnWidth(i, ColumnLayout::kWidthDefaulted);
} else if (pColLayout->GetColumnWidth(i) != 0 &&
!pPS->fGeneralPage.fColumn[i])
{
/* disable column */
LOGI(" Column %d hidden", i);
pColLayout->SetColumnWidth(i, 0);
}
}
if (fpContentList != NULL)
fpContentList->NewColumnWidths();
fPreferences.SetPrefBool(kPrMimicShrinkIt,
pPS->fGeneralPage.fMimicShrinkIt != 0);
fPreferences.SetPrefBool(kPrBadMacSHK, pPS->fGeneralPage.fBadMacSHK != 0);
fPreferences.SetPrefBool(kPrReduceSHKErrorChecks,
pPS->fGeneralPage.fReduceSHKErrorChecks != 0);
if (fPreferences.GetPrefBool(kPrCoerceDOSFilenames)!=
(pPS->fGeneralPage.fCoerceDOSFilenames != 0))
{
LOGI("DOS filename coercion pref now %d",
pPS->fGeneralPage.fCoerceDOSFilenames);
fPreferences.SetPrefBool(kPrCoerceDOSFilenames,
pPS->fGeneralPage.fCoerceDOSFilenames != 0);
mustReload = true;
}
if (fPreferences.GetPrefBool(kPrSpacesToUnder) !=
(pPS->fGeneralPage.fSpacesToUnder != 0))
{
LOGI("Spaces-to-underscores now %d", pPS->fGeneralPage.fSpacesToUnder);
fPreferences.SetPrefBool(kPrSpacesToUnder, pPS->fGeneralPage.fSpacesToUnder != 0);
mustReload = true;
}
fPreferences.SetPrefBool(kPrPasteJunkPaths, pPS->fGeneralPage.fPasteJunkPaths != 0);
fPreferences.SetPrefBool(kPrBeepOnSuccess, pPS->fGeneralPage.fBeepOnSuccess != 0);
if (pPS->fGeneralPage.fOurAssociations != NULL) {
LOGI("NEW ASSOCIATIONS!");
#ifdef CAN_UPDATE_FILE_ASSOC
for (int assoc = 0; assoc < gMyApp.fRegistry.GetNumFileAssocs(); assoc++)
{
gMyApp.fRegistry.SetFileAssoc(assoc,
pPS->fGeneralPage.fOurAssociations[assoc]);
}
#endif
/* delete them so, if they hit "apply" again, we only update once */
delete[] pPS->fGeneralPage.fOurAssociations;
pPS->fGeneralPage.fOurAssociations = NULL;
LOGV("Sending association-change notification to Windows shell");
::SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
}
fPreferences.SetPrefBool(kPrQueryImageFormat, pPS->fDiskImagePage.fQueryImageFormat != 0);
fPreferences.SetPrefBool(kPrOpenVolumeRO, pPS->fDiskImagePage.fOpenVolumeRO != 0);
fPreferences.SetPrefBool(kPrOpenVolumePhys0, pPS->fDiskImagePage.fOpenVolumePhys0 != 0);
fPreferences.SetPrefBool(kPrProDOSAllowLower, pPS->fDiskImagePage.fProDOSAllowLower != 0);
fPreferences.SetPrefBool(kPrProDOSUseSparse, pPS->fDiskImagePage.fProDOSUseSparse != 0);
fPreferences.SetPrefLong(kPrCompressionType, pPS->fCompressionPage.fCompressType);
fPreferences.SetPrefLong(kPrMaxViewFileSize, pPS->fFviewPage.fMaxViewFileSizeKB * 1024);
fPreferences.SetPrefBool(kPrNoWrapText, pPS->fFviewPage.fNoWrapText != 0);
fPreferences.SetPrefBool(kPrHighlightHexDump, pPS->fFviewPage.fHighlightHexDump != 0);
fPreferences.SetPrefBool(kPrHighlightBASIC, pPS->fFviewPage.fHighlightBASIC != 0);
fPreferences.SetPrefBool(kPrDisasmOneByteBrkCop, pPS->fFviewPage.fConvDisasmOneByteBrkCop != 0);
fPreferences.SetPrefBool(kPrConvMouseTextToASCII, pPS->fFviewPage.fConvMouseTextToASCII != 0);
fPreferences.SetPrefBool(kPrConvHiResBlackWhite, pPS->fFviewPage.fConvHiResBlackWhite != 0);
fPreferences.SetPrefLong(kPrConvDHRAlgorithm, pPS->fFviewPage.fConvDHRAlgorithm);
fPreferences.SetPrefBool(kPrRelaxGfxTypeCheck, pPS->fFviewPage.fRelaxGfxTypeCheck != 0);
// fPreferences.SetPrefBool(kPrEOLConvRaw, pPS->fFviewPage.fEOLConvRaw != 0);
// fPreferences.SetPrefBool(kPrConvHighASCII, pPS->fFviewPage.fConvHighASCII != 0);
fPreferences.SetPrefBool(kPrConvTextEOL_HA, pPS->fFviewPage.fConvTextEOL_HA != 0);
fPreferences.SetPrefBool(kPrConvCPMText, pPS->fFviewPage.fConvCPMText != 0);
fPreferences.SetPrefBool(kPrConvPascalText, pPS->fFviewPage.fConvPascalText != 0);
fPreferences.SetPrefBool(kPrConvPascalCode, pPS->fFviewPage.fConvPascalCode != 0);
fPreferences.SetPrefBool(kPrConvApplesoft, pPS->fFviewPage.fConvApplesoft != 0);
fPreferences.SetPrefBool(kPrConvInteger, pPS->fFviewPage.fConvInteger != 0);
fPreferences.SetPrefBool(kPrConvBusiness, pPS->fFviewPage.fConvBusiness != 0);
fPreferences.SetPrefBool(kPrConvGWP, pPS->fFviewPage.fConvGWP != 0);
fPreferences.SetPrefBool(kPrConvText8, pPS->fFviewPage.fConvText8 != 0);
fPreferences.SetPrefBool(kPrConvAWP, pPS->fFviewPage.fConvAWP != 0);
fPreferences.SetPrefBool(kPrConvADB, pPS->fFviewPage.fConvADB != 0);
fPreferences.SetPrefBool(kPrConvASP, pPS->fFviewPage.fConvASP != 0);
fPreferences.SetPrefBool(kPrConvSCAssem, pPS->fFviewPage.fConvSCAssem != 0);
fPreferences.SetPrefBool(kPrConvDisasm, pPS->fFviewPage.fConvDisasm != 0);
fPreferences.SetPrefBool(kPrConvHiRes, pPS->fFviewPage.fConvHiRes != 0);
fPreferences.SetPrefBool(kPrConvDHR, pPS->fFviewPage.fConvDHR != 0);
fPreferences.SetPrefBool(kPrConvSHR, pPS->fFviewPage.fConvSHR != 0);
fPreferences.SetPrefBool(kPrConvPrintShop, pPS->fFviewPage.fConvPrintShop != 0);
fPreferences.SetPrefBool(kPrConvMacPaint, pPS->fFviewPage.fConvMacPaint != 0);
fPreferences.SetPrefBool(kPrConvProDOSFolder, pPS->fFviewPage.fConvProDOSFolder != 0);
fPreferences.SetPrefBool(kPrConvResources, pPS->fFviewPage.fConvResources != 0);
fPreferences.SetPrefString(kPrTempPath, pPS->fFilesPage.fTempPath);
LOGI("--- Temp path now '%ls'", fPreferences.GetPrefString(kPrTempPath));
fPreferences.SetPrefString(kPrExtViewerExts, pPS->fFilesPage.fExtViewerExts);
// if ((pPS->fGeneralPage.fShowToolbarText != 0) != fPreferences.GetShowToolbarText()) {
// fPreferences.SetShowToolbarText(pPS->fGeneralPage.fShowToolbarText != 0);
// //SetToolbarTextMode();
// ResizeClientArea();
// }
/* allow open archive to track changes to preferences */
if (fpOpenArchive != NULL)
fpOpenArchive->PreferencesChanged();
if (mustReload) {
LOGI("Preferences apply requesting GA/CL reload");
if (fpOpenArchive != NULL)
fpOpenArchive->Reload();
if (fpContentList != NULL)
fpContentList->Reload();
}
/* export to registry */
fPreferences.SaveToRegistry();
//Invalidate();
}
void MainWindow::OnEditFind(void)
{
// Handle IDM_EDIT_FIND.
DWORD flags = 0;
if (fpFindDialog != NULL)
return;
if (fFindDown)
flags |= FR_DOWN;
if (fFindMatchCase)
flags |= FR_MATCHCASE;
if (fFindMatchWholeWord)
flags |= FR_WHOLEWORD;
fpFindDialog = new CFindReplaceDialog;
fpFindDialog->Create(TRUE, // "find" only
fFindLastStr, // default string to search for
NULL, // default string to replace
flags, // flags
this); // parent
}
void MainWindow::OnUpdateEditFind(CCmdUI* pCmdUI)
{
pCmdUI->Enable(fpOpenArchive != NULL);
}
LRESULT MainWindow::OnFindDialogMessage(WPARAM wParam, LPARAM lParam)
{
// Handle activity in the modeless "find" dialog.
assert(fpFindDialog != NULL);
fFindDown = (fpFindDialog->SearchDown() != 0);
fFindMatchCase = (fpFindDialog->MatchCase() != 0);
fFindMatchWholeWord = (fpFindDialog->MatchWholeWord() != 0);
if (fpFindDialog->IsTerminating()) {
fpFindDialog = NULL;
return 0;
}
if (fpFindDialog->FindNext()) {
fFindLastStr = fpFindDialog->GetFindString();
fpContentList->FindNext(fFindLastStr, fFindDown, fFindMatchCase,
fFindMatchWholeWord);
} else {
LOGI("Unexpected find dialog activity");
}
return 0;
}
void MainWindow::OnEditSort(UINT id)
{
// Handle IDM_SORT_*.
// The "sort" menu item should really only be active if we have a file open.
LOGD("EDIT SORT %d", id);
ASSERT(id >= IDM_SORT_PATHNAME && id <= IDM_SORT_ORIGINAL);
fPreferences.GetColumnLayout()->SetSortColumn(id - IDM_SORT_PATHNAME);
fPreferences.GetColumnLayout()->SetAscending(true);
if (fpContentList != NULL)
fpContentList->NewSortOrder();
}
void MainWindow::OnUpdateEditSort(CCmdUI* pCmdUI)
{
unsigned int column = fPreferences.GetColumnLayout()->GetSortColumn();
pCmdUI->SetCheck(pCmdUI->m_nID - IDM_SORT_PATHNAME == column);
}
void MainWindow::OnHelpContents(void)
{
MyApp::HandleHelp(this, HELP_TOPIC_WELCOME);
//WinHelp(0, HELP_FINDER);
}
void MainWindow::OnHelpWebSite(void)
{
int err;
err = (int) ::ShellExecute(m_hWnd, L"open", kWebSiteURL, NULL, NULL,
SW_SHOWNORMAL);
if (err <= 32) {
CString msg;
if (err == ERROR_FILE_NOT_FOUND) {
msg = L"Windows call failed: web browser not found. (Sometimes"
L" it mistakenly reports this when IE is not the default"
L" browser.)";
ShowFailureMsg(this, msg, IDS_FAILED);
} else {
msg.Format(L"Unable to launch web browser (err=%d).", err);
ShowFailureMsg(this, msg, IDS_FAILED);
}
}
}
void MainWindow::OnHelpOrdering(void)
{
// How to order... ka-ching!
// TODO: no longer used?
LOGW("OnHelpOrdering -- not implemented");
//WinHelp(HELP_TOPIC_ORDERING_INFO, HELP_CONTEXT);
}
void MainWindow::OnHelpAbout(void)
{
int result;
AboutDialog dlg(this);
result = dlg.DoModal();
LOGV("HelpAbout returned %d", result);
/*
* User could've changed registration. If we're showing the registered
* user name in the title bar, update it.
*/
if (fpOpenArchive == NULL)
SetCPTitle();
}
void MainWindow::OnFileNewArchive(void)
{
// Create a new SHK archive, using a "save as" dialog to select the name.
CString filename, saveFolder, errStr;
GenericArchive* pOpenArchive;
CString errMsg;
CFileDialog dlg(FALSE, L"shk", NULL,
OFN_OVERWRITEPROMPT|OFN_NOREADONLYRETURN|OFN_HIDEREADONLY,
L"ShrinkIt Archives (*.shk)|*.shk||", this);
dlg.m_ofn.lpstrTitle = L"New Archive";
dlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder);
if (dlg.DoModal() != IDOK)
goto bail;
saveFolder = dlg.m_ofn.lpstrFile;
saveFolder = saveFolder.Left(dlg.m_ofn.nFileOffset);
fPreferences.SetPrefString(kPrOpenArchiveFolder, saveFolder);
filename = dlg.GetPathName();
LOGI("NEW FILE '%ls'", (LPCWSTR) filename);
/* remove file if it already exists */
errMsg = RemoveFile(filename);
if (!errMsg.IsEmpty()) {
ShowFailureMsg(this, errMsg, IDS_FAILED);
goto bail;
}
pOpenArchive = new NufxArchive;
errStr = pOpenArchive->New(filename, NULL);
if (!errStr.IsEmpty()) {
CString failed;
CheckedLoadString(&failed, IDS_FAILED);
MessageBox(errStr, failed, MB_ICONERROR);
delete pOpenArchive;
} else {
SwitchContentList(pOpenArchive);
fOpenArchivePathName = dlg.GetPathName();
SetCPTitle(fOpenArchivePathName, fpOpenArchive);
}
bail:
LOGD("--- OnFileNewArchive done");
}
void MainWindow::OnFileOpen(void)
{
// Handle request to open an archive or disk image.
CString openFilters;
CString saveFolder;
/* set up filters; the order must match enum FilterIndex */
openFilters = kOpenNuFX;
openFilters += kOpenBinaryII;
openFilters += kOpenACU;
openFilters += kOpenAppleSingle;
openFilters += kOpenDiskImage;
openFilters += kOpenAll;
openFilters += kOpenEnd;
CFileDialog dlg(TRUE, L"shk", NULL,
OFN_FILEMUSTEXIST, openFilters, this);
DWORD savedIndex = fPreferences.GetPrefLong(kPrLastOpenFilterIndex);
if (savedIndex < kFilterIndexFIRST || savedIndex > kFilterIndexMAX) {
// default to *.* if not set (zero) or out of range
savedIndex = kFilterIndexGeneric;
}
dlg.m_ofn.nFilterIndex = savedIndex;
dlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder);
if (dlg.DoModal() != IDOK)
goto bail;
fPreferences.SetPrefLong(kPrLastOpenFilterIndex, dlg.m_ofn.nFilterIndex);
saveFolder = dlg.m_ofn.lpstrFile;
saveFolder = saveFolder.Left(dlg.m_ofn.nFileOffset);
fPreferences.SetPrefString(kPrOpenArchiveFolder, saveFolder);
FilterIndex filterIndex = (FilterIndex) dlg.m_ofn.nFilterIndex;
if (filterIndex < kFilterIndexFIRST || filterIndex > kFilterIndexMAX) {
ASSERT(false);
LOGW("invalid filterIndex %d", filterIndex);
filterIndex = kFilterIndexGeneric;
}
DoOpenArchive(dlg.GetPathName(), dlg.GetFileExt(),
filterIndex, dlg.GetReadOnlyPref() != 0);
bail:
LOGD("--- OnFileOpen done");
}
void MainWindow::OnFileOpenVolume(void)
{
// Handle request to open a raw disk volume.
LOGD("--- OnFileOpenVolume");
int result;
OpenVolumeDialog dlg(this);
result = dlg.DoModal();
if (result != IDOK)
goto bail;
//DiskImg::SetAllowWritePhys0(fPreferences.GetPrefBool(kPrOpenVolumePhys0));
DoOpenVolume(dlg.fChosenDrive, dlg.fReadOnly != 0);
bail:
return;
}
void MainWindow::OnUpdateFileOpenVolume(CCmdUI* pCmdUI)
{
// don't really need this function
pCmdUI->Enable(TRUE);
}
void MainWindow::DoOpenArchive(const WCHAR* pathName, const WCHAR* ext,
FilterIndex filterIndex, bool readOnly)
{
if (LoadArchive(pathName, ext, filterIndex, readOnly) == 0) {
/* success, update title bar */
fOpenArchivePathName = pathName;
SetCPTitle(fOpenArchivePathName, fpOpenArchive);
} else {
/* some failures will close an open archive */
//if (fpOpenArchive == NULL)
// SetCPTitle();
}
}
void MainWindow::OnFileReopen(void)
{
ReopenArchive();
}
void MainWindow::OnUpdateFileReopen(CCmdUI* pCmdUI)
{
pCmdUI->Enable(fpOpenArchive != NULL);
}
void MainWindow::OnFileSave(void)
{
/*
* This may be called directly from tools, so don't assume that the
* conditions checked for in OnUpdateFileSave hold here.
*/
CString errMsg;
if (fpOpenArchive == NULL)
return;
{
CWaitCursor waitc;
errMsg = fpOpenArchive->Flush();
}
if (!errMsg.IsEmpty())
ShowFailureMsg(this, errMsg, IDS_FAILED);
// update the title bar
DoIdle();
}
void MainWindow::OnUpdateFileSave(CCmdUI* pCmdUI)
{
pCmdUI->Enable(fpOpenArchive != NULL && fpOpenArchive->IsModified());
}
void MainWindow::OnFileClose(void)
{
CloseArchive();
//SetCPTitle();
LOGD("--- OnFileClose done");
}
void MainWindow::OnUpdateFileClose(CCmdUI* pCmdUI)
{
pCmdUI->Enable(fpOpenArchive != NULL);
}
void MainWindow::OnFileArchiveInfo(void)
{
ArchiveInfoDialog* pDlg = NULL;
ASSERT(fpOpenArchive != NULL);
switch (fpOpenArchive->GetArchiveKind()) {
case GenericArchive::kArchiveNuFX:
pDlg = new NufxArchiveInfoDialog((NufxArchive*) fpOpenArchive, this);
break;
case GenericArchive::kArchiveDiskImage:
pDlg = new DiskArchiveInfoDialog((DiskArchive*) fpOpenArchive, this);
break;
case GenericArchive::kArchiveBNY:
pDlg = new BnyArchiveInfoDialog((BnyArchive*) fpOpenArchive, this);
break;
case GenericArchive::kArchiveACU:
pDlg = new AcuArchiveInfoDialog((AcuArchive*) fpOpenArchive, this);
break;
case GenericArchive::kArchiveAppleSingle:
pDlg = new AppleSingleArchiveInfoDialog((AppleSingleArchive*) fpOpenArchive, this);
break;
default:
LOGW("Unexpected archive type %d", fpOpenArchive->GetArchiveKind());
ASSERT(false);
return;
};
pDlg->DoModal();
delete pDlg;
}
void MainWindow::OnUpdateFileArchiveInfo(CCmdUI* pCmdUI)
{
pCmdUI->Enable(fpContentList != NULL);
}
void MainWindow::OnFilePrint(void)
{
PrintListing(fpContentList);
}
void MainWindow::OnUpdateFilePrint(CCmdUI* pCmdUI)
{
pCmdUI->Enable(fpContentList != NULL && fpContentList->GetItemCount() > 0);
}
void MainWindow::PrintListing(const ContentList* pContentList)
{
CPrintDialog dlg(FALSE); // use CPrintDialogEx for Win2K? CPageSetUpDialog?
PrintContentList pcl;
CDC dc;
int itemCount, numPages;
itemCount = pContentList->GetItemCount();
numPages = (itemCount + (pcl.GetLinesPerPage()-1)) / pcl.GetLinesPerPage();
dlg.m_pd.nFromPage = dlg.m_pd.nMinPage = 1;
dlg.m_pd.nToPage = dlg.m_pd.nMaxPage = numPages;
dlg.m_pd.hDevMode = fhDevMode;
dlg.m_pd.hDevNames = fhDevNames;
dlg.m_pd.Flags |= PD_USEDEVMODECOPIESANDCOLLATE;
dlg.m_pd.Flags &= ~(PD_NOPAGENUMS);
if (dlg.DoModal() != IDOK)
return;
if (dc.Attach(dlg.GetPrinterDC()) != TRUE) {
CString msg;
CheckedLoadString(&msg, IDS_PRINTER_NOT_USABLE);
ShowFailureMsg(this, msg, IDS_FAILED);
return;
}
pcl.Setup(&dc, this);
if (dlg.m_pd.Flags & PD_PAGENUMS)
pcl.Print(pContentList, dlg.m_pd.nFromPage, dlg.m_pd.nToPage);
else
pcl.Print(pContentList);
fhDevMode = dlg.m_pd.hDevMode;
fhDevNames = dlg.m_pd.hDevNames;
}
void MainWindow::OnFileExit(void)
{
// Handle Exit item by sending a close request.
SendMessage(WM_CLOSE, 0, 0);
}
void MainWindow::OnEditSelectAll(void)
{
ASSERT(fpContentList != NULL);
fpContentList->SelectAll();
}
void MainWindow::OnUpdateEditSelectAll(CCmdUI* pCmdUI)
{
pCmdUI->Enable(fpContentList != NULL);
}
void MainWindow::OnEditInvertSelection(void)
{
ASSERT(fpContentList != NULL);
fpContentList->InvertSelection();
}
void MainWindow::OnUpdateEditInvertSelection(CCmdUI* pCmdUI)
{
pCmdUI->Enable(fpContentList != NULL);
}
GenericEntry* MainWindow::GetSelectedItem(ContentList* pContentList)
{
if (pContentList->GetSelectedCount() != 1)
return NULL;
POSITION posn;
posn = pContentList->GetFirstSelectedItemPosition();
if (posn == NULL) {
ASSERT(false);
return NULL;
}
int num = pContentList->GetNextSelectedItem(/*ref*/ posn);
GenericEntry* pEntry = (GenericEntry*) pContentList->GetItemData(num);
if (pEntry == NULL) {
LOGW(" Glitch: couldn't find entry %d", num);
ASSERT(false);
}
return pEntry;
}
void MainWindow::HandleDoubleClick(void)
{
const uint32_t kTypeDimg = 0x64496d67;
const uint32_t kCreatorDcpy = 0x64437079;
bool handled = false;
ASSERT(fpContentList != NULL);
if (fpContentList->GetSelectedCount() == 0) {
/* nothing selected, they double-clicked outside first column */
LOGI("Double-click but nothing selected");
return;
}
if (fpContentList->GetSelectedCount() != 1) {
/* multiple items, just bring up viewer */
HandleView();
return;
}
/*
* Find the GenericEntry that corresponds to this item.
*/
GenericEntry* pEntry = GetSelectedItem(fpContentList);
if (pEntry == NULL)
return;
LOGI(" Double-click got '%ls'", (LPCWSTR) pEntry->GetPathNameUNI());
const WCHAR* ext;
long fileType, auxType;
ext = PathName::FindExtension(pEntry->GetPathNameUNI(), pEntry->GetFssep());
fileType = pEntry->GetFileType();
auxType = pEntry->GetAuxType();
/* // unit tests for MatchSemicolonList
MatchSemicolonList("gif; jpeg; jpg", "jpeg");
MatchSemicolonList("gif; jpeg; jpg", "jpg");
MatchSemicolonList("gif; jpeg; jpg", "gif");
MatchSemicolonList("gif;jpeg;jpg", "gif;");
MatchSemicolonList("gif; jpeg; jpg", "jpe");
MatchSemicolonList("gif; jpeg; jpg", "jpegx");
MatchSemicolonList("gif; jpeg; jpg", "jp");
MatchSemicolonList("gif; jpeg; jpg", "jpgx");
MatchSemicolonList("gif; jpeg; jpg", "if");
MatchSemicolonList("gif; jpeg; jpg", "gifs");
MatchSemicolonList("gif, jpeg; jpg", "jpeg");
MatchSemicolonList("", "jpeg");
MatchSemicolonList(";", "jpeg");
MatchSemicolonList("gif, jpeg; jpg", "");
*/
/*
* Figure out what to do with it.
*/
CString extViewerExts;
extViewerExts = fPreferences.GetPrefString(kPrExtViewerExts);
if (ext != NULL && MatchSemicolonList(extViewerExts, ext+1)) {
LOGI(" Launching external viewer for '%ls'", ext);
TmpExtractForExternal(pEntry);
handled = true;
} else if (pEntry->GetRecordKind() == GenericEntry::kRecordKindFile) {
if ((ext != NULL && (
wcsicmp(ext, L".shk") == 0 ||
wcsicmp(ext, L".sdk") == 0 ||
wcsicmp(ext, L".bxy") == 0)) ||
(fileType == 0xe0 && auxType == 0x8002))
{
LOGI(" Guessing NuFX");
TmpExtractAndOpen(pEntry, GenericEntry::kDataThread, kModeNuFX);
handled = true;
} else
if ((ext != NULL && (
wcsicmp(ext, L".bny") == 0 ||
wcsicmp(ext, L".bqy") == 0)) ||
(fileType == 0xe0 && auxType == 0x8000))
{
LOGI(" Guessing Binary II");
TmpExtractAndOpen(pEntry, GenericEntry::kDataThread, kModeBinaryII);
handled = true;
} else
if ((ext != NULL && (
wcsicmp(ext, L".acu") == 0)) ||
(fileType == 0xe0 && auxType == 0x8001))
{
LOGI(" Guessing ACU");
TmpExtractAndOpen(pEntry, GenericEntry::kDataThread, kModeACU);
handled = true;
} else
if ((ext != NULL && (
wcsicmp(ext, L".as") == 0)) ||
(fileType == 0xe0 && auxType == 0x0001))
{
LOGI(" Guessing AppleSingle");
TmpExtractAndOpen(pEntry, GenericEntry::kDataThread, kModeAppleSingle);
handled = true;
} else
if (fileType == kTypeDimg && auxType == kCreatorDcpy &&
pEntry->GetDataForkLen() == 819284)
{
/* type is dImg, creator is dCpy, length is 800K + DC stuff */
LOGI(" Looks like a DiskCopy disk image");
TmpExtractAndOpen(pEntry, GenericEntry::kDataThread, kModeDiskImage);
handled = true;
}
} else if (pEntry->GetRecordKind() == GenericEntry::kRecordKindForkedFile) {
/*
* On Mac HFS volumes these can have resource forks (which we ignore).
* We could probably just generally ignore resource forks for all of
* the above files, rather than pulling this one out separately, but
* this is the only one that could reasonably have a resource fork.
*/
if (fileType == kTypeDimg && auxType == kCreatorDcpy &&
pEntry->GetDataForkLen() == 819284)
{
/* type is dImg, creator is dCpy, length is 800K + DC stuff */
LOGI(" Looks like a (forked) DiskCopy disk image");
TmpExtractAndOpen(pEntry, GenericEntry::kDataThread, kModeDiskImage);
handled = true;
}
} else if (pEntry->GetRecordKind() == GenericEntry::kRecordKindDisk) {
LOGI(" Opening archived disk image");
TmpExtractAndOpen(pEntry, GenericEntry::kDiskImageThread, kModeDiskImage);
handled = true;
}
if (!handled) {
// standard viewer
HandleView();
}
/* set "/t" temp flag and delete afterward, warning user (?) */
}
int MainWindow::TmpExtractAndOpen(GenericEntry* pEntry, int threadKind,
const WCHAR* modeStr)
{
CString dispName;
bool mustDelete = false;
/*
* Get the name to display in the title bar. Double quotes will
* screw it up, so we have to replace them. (We could escape them,
* but then we'd also have to escape the escape char.)
*/
dispName = pEntry->GetFileName();
dispName.Replace('"', '_');
WCHAR nameBuf[MAX_PATH];
UINT unique;
unique = GetTempFileName(fPreferences.GetPrefString(kPrTempPath),
L"CPfile", 0, nameBuf);
if (unique == 0) {
DWORD dwerr = ::GetLastError();
LOGI("GetTempFileName failed on '%ls' (err=%ld)",
fPreferences.GetPrefString(kPrTempPath), dwerr);
return dwerr;
}
mustDelete = true;
/*
* Open the temp file and extract the data into it.
*/
CString errMsg;
int result;
FILE* fp;
fp = _wfopen(nameBuf, L"wb");
if (fp != NULL) {
LOGD("Extracting to '%ls' (unique=%d)", nameBuf, unique);
result = pEntry->ExtractThreadToFile(threadKind, fp,
GenericEntry::kConvertEOLOff, GenericEntry::kConvertHAOff,
&errMsg);
fclose(fp);
if (result == IDOK) {
/* success */
CString parameters;
parameters.Format(L"-mode %ls -dispname \"%ls\" -temparc \"%ls\"",
modeStr, (LPCWSTR) dispName, nameBuf);
int err;
err = (int) ::ShellExecute(m_hWnd, L"open",
gMyApp.GetExeFileName(), parameters, NULL,
SW_SHOWNORMAL);
if (err <= 32) {
CString msg;
msg.Format(L"Unable to launch CiderPress (err=%d).", err);
ShowFailureMsg(this, msg, IDS_FAILED);
} else {
/* during dev, "missing DLL" causes false-positive success */
LOGI("Successfully launched CiderPress, mode=%ls", modeStr);
mustDelete = false; // up to newly-launched app
}
} else {
ShowFailureMsg(this, errMsg, IDS_FAILED);
}
} else {
CString msg;
msg.Format(L"Unable to open temp file '%ls'.", nameBuf);
::ShowFailureMsg(this, msg, IDS_FAILED);
}
if (mustDelete) {
LOGI("Deleting '%ls'", nameBuf);
_wunlink(nameBuf);
}
return 0;
}
int MainWindow::TmpExtractForExternal(GenericEntry* pEntry)
{
const WCHAR* ext;
ext = PathName::FindExtension(pEntry->GetPathNameUNI(), pEntry->GetFssep());
WCHAR nameBuf[MAX_PATH];
UINT unique;
unique = GetTempFileName(fPreferences.GetPrefString(kPrTempPath),
L"CPfile", 0, nameBuf);
if (unique == 0) {
DWORD dwerr = ::GetLastError();
LOGI("GetTempFileName failed on '%ls' (err=%ld)",
fPreferences.GetPrefString(kPrTempPath), dwerr);
return dwerr;
}
fDeleteList.Add(nameBuf); // file is created by GetTempFileName
wcscat(nameBuf, ext);
/*
* Open the temp file and extract the data into it.
*/
CString errMsg;
int result;
FILE* fp;
fp = _wfopen(nameBuf, L"wb");
if (fp != NULL) {
fDeleteList.Add(nameBuf); // second file created by fopen
LOGI("Extracting to '%ls' (unique=%d)", nameBuf, unique);
result = pEntry->ExtractThreadToFile(GenericEntry::kDataThread, fp,
GenericEntry::kConvertEOLOff, GenericEntry::kConvertHAOff,
&errMsg);
fclose(fp);
if (result == IDOK) {
/* success */
int err;
err = (int) ::ShellExecute(m_hWnd, L"open", nameBuf, NULL,
NULL, SW_SHOWNORMAL);
if (err <= 32) {
CString msg;
msg.Format(L"Unable to launch external viewer (err=%d).", err);
ShowFailureMsg(this, msg, IDS_FAILED);
} else {
LOGI("Successfully launched external viewer");
}
} else {
ShowFailureMsg(this, errMsg, IDS_FAILED);
}
} else {
CString msg;
msg.Format(L"Unable to open temp file '%ls'.", nameBuf);
ShowFailureMsg(this, msg, IDS_FAILED);
}
return 0;
}
#if 0
/*
* Handle a "default action" selection from the right-click menu. The
* action only applies to the record that was clicked on, so we need to
* retrieve that from the control.
*/
void MainWindow::OnRtClkDefault(void)
{
int idx;
ASSERT(fpContentList != NULL);
idx = fpContentList->GetRightClickItem();
ASSERT(idx != -1);
LOGI("OnRtClkDefault %d", idx);
fpContentList->ClearRightClickItem();
}
#endif
/*
* ===================================
* Progress meter
* ===================================
*/
/*
* There are two different mechanisms for reporting progress: ActionProgress
* dialogs (for adding/extracting files) and a small box in the lower
* right-hand corner (for opening archives). These functions will set
* the progress in the active action progress dialog if it exists, or
* will set the percentage in the window frame if not.
*/
void MainWindow::SetProgressBegin(void)
{
if (fpActionProgress != NULL)
fpActionProgress->SetProgress(0);
else
fStatusBar.SetPaneText(kProgressPane, L"--%");
//LOGI(" Complete: BEGIN");
/* redraw stuff with the changes */
(void) PeekAndPump();
}
int MainWindow::SetProgressUpdate(int percent, const WCHAR* oldName,
const WCHAR* newName)
{
int status = IDOK;
if (fpActionProgress != NULL) {
status = fpActionProgress->SetProgress(percent);
if (oldName != NULL)
fpActionProgress->SetArcName(oldName);
if (newName != NULL)
fpActionProgress->SetFileName(newName);
} else {
WCHAR buf[8];
wsprintf(buf, L"%d%%", percent);
fStatusBar.SetPaneText(kProgressPane, buf);
//LOGI(" Complete: %ls", buf);
}
if (!PeekAndPump()) {
LOGI("SetProgressUpdate: shutdown?!");
}
//EventPause(10); // DEBUG DEBUG
return status;
}
void MainWindow::SetProgressEnd(void)
{
if (fpActionProgress != NULL)
fpActionProgress->SetProgress(100);
else
fStatusBar.SetPaneText(kProgressPane, L"");
// EventPause(100); // DEBUG DEBUG
//LOGI(" Complete: END");
}
bool MainWindow::SetProgressCounter(const WCHAR* str, long val)
{
/* if the main window is enabled, user could activate menus */
ASSERT(!IsWindowEnabled());
if (fpProgressCounter != NULL) {
//LOGI("SetProgressCounter '%ls' %d", str, val);
CString msg;
if (str != NULL)
fpProgressCounter->SetCounterFormat(str);
fpProgressCounter->SetCount((int) val);
} else {
if (val < 0) {
fStatusBar.SetPaneText(kProgressPane, L"");
} else {
CString tmpStr;
tmpStr.Format(L"%ld", val);
fStatusBar.SetPaneText(kProgressPane, tmpStr);
}
}
if (!PeekAndPump()) {
LOGI("SetProgressCounter: shutdown?!");
}
//EventPause(10); // DEBUG DEBUG
if (fpProgressCounter != NULL)
return !fpProgressCounter->GetCancel();
else
return true;
}
BOOL MainWindow::PeekAndPump(void)
{
MSG msg;
while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
if (!AfxGetApp()->PumpMessage()) {
::PostQuitMessage(0);
return FALSE;
}
}
LONG lIdle = 0;
while (AfxGetApp()->OnIdle(lIdle++))
;
return TRUE;
}
void MainWindow::EventPause(int duration)
{
int count = duration / 10;
for (int i = 0; i < count; i++) {
PeekAndPump();
::Sleep(10);
}
}
/*static*/ BOOL CALLBACK MainWindow::PrintAbortProc(HDC hDC, int nCode)
{
MainWindow* pMain = (MainWindow*)::AfxGetMainWnd();
pMain->PeekAndPump();
if (pMain->GetAbortPrinting()) {
LOGI("PrintAbortProc returning FALSE (abort printing)");
return FALSE;
}
LOGI(" PrintAbortProc returning TRUE (continue printing)");
return TRUE;
}
/*
* ===================================
* Support functions
* ===================================
*/
void MainWindow::DrawEmptyClientArea(CDC* pDC, const CRect& clientRect)
{
CBrush brush;
brush.CreateSolidBrush(::GetSysColor(COLOR_APPWORKSPACE)); // dk gray
CBrush* pOldBrush = pDC->SelectObject(&brush);
pDC->FillRect(&clientRect, &brush);
pDC->SelectObject(pOldBrush);
CPen penWH(PS_SOLID, 1, ::GetSysColor(COLOR_3DHIGHLIGHT)); // white
CPen penLG(PS_SOLID, 1, ::GetSysColor(COLOR_3DLIGHT)); // lt gray
CPen penDG(PS_SOLID, 1, ::GetSysColor(COLOR_3DSHADOW)); // dk gray
CPen penBL(PS_SOLID, 1, ::GetSysColor(COLOR_3DDKSHADOW)); // near-black
CPen* pOldPen = pDC->SelectObject(&penWH);
//pDC->SelectObject(&penWH);
pDC->MoveTo(clientRect.right-1, clientRect.top);
pDC->LineTo(clientRect.right-1, clientRect.bottom-1);
pDC->LineTo(clientRect.left-1, clientRect.bottom-1);
pDC->SelectObject(&penBL);
pDC->MoveTo(clientRect.right-3, clientRect.top+1);
pDC->LineTo(clientRect.left+1, clientRect.top+1);
pDC->LineTo(clientRect.left+1, clientRect.bottom-2);
pDC->SelectObject(&penLG);
pDC->MoveTo(clientRect.right-2, clientRect.top+1);
pDC->LineTo(clientRect.right-2, clientRect.bottom-2);
pDC->LineTo(clientRect.left, clientRect.bottom-2);
pDC->SelectObject(&penDG);
pDC->MoveTo(clientRect.right-2, clientRect.top);
pDC->LineTo(clientRect.left, clientRect.top);
pDC->LineTo(clientRect.left, clientRect.bottom-1);
pDC->SelectObject(pOldPen);
}
GenericArchive* MainWindow::CreateArchiveInstance(FilterIndex filterIndex) const
{
GenericArchive* pOpenArchive = NULL;
switch (filterIndex) {
case kFilterIndexNuFX: pOpenArchive = new NufxArchive; break;
case kFilterIndexBinaryII: pOpenArchive = new BnyArchive; break;
case kFilterIndexACU: pOpenArchive = new AcuArchive; break;
case kFilterIndexAppleSingle: pOpenArchive = new AppleSingleArchive; break;
case kFilterIndexDiskImage: pOpenArchive = new DiskArchive; break;
default: ASSERT(false); break;
}
return pOpenArchive;
}
int MainWindow::LoadArchive(const WCHAR* fileName, const WCHAR* extension,
FilterIndex filterIndex, bool readOnly)
{
static const WCHAR kFileArchive[] = L"This appears to be a file archive.";
GenericArchive::OpenResult openResult;
const FilterIndex origFilterIndex = filterIndex;
GenericArchive* pOpenArchive = NULL;
LOGI("LoadArchive: '%ls' ro=%d idx=%d", fileName, readOnly, filterIndex);
/* close any existing archive to avoid weirdness from re-open */
CloseArchive();
/*
* If they used the "All Files (*.*)" filter, try guess based
* on the file extension.
*/
if (filterIndex == kFilterIndexGeneric) {
int i;
for (i = 0; i < NELEM(gExtensionToIndex); i++) {
if (wcsicmp(extension, gExtensionToIndex[i].extension) == 0) {
filterIndex = gExtensionToIndex[i].idx;
break;
}
}
if (i == NELEM(gExtensionToIndex)) {
// no match found, use "disk image" as initial guess
filterIndex = kFilterIndexDiskImage;
}
}
/*
* Try to open the file according to the specified filter index. If
* it works, we're done. Trying this first ensures that you can choose
* to open, say, a .SDK file as either ShrinkIt or Disk Image.
*
* It's possible to cancel the file open if you have "confirm disk image
* format set" and the file is a disk image. In that case we want to
* return with an error result, but without showing an error dialog.
*/
CString firstErrStr;
pOpenArchive = CreateArchiveInstance(filterIndex);
LOGD("First try: %ls", (LPCWSTR) pOpenArchive->GetDescription());
openResult = pOpenArchive->Open(fileName, readOnly, &firstErrStr);
if (openResult == GenericArchive::kResultSuccess) {
// success!
SwitchContentList(pOpenArchive);
return 0;
} else if (openResult == GenericArchive::kResultFileArchive) {
if (wcsicmp(extension, L"zip") == 0) {
// we could probably just return in this case
firstErrStr = L"Zip archives with multiple files are not supported";
} else {
firstErrStr = kFileArchive;
}
} else if (openResult == GenericArchive::kResultCancel) {
LOGD("canceled");
delete pOpenArchive;
return -1;
}
delete pOpenArchive;
/*
* That didn't work. Try the others.
*/
for (int i = kFilterIndexFIRST; i <= kFilterIndexLASTNG; i++) {
if (i == filterIndex) continue;
pOpenArchive = CreateArchiveInstance((FilterIndex) i);
LOGD("Now trying: %ls", (LPCWSTR) pOpenArchive->GetDescription());
CString dummyErrStr;
openResult = pOpenArchive->Open(fileName, readOnly, &dummyErrStr);
if (openResult == GenericArchive::kResultSuccess) {
// success!
SwitchContentList(pOpenArchive);
return 0;
} else if (openResult == GenericArchive::kResultCancel) {
LOGD("cancelled");
delete pOpenArchive;
return -1;
} else if (i == kFilterIndexNuFX && firstErrStr == kFileArchive) {
// For .shk files we first check to see if it's a disk image,
// then we try by file. If it's a damaged file archive, we
// really want to present the file archive failure message, not
// "that looks like a file archive". So we tweak the result a
// little here.
//
// This doesn't catch all the cases where the NufxLib error
// message is more useful than the DiskImg error message, but
// it's hard to accurately determine what the most-accurate
// message is in some cases.
firstErrStr = dummyErrStr;
}
delete pOpenArchive;
}
/*
* Nothing worked. Show the original error message so that it matches
* up with the chosen filter index -- if they seemed to be trying to
* open an AppleSingle, don't tell them we failed because the file isn't
* a disk image.
*
* If they were using the Generic filter, they'll get the message from
* the disk image library. It might make more sense to just say "we
* couldn't figure out what this was", but most of the time people are
* trying to open disk images anyway.
*/
if (firstErrStr.IsEmpty()) {
// not expected; put up a generic message if it happens
firstErrStr = L"Unable to determine what kind of file this is.";
}
ShowFailureMsg(this, firstErrStr, IDS_FAILED);
return -1;
}
int MainWindow::DoOpenVolume(CString drive, bool readOnly)
{
int result = -1;
ASSERT(drive.GetLength() > 0);
CString errStr;
//char filename[4] = "_:\\";
//filename[0] = driveLetter;
LOGI("FileOpenVolume '%ls' %d", (LPCWSTR)drive, readOnly);
/* close existing archive */
CloseArchive();
GenericArchive* pOpenArchive = NULL;
pOpenArchive = new DiskArchive;
{
CWaitCursor waitc;
GenericArchive::OpenResult openResult;
openResult = pOpenArchive->Open(drive, readOnly, &errStr);
if (openResult == GenericArchive::kResultCancel) {
// this bubbles out of the format confirmation dialog
goto bail;
} else if (openResult != GenericArchive::kResultSuccess) {
if (!errStr.IsEmpty())
ShowFailureMsg(this, errStr, IDS_FAILED);
goto bail;
}
}
// success!
SwitchContentList(pOpenArchive);
pOpenArchive = NULL;
fOpenArchivePathName = drive;
result = 0;
fOpenArchivePathName = drive;
SetCPTitle(fOpenArchivePathName, fpOpenArchive);
bail:
if (pOpenArchive != NULL) {
ASSERT(result != 0);
delete pOpenArchive;
}
return result;
}
void MainWindow::ReopenArchive(void)
{
if (fpOpenArchive == NULL) {
ASSERT(false);
return;
}
/* clear the flag, regardless of success or failure */
fNeedReopen = false;
GenericArchive* pOpenArchive = NULL;
CString pathName = fpOpenArchive->GetPathName();
bool readOnly = fpOpenArchive->IsReadOnly();
GenericArchive::ArchiveKind archiveKind = fpOpenArchive->GetArchiveKind();
GenericArchive::OpenResult openResult;
CString errStr;
/* if the open fails we *don't* want to leave the previous content up */
LOGI("Reopening '%ls' ro=%d kind=%d",
(LPCWSTR) pathName, readOnly, archiveKind);
CloseArchive();
switch (archiveKind) {
case GenericArchive::kArchiveDiskImage:
pOpenArchive = new DiskArchive;
break;
case GenericArchive::kArchiveNuFX:
pOpenArchive = new NufxArchive;
break;
case GenericArchive::kArchiveBNY:
pOpenArchive = new BnyArchive;
break;
default:
ASSERT(false);
return;
}
openResult = pOpenArchive->Open(pathName, readOnly, &errStr);
if (openResult == GenericArchive::kResultCancel) {
// this bubbles out of the format confirmation dialog
goto bail;
} else if (openResult != GenericArchive::kResultSuccess) {
if (!errStr.IsEmpty())
ShowFailureMsg(this, errStr, IDS_FAILED);
goto bail;
}
LOGI(" Reopen was successful");
SwitchContentList(pOpenArchive);
pOpenArchive = NULL;
SetCPTitle(pathName, fpOpenArchive);
bail:
delete pOpenArchive;
}
bool MainWindow::IsOpenPathName(const WCHAR* path)
{
if (fpOpenArchive == NULL)
return false;
if (wcsicmp(path, fpOpenArchive->GetPathName()) == 0)
return true;
return false;
}
void MainWindow::SwitchContentList(GenericArchive* pOpenArchive)
{
assert(pOpenArchive != NULL);
/*
* We've got an archive opened successfully. If we already had one
* open, shut it. (This assumes that closing an archive is a simple
* matter of closing files and freeing storage. If we needed to do
* something that might fail, like flush changes, we should've done
* that before getting this far to avoid confusion.)
*/
if (fpOpenArchive != NULL)
CloseArchive();
ASSERT(fpOpenArchive == NULL);
ASSERT(fpContentList == NULL);
/*
* Without this we get an assertion failure in CImageList::Attach if we
* call here from ReopenArchive. I think Windows needs to do some
* cleanup, though I don't understand how the reopen case differs from
* the usual case. Maybe there's more stuff pending in the "reopen"
* case? In any event, this seems to work, which is all you can hope
* for from MFC. It does, however, make the screen flash, which it
* didn't do before.
*
* UPDATE: this tripped once while I was debugging, even with this. The
* PeekAndPump function does force the idle loop to run, so I'm not sure
* why it failed, unless the debugger somehow affected the idle
* processing. Yuck.
*
* The screen flash bugged me so I took it back out. And the assert
* didn't hit. I really, really love Windows.
*/
//PeekAndPump();
fpContentList = new ContentList(pOpenArchive,
fPreferences.GetColumnLayout());
CRect sizeRect;
GetClientRect(&sizeRect);
fpContentList->Create(WS_CHILD | WS_VISIBLE | WS_VSCROLL,
sizeRect, this, IDC_CONTENT_LIST);
fpOpenArchive = pOpenArchive;
}
void MainWindow::CloseArchiveWOControls(void)
{
if (fpOpenArchive != NULL) {
//fpOpenArchive->Close();
LOGI("Deleting OpenArchive");
delete fpOpenArchive;
fpOpenArchive = NULL;
}
}
void MainWindow::CloseArchive(void)
{
CWaitCursor waitc; // closing large compressed archive can be slow
// destroy the ContentList
if (fpContentList != NULL) {
LOGI("Destroying ContentList");
fpContentList->DestroyWindow(); // auto-cleanup invokes "delete"
fpContentList = NULL;
}
// destroy the GenericArchive
CloseArchiveWOControls();
// reset the title bar
SetCPTitle();
}
void MainWindow::SetCPTitle(const WCHAR* pathname, GenericArchive* pOpenArchive)
{
ASSERT(pathname != NULL);
CString title;
CString appName;
CheckedLoadString(&appName, IDS_MB_APP_NAME);
CString archiveDescription(pOpenArchive->GetDescription());
title.Format(L"%ls - %ls (%ls)", (LPCWSTR) appName, pathname,
(LPCWSTR) archiveDescription);
if (fpOpenArchive->IsReadOnly()) {
CString readOnly;
CheckedLoadString(&readOnly, IDS_READONLY);
title += L" ";
title += readOnly;
}
SetWindowText(title);
}
void MainWindow::SetCPTitle(void)
{
CString appName, regName, title;
CString user, company, reg, versions, expire;
#if 0
if (gMyApp.fRegistry.GetRegistration(&user, &company, &reg, &versions,
&expire) == 0)
{
if (reg.IsEmpty()) {
regName += _T(" (unregistered)");
} else {
regName += _T(" (registered to ");
regName += user;
regName += _T(")");
// include company?
}
}
#endif
CheckedLoadString(&appName, IDS_MB_APP_NAME);
title = appName + regName;
SetWindowText(title);
}
CString MainWindow::GetPrintTitle(void)
{
CString title;
if (fpOpenArchive == NULL) {
ASSERT(false);
return title;
}
CString appName;
CheckedLoadString(&appName, IDS_MB_APP_NAME);
CString archiveDescription(fpOpenArchive->GetDescription());
title.Format(L"%ls - %ls (%ls)", (LPCWSTR) appName,
(LPCWSTR) fOpenArchivePathName, (LPCWSTR) archiveDescription);
return title;
}
void MainWindow::SuccessBeep(void)
{
const Preferences* pPreferences = GET_PREFERENCES();
if (pPreferences->GetPrefBool(kPrBeepOnSuccess)) {
LOGI("<happy-beep>");
::MessageBeep(MB_OK);
}
}
void MainWindow::FailureBeep(void)
{
const Preferences* pPreferences = GET_PREFERENCES();
if (pPreferences->GetPrefBool(kPrBeepOnSuccess)) {
LOGI("<failure-beep>");
::MessageBeep(MB_ICONEXCLAMATION); // maybe MB_ICONHAND?
}
}
CString MainWindow::RemoveFile(const WCHAR* fileName)
{
CString errMsg;
int cc;
cc = _wunlink(fileName);
if (cc < 0 && errno != ENOENT) {
int err = errno;
LOGI("Failed removing file '%ls', errno=%d", fileName, err);
errMsg.Format(L"Unable to remove '%ls': %hs.",
fileName, strerror(err));
if (err == EACCES)
errMsg += L"\n\n(Make sure the file isn't open.)";
}
return errMsg;
}
/*static*/ void MainWindow::ConfigureReformatFromPreferences(ReformatHolder* pReformat)
{
const Preferences* pPreferences = GET_PREFERENCES();
pReformat->SetReformatAllowed(ReformatHolder::kReformatRaw, true);
pReformat->SetReformatAllowed(ReformatHolder::kReformatHexDump, true);
pReformat->SetReformatAllowed(ReformatHolder::kReformatTextEOL_HA,
pPreferences->GetPrefBool(kPrConvTextEOL_HA));
pReformat->SetReformatAllowed(ReformatHolder::kReformatResourceFork,
pPreferences->GetPrefBool(kPrConvResources));
pReformat->SetReformatAllowed(ReformatHolder::kReformatProDOSDirectory,
pPreferences->GetPrefBool(kPrConvProDOSFolder));
pReformat->SetReformatAllowed(ReformatHolder::kReformatPascalText,
pPreferences->GetPrefBool(kPrConvPascalText));
pReformat->SetReformatAllowed(ReformatHolder::kReformatPascalCode,
pPreferences->GetPrefBool(kPrConvPascalCode));
pReformat->SetReformatAllowed(ReformatHolder::kReformatCPMText,
pPreferences->GetPrefBool(kPrConvCPMText));
pReformat->SetReformatAllowed(ReformatHolder::kReformatApplesoft,
pPreferences->GetPrefBool(kPrConvApplesoft));
pReformat->SetReformatAllowed(ReformatHolder::kReformatApplesoft_Hilite,
pPreferences->GetPrefBool(kPrConvApplesoft));
pReformat->SetReformatAllowed(ReformatHolder::kReformatInteger,
pPreferences->GetPrefBool(kPrConvInteger));
pReformat->SetReformatAllowed(ReformatHolder::kReformatInteger_Hilite,
pPreferences->GetPrefBool(kPrConvInteger));
pReformat->SetReformatAllowed(ReformatHolder::kReformatBusiness,
pPreferences->GetPrefBool(kPrConvBusiness));
pReformat->SetReformatAllowed(ReformatHolder::kReformatBusiness_Hilite,
pPreferences->GetPrefBool(kPrConvBusiness));
pReformat->SetReformatAllowed(ReformatHolder::kReformatSCAssem,
pPreferences->GetPrefBool(kPrConvSCAssem));
pReformat->SetReformatAllowed(ReformatHolder::kReformatMerlin,
pPreferences->GetPrefBool(kPrConvSCAssem));
pReformat->SetReformatAllowed(ReformatHolder::kReformatLISA2,
pPreferences->GetPrefBool(kPrConvSCAssem));
pReformat->SetReformatAllowed(ReformatHolder::kReformatLISA3,
pPreferences->GetPrefBool(kPrConvSCAssem));
pReformat->SetReformatAllowed(ReformatHolder::kReformatLISA4,
pPreferences->GetPrefBool(kPrConvSCAssem));
pReformat->SetReformatAllowed(ReformatHolder::kReformatMonitor8,
pPreferences->GetPrefBool(kPrConvDisasm));
pReformat->SetReformatAllowed(ReformatHolder::kReformatDisasmMerlin8,
pPreferences->GetPrefBool(kPrConvDisasm));
pReformat->SetReformatAllowed(ReformatHolder::kReformatMonitor16Long,
pPreferences->GetPrefBool(kPrConvDisasm));
pReformat->SetReformatAllowed(ReformatHolder::kReformatMonitor16Short,
pPreferences->GetPrefBool(kPrConvDisasm));
pReformat->SetReformatAllowed(ReformatHolder::kReformatDisasmOrcam16,
pPreferences->GetPrefBool(kPrConvDisasm));
pReformat->SetReformatAllowed(ReformatHolder::kReformatAWGS_WP,
pPreferences->GetPrefBool(kPrConvGWP));
pReformat->SetReformatAllowed(ReformatHolder::kReformatTeach,
pPreferences->GetPrefBool(kPrConvGWP));
pReformat->SetReformatAllowed(ReformatHolder::kReformatGWP,
pPreferences->GetPrefBool(kPrConvGWP));
pReformat->SetReformatAllowed(ReformatHolder::kReformatMagicWindow,
pPreferences->GetPrefBool(kPrConvText8));
pReformat->SetReformatAllowed(ReformatHolder::kReformatGutenberg,
pPreferences->GetPrefBool(kPrConvGutenberg));
pReformat->SetReformatAllowed(ReformatHolder::kReformatAWP,
pPreferences->GetPrefBool(kPrConvAWP));
pReformat->SetReformatAllowed(ReformatHolder::kReformatAWP,
pPreferences->GetPrefBool(kPrConvAWP));
pReformat->SetReformatAllowed(ReformatHolder::kReformatADB,
pPreferences->GetPrefBool(kPrConvADB));
pReformat->SetReformatAllowed(ReformatHolder::kReformatASP,
pPreferences->GetPrefBool(kPrConvASP));
pReformat->SetReformatAllowed(ReformatHolder::kReformatHiRes,
pPreferences->GetPrefBool(kPrConvHiRes));
pReformat->SetReformatAllowed(ReformatHolder::kReformatHiRes_BW,
pPreferences->GetPrefBool(kPrConvHiRes));
pReformat->SetReformatAllowed(ReformatHolder::kReformatDHR_Latched,
pPreferences->GetPrefBool(kPrConvDHR));
pReformat->SetReformatAllowed(ReformatHolder::kReformatDHR_BW,
pPreferences->GetPrefBool(kPrConvDHR));
pReformat->SetReformatAllowed(ReformatHolder::kReformatDHR_Plain140,
pPreferences->GetPrefBool(kPrConvDHR));
pReformat->SetReformatAllowed(ReformatHolder::kReformatDHR_Window,
pPreferences->GetPrefBool(kPrConvDHR));
pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_PIC,
pPreferences->GetPrefBool(kPrConvSHR));
pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_JEQ,
pPreferences->GetPrefBool(kPrConvSHR));
pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_Paintworks,
pPreferences->GetPrefBool(kPrConvSHR));
pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_Packed,
pPreferences->GetPrefBool(kPrConvSHR));
pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_APF,
pPreferences->GetPrefBool(kPrConvSHR));
pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_3200,
pPreferences->GetPrefBool(kPrConvSHR));
pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_3201,
pPreferences->GetPrefBool(kPrConvSHR));
pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_DG256,
pPreferences->GetPrefBool(kPrConvSHR));
pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_DG3200,
pPreferences->GetPrefBool(kPrConvSHR));
pReformat->SetReformatAllowed(ReformatHolder::kReformatPrintShop,
pPreferences->GetPrefBool(kPrConvPrintShop));
pReformat->SetReformatAllowed(ReformatHolder::kReformatMacPaint,
pPreferences->GetPrefBool(kPrConvMacPaint));
pReformat->SetOption(ReformatHolder::kOptHiliteHexDump,
pPreferences->GetPrefBool(kPrHighlightHexDump));
pReformat->SetOption(ReformatHolder::kOptHiliteBASIC,
pPreferences->GetPrefBool(kPrHighlightBASIC));
pReformat->SetOption(ReformatHolder::kOptHiResBW,
pPreferences->GetPrefBool(kPrConvHiResBlackWhite));
pReformat->SetOption(ReformatHolder::kOptDHRAlgorithm,
pPreferences->GetPrefLong(kPrConvDHRAlgorithm));
pReformat->SetOption(ReformatHolder::kOptRelaxGfxTypeCheck,
pPreferences->GetPrefBool(kPrRelaxGfxTypeCheck));
pReformat->SetOption(ReformatHolder::kOptOneByteBrkCop,
pPreferences->GetPrefBool(kPrDisasmOneByteBrkCop));
pReformat->SetOption(ReformatHolder::kOptMouseTextToASCII,
pPreferences->GetPrefBool(kPrConvMouseTextToASCII));
}
/*static*/ ReformatHolder::SourceFormat MainWindow::ReformatterSourceFormat(
DiskImg::FSFormat format)
{
/*
* Gutenberg both UsesDOSFileStructure and is formatted with
* kFormatGutenberg, so check for the latter first.
*/
if (format == DiskImg::kFormatGutenberg)
return ReformatHolder::kSourceFormatGutenberg;
else if (DiskImg::UsesDOSFileStructure(format))
return ReformatHolder::kSourceFormatDOS;
else if (format == DiskImg::kFormatCPM)
return ReformatHolder::kSourceFormatCPM;
else
return ReformatHolder::kSourceFormatGeneric;
}