/* * 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_WM_CLOSE() 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"); } void MainWindow::OnClose() { SaveWinPlacement(); CFrameWnd::OnClose(); } 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""); RestoreWinPlacement(); 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 * =================================== */ static const WCHAR kMainWinSection[] = L"mainwin"; static const WCHAR kMainWin_Left[] = L"left"; static const WCHAR kMainWin_Top[] = L"top"; static const WCHAR kMainWin_Right[] = L"right"; static const WCHAR kMainWin_Bottom[] = L"bottom"; static const WCHAR kMainWin_Cmd[] = L"cmd"; void MainWindow::SaveWinPlacement() { // Capture the current window position. At the point where the destructor // fires, too much has been shut down, so we need to do it here. WINDOWPLACEMENT wndpl; wndpl.length = sizeof(wndpl); if (!GetWindowPlacement(&wndpl)) { LOGW("Unable to get window placement"); return; } LOGD("Window is at ltrb=%ld,%ld %ldx%ld showCmd=%d", wndpl.rcNormalPosition.left, wndpl.rcNormalPosition.top, wndpl.rcNormalPosition.right - wndpl.rcNormalPosition.left, wndpl.rcNormalPosition.bottom - wndpl.rcNormalPosition.top, wndpl.showCmd); gMyApp.WriteProfileInt(kMainWinSection, kMainWin_Left, wndpl.rcNormalPosition.left); gMyApp.WriteProfileInt(kMainWinSection, kMainWin_Top, wndpl.rcNormalPosition.top); gMyApp.WriteProfileInt(kMainWinSection, kMainWin_Right, wndpl.rcNormalPosition.right); gMyApp.WriteProfileInt(kMainWinSection, kMainWin_Bottom, wndpl.rcNormalPosition.bottom); gMyApp.WriteProfileInt(kMainWinSection, kMainWin_Cmd, wndpl.showCmd); } void MainWindow::RestoreWinPlacement() { const int kNoVal = -32000; RECT normPos; normPos.left = gMyApp.GetProfileInt(kMainWinSection, kMainWin_Left, kNoVal); normPos.top = gMyApp.GetProfileInt(kMainWinSection, kMainWin_Top, kNoVal); normPos.right = gMyApp.GetProfileInt(kMainWinSection, kMainWin_Right, kNoVal); normPos.bottom = gMyApp.GetProfileInt(kMainWinSection, kMainWin_Bottom, kNoVal); int cmd = gMyApp.GetProfileInt(kMainWinSection, kMainWin_Cmd, kNoVal); if (normPos.left == kNoVal || normPos.top == kNoVal || normPos.right == kNoVal || normPos.bottom == kNoVal || cmd == kNoVal) { LOGI("Previous window placement not found."); return; } POINT nopt; nopt.x = nopt.y = -1; // We need to set the placement for "normal", and then deal with // maximized windows by having the MyApp ShowWindow() call pass the // appropriate command. If we pass the correct normal rect here with // the "maximized" command, we end up with a non-maximized window of // maximum size. (If done correctly, you can maximize, quit, restart, // and un-maximize back to the original size.) WINDOWPLACEMENT wndpl; wndpl.length = sizeof(wndpl); wndpl.flags = 0; wndpl.showCmd = SW_SHOWNORMAL; wndpl.ptMinPosition = nopt; wndpl.ptMaxPosition = nopt; wndpl.rcNormalPosition = normPos; LOGD("Restoring previous placement, cmd=%d", wndpl.showCmd); SetWindowPlacement(&wndpl); // Stomp on MyApp's show command. We don't want to start minimized, // so switch to "normal" if that's set. gMyApp.m_nCmdShow = (cmd == SW_SHOWMINIMIZED ? SW_SHOWNORMAL : cmd); } 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, ®, &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(""); ::MessageBeep(MB_OK); } } void MainWindow::FailureBeep(void) { const Preferences* pPreferences = GET_PREFERENCES(); if (pPreferences->GetPrefBool(kPrBeepOnSuccess)) { LOGI(""); ::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; }