You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
12473 lines
350 KiB
12473 lines
350 KiB
/*++
|
|
|
|
Copyright (c) 1989-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
CompatAdmin.cpp
|
|
|
|
Abstract:
|
|
|
|
This module handles most of the GUI for the application. It contains the message loop and
|
|
WinMain
|
|
|
|
Notes:
|
|
|
|
1. Has a global try catch, so if the Program AVs we will get a "Out of Memory" Error
|
|
2. This is a Unicode app
|
|
3. The code was written with tab size of 4
|
|
4. Please go through the data strctures and their explainations as in CompatAdmin.h
|
|
5. The documentation in the code assumes that you are familiar with the basic understanding of
|
|
the SdbApis and the lay out of the xml that is compiled into the .sdb database format
|
|
6. Tools/stuff you should use or know about:
|
|
a) shimdbc : Shim database compiler. Compiles a xml into database. Owner: markder, vadimb
|
|
b) dumpsdb : Used to view a .sdb as a text file. Owner: dmunsil
|
|
c) sdbapis : All our sdb apis. Owner: dmunsil
|
|
d) shimengine : The shim infrastructure core component. Owner: clupu
|
|
|
|
Usage:
|
|
|
|
CompatAdmin.exe [/x]. The /x switch is for enabling expert mode. In expert mode
|
|
we show the non-general shims as well and the parameters for shims and flags can be
|
|
configured
|
|
|
|
Author:
|
|
|
|
kinshu created July 2, 2001
|
|
|
|
Revision History:
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#include <richedit.h>
|
|
|
|
//////////////////////// Extern variables /////////////////////////////////////
|
|
|
|
extern DATABASE GlobalDataBase;
|
|
extern PDATABASE g_pPresentDataBase;
|
|
extern BOOL g_bEntryConflictDonotShow;
|
|
extern HWND g_hdlgSearchDB;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
//////////////////////// Defines //////////////////////////////////////////////
|
|
|
|
#define STR_NEW_LINE_CHARS TEXT("\r\n")
|
|
#define STR_APPPATCH_CUSTOM TEXT("AppPatch\\Custom\\")
|
|
|
|
// Buffer allocation size in TCHARS for the common dialog that lets us open multiple files
|
|
#define MAX_BUFF_OPENMULTIPLE_FILES MAX_PATH * 10
|
|
|
|
// Width and height of the buttons in the toolbar
|
|
#define IMAGE_WIDTH 24
|
|
#define IMAGE_HEIGHT 24
|
|
|
|
// Number of buttons in the toolbar. This includes the separators as well.
|
|
#define BUTTON_COUNT 11
|
|
|
|
//
|
|
// Defines for the context menus (position)
|
|
#define MENU_CONTEXT_DATABASE 0
|
|
#define MENU_CONTEXT_APP_LAYER 1
|
|
#define MENU_CONTEXT_FIX 2
|
|
#define MENU_CONTEXT_LIST 4
|
|
#define MENU_CONTEXT_DATABASE_ALL 5
|
|
|
|
// defines for the colums used in the event dialog
|
|
#define EVENTS_COLUMN_TIME 0
|
|
#define EVENTS_COLUMN_MSG 1
|
|
|
|
//
|
|
// Number of controls in the main window which have to be resized. This is passed as a
|
|
// paramreter to BeginDeferWindowPos()
|
|
#define MAIN_WINDOW_CONTROL_COUNT 6
|
|
|
|
// We need to cleanup if we are checking for leaks
|
|
#define HELP_BOUND_CHECK 1
|
|
|
|
// Maximum number of chars that we show for the file name in MRU. Does not include the extension
|
|
#define MAX_FILE_SHOW 20
|
|
|
|
// Maximum number of chars that we show for the drive and dir of the path in MRU
|
|
#define MAX_DIR_SHOW 20
|
|
|
|
// Maximum length of a string that can be shown as the File menu MRU.
|
|
#define MAX_LENGTH_MRU_MENUITEM (MAX_FILE_SHOW + MAX_DIR_SHOW + 4) // 4 is for the extension .sdb
|
|
|
|
// The redraw type passed to DeferWindowPos() in OnMoveSplitter()
|
|
#define REDRAW_TYPE SWP_NOZORDER | SWP_NOACTIVATE
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
//////////////////////// Global Variables /////////////////////////////////////
|
|
|
|
//
|
|
// We are going to delete the contents of the entry tree by callign TreeDeleteAll().
|
|
// We need to handle this because we should not be processing any TVN_SELTCHANGE
|
|
// messages when we are deleting the entry tree. If we have by mistake delete the contents
|
|
// of the LPARAM of a entry tree and then we delete the entry tree, that item can get the
|
|
// focus and then we will try to make use of the LPARAM of the tree item and this can cause
|
|
// an access violation as the item has been deleted already. As a general rule, delete
|
|
// the tree item first and then delete the data structure that is referenced by some
|
|
// pointer that is in the LPARAM for the tree item
|
|
//
|
|
BOOL g_bDeletingEntryTree;
|
|
|
|
//
|
|
// If we have some wizard window open then we do not want that we should be able
|
|
// to change the present database by double clicking the search or query results
|
|
BOOL g_bSomeWizardActive;
|
|
|
|
// The clipboad
|
|
CLIPBOARD gClipBoard;
|
|
|
|
//
|
|
// We do not want the installed database list to be updated when we are installing a
|
|
// database in the process of test run
|
|
BOOL g_bUpdateInstalledRequired = TRUE;
|
|
|
|
// The index of the item that is presently selected in the Contents List
|
|
INT g_iContentsListSelectIndex = -1;
|
|
|
|
//
|
|
// This will contain the function pointer of the original tree view proc. We are subclassing
|
|
// both of the tree views
|
|
WNDPROC g_PrevTreeProc = NULL;
|
|
|
|
//
|
|
// This will contain the function pointer of the original list view proc. We are subclassing
|
|
// the list views
|
|
WNDPROC g_PrevListProc = NULL;
|
|
|
|
//
|
|
// The array of the keys on which we will be listening for changes. Used for automatic update of
|
|
// the per-user compatibility list and the installed databases list
|
|
HKEY g_hKeyNotify[2];
|
|
|
|
// Event Handles to wait for the Per User and All Users settings to change
|
|
HANDLE g_arrhEventNotify[2];
|
|
|
|
// Handle to the toolbar
|
|
HWND g_hwndToolBar;
|
|
|
|
// Stringlist that holds the most recently used files.
|
|
CSTRINGLIST g_strlMRU;
|
|
|
|
// The name of the application
|
|
TCHAR g_szAppName[128];
|
|
|
|
// Misc. data to be passed to dialogs as arg (when we are already using the LPARAM)
|
|
TCHAR g_szData[1024];
|
|
|
|
//The new URL should now point to the location from where we can get the SP3
|
|
TCHAR g_szW2KUrl[] = TEXT("http://www.microsoft.com/windows2000/downloads/tools/appcompat/");
|
|
|
|
// URL for the toolkit. Shown in the description window when we do not have any other description
|
|
// Bonus !
|
|
TCHAR g_szToolkitURL[] = _T("http://msdn.microsoft.com/compatibility");
|
|
|
|
// Is service pack greater than 2
|
|
BOOL g_fSPGreaterThan2;
|
|
|
|
// The accelerator handle
|
|
HACCEL g_hAccelerator;
|
|
|
|
// Are we on Win2000
|
|
BOOL g_bWin2K = FALSE;
|
|
|
|
//Specifies whether the contents of ClipBoard are because of cut or copy.
|
|
BOOL g_bIsCut = FALSE;
|
|
|
|
// The module handle
|
|
HINSTANCE g_hInstance;
|
|
|
|
// The handle to the main dialog window
|
|
HWND g_hDlg;
|
|
|
|
// Handle to the window of the entry tree, displayed in the contents pane
|
|
HWND g_hwndEntryTree;
|
|
|
|
// Handle to the window of the contents list, displayed in the contemts pane
|
|
HWND g_hwndContentsList;
|
|
|
|
// Handle to the status bar
|
|
HWND g_hwndStatus;
|
|
|
|
// Handle to In-place edit control for the DB tree and the contents list
|
|
HWND g_hwndEditText;
|
|
|
|
//
|
|
// Handle to the image list. This image list is used by all except the matching wizard page,
|
|
// and the toolbar
|
|
HIMAGELIST g_hImageList;
|
|
|
|
// BUGBUG: Bad stuff, use a map instead
|
|
UINT g_uImageRedirector[1024];
|
|
|
|
// EXE selected in the Entry Tree
|
|
PDBENTRY g_pSelEntry;
|
|
|
|
// First EXE of the Selected App in the DataBase Tree
|
|
PDBENTRY g_pEntrySelApp;
|
|
|
|
// Spefies if the contents list is visible, FALSE implies the entry tree is visible
|
|
BOOL g_bIsContentListVisible;
|
|
|
|
//
|
|
// The width, height of the main dialog window. Used when we handle WM_SIZE
|
|
int g_cWidth;
|
|
int g_cHeight;
|
|
|
|
// The X position where the mouse was last pressed.
|
|
int g_iMousePressedX;
|
|
|
|
// If the mouse is pressed: used when we handle the split bar
|
|
BOOL g_bDrag;
|
|
|
|
// Whether the system apps, tree item has been expanded.
|
|
BOOL g_bMainAppExpanded = FALSE;
|
|
|
|
// Used for giving default names to the .SDB files in the DataBase constructor
|
|
UINT g_uNextDataBaseIndex = 1;
|
|
|
|
// Used for painting the split bar.
|
|
RECT g_rectBar;
|
|
|
|
// The db tree that constitutes the root-pane. This is the LHS tree control
|
|
DatabaseTree DBTree;
|
|
|
|
//
|
|
// Used to tell if the description window is shown. Is true by default. User can make it
|
|
// false using menu
|
|
BOOL g_bDescWndOn = TRUE;
|
|
|
|
// The list control for the events dialog
|
|
HWND g_hwndEventsWnd;
|
|
|
|
// The event count. This is used as the index into the event list view.
|
|
INT g_iEventCount;
|
|
|
|
// Buffer used to update the content of the richedit description window
|
|
TCHAR g_szDesc[1024];
|
|
|
|
// Handle to rich edit control
|
|
HWND g_hwndRichEdit;
|
|
|
|
//
|
|
// The expert mode. Is the /x switch on? In expert mode the user can see all the shims and flags
|
|
// and can change the paramters of the shims
|
|
BOOL g_bExpert;
|
|
|
|
// Does the user have admin rights
|
|
BOOL g_bAdmin = TRUE;
|
|
|
|
// The handle to the thread that handles the updates to the installed databases
|
|
// and the per-user settings
|
|
HANDLE g_hThreadWait;
|
|
|
|
// Help cookie that is returned while initializing and is used when uninitalizing
|
|
DWORD g_dwCookie = 0;
|
|
|
|
//
|
|
// The path of CompatAdmin. This is required so that we can load the help file appropriately.
|
|
// Buffer size is made MAX_PATH + 1, becasue GetModuleFileName does not NULL terminate.
|
|
TCHAR g_szAppPath[MAX_PATH + 1];
|
|
|
|
// The critical section that controls which call to ShowMainEntries(); gets through.
|
|
CRITICAL_SECTION g_critsectShowMain;
|
|
|
|
// The critical section that guards the variable that tells us if somebody is already trying to
|
|
// populate the global app list.
|
|
CRITICAL_SECTION s_csExpanding;
|
|
|
|
//
|
|
// The critical section that protects the installed database datastructure.
|
|
// The installed datastructure is iterated when we are querying the datastructure and
|
|
// the query is done in a separate thread
|
|
CRITICAL_SECTION g_csInstalledList;
|
|
|
|
// Is somebody trying to populate the main database entries
|
|
BOOL g_bExpanding = FALSE;
|
|
|
|
// The presently selected database
|
|
PDATABASE g_pPresentDataBase;
|
|
|
|
// Height and width of the event window
|
|
static int s_cEventWidth;
|
|
static int s_cEventHeight;
|
|
|
|
// The imagelist for the toolbar
|
|
static HIMAGELIST s_hImageListToolBar;
|
|
|
|
// The hot imagelist for the toolbar
|
|
static HIMAGELIST s_hImageListToolBarHot;
|
|
|
|
// If we are about to exit CompatAdmin. It will surely exit now.
|
|
static BOOL s_bProcessExiting;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////// Function Declarations ////////////////////////////////
|
|
|
|
INT
|
|
GetContentsListIndex(
|
|
HWND hwndList,
|
|
LPARAM lParam
|
|
);
|
|
void
|
|
HandleMRUActivation(
|
|
WPARAM wCode
|
|
);
|
|
|
|
BOOL
|
|
EndListViewLabelEdit(
|
|
LPARAM lparam
|
|
);
|
|
|
|
void
|
|
OnEntryTreeSelChange(
|
|
LPARAM lParam
|
|
);
|
|
|
|
void
|
|
ShowExcludeStatusMessage(
|
|
HWND hwndTree,
|
|
HTREEITEM hItem
|
|
);
|
|
|
|
void
|
|
ShowIncludeStatusMessage(
|
|
HWND hwndTree,
|
|
HTREEITEM hItem
|
|
);
|
|
|
|
void
|
|
ShowEventsWindow(
|
|
void
|
|
);
|
|
|
|
void
|
|
SetStatusDBItems(
|
|
IN HTREEITEM hItem
|
|
);
|
|
|
|
void
|
|
OnExitCleanup(
|
|
void
|
|
);
|
|
|
|
void
|
|
EventsWindowSize(
|
|
HWND hDlf
|
|
);
|
|
|
|
void
|
|
ShowHelp(
|
|
HWND hdlg,
|
|
WPARAM wCode
|
|
);
|
|
|
|
INT_PTR
|
|
CALLBACK
|
|
EventDlgProc(
|
|
HWND hdlg,
|
|
UINT uMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
);
|
|
|
|
INT
|
|
ShowMainEntries(
|
|
HWND hdlg
|
|
);
|
|
|
|
BOOL
|
|
HandleDBTreeSelChange(
|
|
HTREEITEM hItem
|
|
);
|
|
|
|
void
|
|
CopyToClipBoard(
|
|
WPARAM wCode
|
|
);
|
|
|
|
void
|
|
LuapCleanup(
|
|
void
|
|
);
|
|
|
|
BOOL
|
|
LoadInstalledDataBases(
|
|
void
|
|
);
|
|
|
|
INT
|
|
LoadSpecificInstalledDatabaseGuid(
|
|
PCTSTR pszGuid
|
|
);
|
|
|
|
void
|
|
ContextMenuExeTree(
|
|
LPARAM lParam
|
|
);
|
|
|
|
void
|
|
SetTBButtonStatus(
|
|
HWND hwndTB
|
|
);
|
|
|
|
void
|
|
SetTBButtonStatus(
|
|
HWND hwndTB,
|
|
HWND hwndControl
|
|
);
|
|
|
|
void
|
|
DrawSplitter(
|
|
HWND hdlg
|
|
);
|
|
|
|
BOOL
|
|
AddRegistryKeys(
|
|
void
|
|
);
|
|
|
|
void
|
|
ShowToolBarToolTips(
|
|
HWND hdlg,
|
|
LPARAM lParam
|
|
);
|
|
|
|
INT_PTR
|
|
CALLBACK
|
|
QueryDBDlg(
|
|
HWND hdlg,
|
|
UINT uMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
);
|
|
|
|
DWORD
|
|
WINAPI
|
|
ThreadEventKeyNotify(
|
|
PVOID pVoid
|
|
);
|
|
|
|
void
|
|
PasteFromClipBoard(
|
|
void
|
|
);
|
|
|
|
void
|
|
AddMRUToFileMenu(
|
|
HMENU hmenuFile
|
|
);
|
|
|
|
LRESULT
|
|
CALLBACK
|
|
ListViewProc(
|
|
HWND hWnd,
|
|
UINT uMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
);
|
|
|
|
LRESULT
|
|
CALLBACK
|
|
TreeViewProc(
|
|
HWND hWnd,
|
|
UINT uMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
);
|
|
|
|
void
|
|
LoadDisplaySettings(
|
|
void
|
|
);
|
|
|
|
void
|
|
OnMoveSplitter(
|
|
HWND hwnd,
|
|
LPARAM lParam,
|
|
BOOL bDoTheDrag,
|
|
INT iDiff
|
|
);
|
|
|
|
INT_PTR
|
|
CALLBACK
|
|
MsgBoxDlgProc(
|
|
HWND hdlg,
|
|
UINT uMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
);
|
|
|
|
BOOL
|
|
CheckProperSP(
|
|
void
|
|
);
|
|
|
|
void
|
|
LoadPerUserSettings(
|
|
void
|
|
);
|
|
|
|
void
|
|
PopulateContentsList(
|
|
HTREEITEM hItem
|
|
);
|
|
|
|
void
|
|
CreateNewAppHelp(
|
|
void
|
|
);
|
|
|
|
void
|
|
ModifyAppHelp(
|
|
void
|
|
);
|
|
|
|
void
|
|
CreateNewAppFix(
|
|
void
|
|
);
|
|
|
|
void
|
|
ModifyAppFix(
|
|
void
|
|
);
|
|
|
|
void
|
|
CreateNewLayer(
|
|
void
|
|
);
|
|
|
|
void
|
|
OnDelete(
|
|
);
|
|
void
|
|
CreateNewDatabase(
|
|
void
|
|
);
|
|
|
|
void
|
|
DatabaseSaveAll(
|
|
void
|
|
);
|
|
|
|
void
|
|
OnDatabaseClose(
|
|
void
|
|
);
|
|
|
|
void
|
|
ChangeEnableStatus(
|
|
void
|
|
);
|
|
|
|
BOOL
|
|
ModifyLayer(
|
|
void
|
|
);
|
|
|
|
void
|
|
OnRename(
|
|
void
|
|
);
|
|
|
|
void
|
|
ProcessSwitches(
|
|
void
|
|
);
|
|
|
|
INT_PTR
|
|
CALLBACK
|
|
ShowDBPropertiesDlgProc(
|
|
HWND hdlg,
|
|
UINT uMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
);
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
HandlePopUp(
|
|
IN HWND hDlg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
HandlePopUp
|
|
|
|
Desc: Handles pop down menu (WM_INITMENUPOPUP). Disables items as necessary
|
|
|
|
Params:
|
|
IN HWND hDlg: The window that received WM_INITMENUPOPUP
|
|
IN WPARAM wParam: As with WM_INITMENUPOPUP
|
|
IN LPARAM lParam: As with WM_INITMENUPOPUP
|
|
|
|
Notes: The tool bar buttons are NOT diasbled/enabled in this routine. For that the function
|
|
SetTBButtonStatus() is used
|
|
--*/
|
|
{
|
|
HWND hwndFocus = GetFocus();
|
|
PDATABASE pCurrentSelectedDB = GetCurrentDB();
|
|
|
|
//
|
|
// SelectAll, Invert Selection
|
|
//
|
|
int iEnable = MF_GRAYED;
|
|
HMENU hMenu = (HMENU)wParam;
|
|
|
|
if (g_bIsContentListVisible
|
|
&& ListView_GetItemCount(g_hwndContentsList) > 0
|
|
&& hwndFocus == g_hwndContentsList) {
|
|
|
|
iEnable = MF_ENABLED;
|
|
}
|
|
|
|
EnableMenuItem(hMenu, ID_EDIT_SELECTALL, iEnable);
|
|
EnableMenuItem(hMenu, ID_EDIT_INVERTSELECTION, iEnable);
|
|
|
|
//
|
|
// Change Status menu item
|
|
//
|
|
HTREEITEM hItem = TreeView_GetSelection(DBTree.m_hLibraryTree);
|
|
TYPE typeSelectedItemDB = (TYPE)GetItemType(DBTree.m_hLibraryTree, hItem);
|
|
|
|
MENUITEMINFO Info = {0};
|
|
|
|
Info.cbSize = sizeof(MENUITEMINFO);
|
|
Info.fMask = MIIM_STRING;
|
|
|
|
|
|
if (g_pSelEntry && g_pSelEntry->bDisablePerMachine == FALSE) {
|
|
Info.dwTypeData = GetString(IDS_DISABLE);
|
|
} else {
|
|
Info.dwTypeData = GetString(IDS_ENABLE);
|
|
}
|
|
|
|
SetMenuItemInfo(hMenu, ID_FIX_CHANGEENABLESTATUS, MF_BYCOMMAND, &Info);
|
|
|
|
//
|
|
// Set the text for edit/add apphelp
|
|
//
|
|
if (g_pSelEntry && g_pSelEntry->appHelp.bPresent) {
|
|
Info.dwTypeData = GetString(IDS_EDITAPPHELP);
|
|
} else {
|
|
Info.dwTypeData = GetString(IDS_CREATEAPPHELP);
|
|
}
|
|
|
|
SetMenuItemInfo(hMenu, ID_MODIFY_APPHELPMESSAGE, MF_BYCOMMAND, &Info);
|
|
|
|
//
|
|
// Set the text for edit/add app fix
|
|
//
|
|
if (g_pSelEntry &&
|
|
(g_pSelEntry->pFirstFlag
|
|
|| g_pSelEntry->pFirstLayer
|
|
|| g_pSelEntry->pFirstPatch
|
|
|| g_pSelEntry->pFirstShim)) {
|
|
|
|
Info.dwTypeData = GetString(IDS_EDITFIX);
|
|
|
|
} else {
|
|
Info.dwTypeData = GetString(IDS_CREATEFIX);
|
|
}
|
|
|
|
SetMenuItemInfo(hMenu, ID_MODIFY_APPLICATIONFIX, MF_BYCOMMAND, &Info);
|
|
|
|
//
|
|
// Set the text and id for install/uninstall menu item
|
|
//
|
|
Info.fMask = MIIM_STRING;
|
|
|
|
if (pCurrentSelectedDB && pCurrentSelectedDB->type != DATABASE_TYPE_WORKING) {
|
|
Info.dwTypeData = GetString(IDS_MENU_UINSTALL);
|
|
} else {
|
|
Info.dwTypeData = GetString(IDS_MENU_INSTALL);
|
|
}
|
|
|
|
SetMenuItemInfo(hMenu, ID_DATABASE_INSTALL_UNINSTALL, MF_BYCOMMAND, &Info);
|
|
|
|
BOOL bReadOnly = (pCurrentSelectedDB && pCurrentSelectedDB->type != DATABASE_TYPE_WORKING);
|
|
|
|
if (pCurrentSelectedDB == NULL) {
|
|
bReadOnly = TRUE;
|
|
}
|
|
|
|
//
|
|
// Close
|
|
//
|
|
iEnable = (pCurrentSelectedDB && pCurrentSelectedDB->type == DATABASE_TYPE_WORKING) ? MF_ENABLED : MF_GRAYED ;
|
|
EnableMenuItem(hMenu, ID_DATABASE_CLOSE, iEnable);
|
|
|
|
//
|
|
// Disable the items for the global and Installed databases
|
|
//
|
|
iEnable = (bReadOnly) ? MF_GRAYED : MF_ENABLED;
|
|
|
|
EnableMenuItem(hMenu, ID_FILE_SAVE, iEnable);
|
|
EnableMenuItem(hMenu, ID_FILE_SAVEAS, iEnable);
|
|
|
|
EnableMenuItem(hMenu, ID_FIX_CREATEANAPPLICATIONFIX, iEnable);
|
|
EnableMenuItem(hMenu, ID_FIX_CREATENEWLAYER, iEnable);
|
|
|
|
//
|
|
// AppHelp mechanism is not supported in win2k
|
|
//
|
|
EnableMenuItem(hMenu,
|
|
ID_FIX_CREATEANEWAPPHELPMESSAGE,
|
|
(g_bWin2K) ? MF_GRAYED : iEnable);
|
|
|
|
//
|
|
// Save all and close all
|
|
//
|
|
if (!bReadOnly || typeSelectedItemDB == TYPE_GUI_DATABASE_WORKING_ALL) {
|
|
iEnable = MF_ENABLED;
|
|
} else {
|
|
iEnable = MF_GRAYED;
|
|
}
|
|
|
|
EnableMenuItem(hMenu, ID_DATABASE_SAVEALL, iEnable);
|
|
EnableMenuItem(hMenu, ID_DATABASE_CLOSEALL, iEnable);
|
|
|
|
//
|
|
// Paste menu item
|
|
//
|
|
if (bReadOnly) {
|
|
EnableMenuItem(hMenu, ID_EDIT_PASTE, MF_GRAYED);
|
|
} else {
|
|
|
|
int iEnablePaste = (gClipBoard.pClipBoardHead) ? MF_ENABLED : MF_GRAYED;
|
|
|
|
if (iEnablePaste == MF_ENABLED) {
|
|
//
|
|
// Check if the item that we have in the clipboard can be pasted in the item that we have got selected
|
|
//
|
|
if (gClipBoard.type == FIX_SHIM || gClipBoard.type == FIX_FLAG) {
|
|
|
|
//
|
|
// The focus should be on the db tree and a layer should be selected
|
|
// in the db tree or the focus should be on the contents list and then
|
|
// the root of all layes should be selected in the dbtree or a layer should
|
|
// be selected.
|
|
//
|
|
if (hwndFocus == DBTree.m_hLibraryTree
|
|
&& typeSelectedItemDB == FIX_LAYER) {
|
|
|
|
iEnablePaste = MF_ENABLED;
|
|
|
|
} else if (hwndFocus == g_hwndContentsList
|
|
&& (typeSelectedItemDB == FIX_LAYER
|
|
|| typeSelectedItemDB == TYPE_GUI_LAYERS)) {
|
|
//
|
|
// We have focus on the contents list, we can paste shims if a layer is selected
|
|
// or the root of all layers is selected in the db tree
|
|
//
|
|
iEnablePaste = MF_ENABLED;
|
|
|
|
} else {
|
|
iEnablePaste = MF_GRAYED;
|
|
}
|
|
|
|
} else if (gClipBoard.type == FIX_LAYER || gClipBoard.type == TYPE_GUI_LAYERS) {
|
|
//
|
|
// In the db tree we should have a database or the all layers item selected
|
|
//
|
|
if (typeSelectedItemDB == TYPE_GUI_LAYERS || typeSelectedItemDB == DATABASE_TYPE_WORKING) {
|
|
iEnablePaste = MF_ENABLED;
|
|
} else {
|
|
iEnablePaste = MF_GRAYED;
|
|
}
|
|
} else if (gClipBoard.type == TYPE_ENTRY) {
|
|
//
|
|
// If we have copied an entry from the entry tree, in the db tree the focus can be
|
|
// on a database, an application, or all application node
|
|
//
|
|
if (typeSelectedItemDB == DATABASE_TYPE_WORKING
|
|
|| (typeSelectedItemDB == TYPE_ENTRY && gClipBoard.SourceType == ENTRY_TREE)
|
|
|| typeSelectedItemDB == TYPE_GUI_APPS) {
|
|
|
|
iEnablePaste = MF_ENABLED;
|
|
} else {
|
|
iEnablePaste = MF_GRAYED;
|
|
}
|
|
}
|
|
}
|
|
|
|
EnableMenuItem(hMenu, ID_EDIT_PASTE, iEnablePaste);
|
|
}
|
|
|
|
BOOL bEnableCopy = FALSE, bEnableModify = FALSE;
|
|
HWND hwndGetFocus = GetFocus();
|
|
HTREEITEM hItemSelected = NULL;
|
|
TYPE type = TYPE_UNKNOWN;
|
|
|
|
//
|
|
// First get the type of the selected tree item and the corresponding type
|
|
//
|
|
if (hwndGetFocus == DBTree.m_hLibraryTree || hwndFocus == g_hwndEntryTree) {
|
|
//
|
|
// For the db tree or the entry tree
|
|
//
|
|
hItemSelected = TreeView_GetSelection(hwndGetFocus);
|
|
type = (TYPE)GetItemType(hwndGetFocus, hItemSelected);
|
|
|
|
} else {
|
|
//
|
|
// For the contents list, the tree item is the item that is selected in the db tree
|
|
//
|
|
hItemSelected = TreeView_GetSelection(DBTree.m_hLibraryTree);
|
|
type = (TYPE)GetItemType(DBTree.m_hLibraryTree, hItemSelected);
|
|
}
|
|
|
|
//
|
|
// Copy will be enabled only if the presently selected item is copy-able
|
|
//
|
|
if (hwndGetFocus == DBTree.m_hLibraryTree) {
|
|
//
|
|
// For the db tree
|
|
//
|
|
if (hItemSelected) {
|
|
|
|
if (type == TYPE_ENTRY
|
|
|| type == FIX_LAYER
|
|
|| type == FIX_SHIM
|
|
|| type == FIX_FLAG
|
|
|| type == TYPE_GUI_APPS
|
|
|| type == TYPE_GUI_LAYERS) {
|
|
|
|
bEnableCopy = TRUE;
|
|
}
|
|
}
|
|
|
|
} else if (hwndGetFocus == g_hwndEntryTree) {
|
|
//
|
|
// For the entry tree
|
|
//
|
|
if (hItemSelected) {
|
|
|
|
if (type == TYPE_ENTRY) {
|
|
bEnableCopy = TRUE;
|
|
}
|
|
}
|
|
|
|
} else if (hwndFocus == g_hwndContentsList) {
|
|
//
|
|
// For the contents list
|
|
//
|
|
if (type == TYPE_GUI_APPS
|
|
|| type == TYPE_GUI_LAYERS
|
|
|| type == FIX_LAYER
|
|
|| type == TYPE_GUI_SHIMS) {
|
|
//
|
|
// Make sure atleast one is selected
|
|
//
|
|
if (ListView_GetSelectedCount(g_hwndContentsList) > 0) {
|
|
bEnableCopy = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
iEnable = (bEnableCopy && pCurrentSelectedDB) ? MF_ENABLED : MF_GRAYED;
|
|
EnableMenuItem(hMenu, ID_EDIT_COPY, iEnable);
|
|
|
|
iEnable = (bReadOnly) ? MF_GRAYED : iEnable;
|
|
|
|
//
|
|
// Cut Menu
|
|
//
|
|
if (bReadOnly) {
|
|
iEnable = MF_GRAYED;
|
|
}
|
|
|
|
if (hwndFocus == DBTree.m_hLibraryTree) {
|
|
|
|
if (type == TYPE_GUI_SHIMS || type == FIX_SHIM || type == FIX_FLAG) {
|
|
iEnable = MF_GRAYED;
|
|
}
|
|
} else if (hwndFocus == g_hwndContentsList) {
|
|
//
|
|
// Cut is not available for the shims
|
|
//
|
|
if (type == TYPE_GUI_SHIMS || type == FIX_LAYER) {
|
|
iEnable = MF_GRAYED;
|
|
}
|
|
}
|
|
|
|
EnableMenuItem(hMenu, ID_EDIT_CUT, iEnable);
|
|
|
|
//
|
|
// Delete Menu
|
|
//
|
|
if (hwndFocus == g_hwndEntryTree) {
|
|
//
|
|
// For the entry tree, If the db is not readonly, everything except the commandline for the shim and the
|
|
// in-exclude items are prone to deletion
|
|
//
|
|
if (bReadOnly
|
|
|| type == TYPE_GUI_COMMANDLINE
|
|
|| type == TYPE_GUI_EXCLUDE
|
|
|| type == TYPE_GUI_INCLUDE
|
|
|| g_pSelEntry == NULL) {
|
|
|
|
EnableMenuItem(hMenu, ID_EDIT_DELETE, MF_GRAYED);
|
|
|
|
} else {
|
|
EnableMenuItem(hMenu, ID_EDIT_DELETE, MF_ENABLED);
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// If we are not on the entry tree, then what can be cut can be deleted as well
|
|
//
|
|
EnableMenuItem(hMenu, ID_EDIT_DELETE, iEnable);
|
|
}
|
|
|
|
EnableMenuItem(hMenu, ID_MODIFY_APPLICATIONFIX, MF_GRAYED);
|
|
EnableMenuItem(hMenu, ID_MODIFY_APPHELPMESSAGE, MF_GRAYED);
|
|
EnableMenuItem(hMenu, ID_MODIFY_COMPATIBILITYMODE, MF_GRAYED);
|
|
|
|
//
|
|
// Modify Menu
|
|
//
|
|
if (!bReadOnly && hwndGetFocus == g_hwndEntryTree && type == TYPE_ENTRY) {
|
|
|
|
EnableMenuItem(hMenu, ID_MODIFY_APPLICATIONFIX, MF_ENABLED);
|
|
|
|
//
|
|
// AppHelp mechanism is not supported in win2k
|
|
//
|
|
EnableMenuItem(hMenu,
|
|
ID_MODIFY_APPHELPMESSAGE,
|
|
(g_bWin2K) ? MF_GRAYED : MF_ENABLED);
|
|
}
|
|
|
|
if (!bReadOnly && hwndGetFocus == DBTree.m_hLibraryTree && type == FIX_LAYER) {
|
|
EnableMenuItem(hMenu, ID_MODIFY_COMPATIBILITYMODE, MF_ENABLED);
|
|
}
|
|
|
|
//
|
|
// Install / Un-install menu should be enabled iff we are not on the system db and g_pPresentDatabase is valid
|
|
//
|
|
iEnable = (pCurrentSelectedDB && (pCurrentSelectedDB->type != DATABASE_TYPE_GLOBAL)) ? MF_ENABLED : MF_GRAYED;
|
|
EnableMenuItem(hMenu, ID_DATABASE_INSTALL_UNINSTALL, iEnable);
|
|
|
|
//
|
|
// If no entry has been selected
|
|
//
|
|
iEnable = (g_pSelEntry == NULL) ? MF_GRAYED : MF_ENABLED;
|
|
|
|
EnableMenuItem(hMenu, ID_FIX_CHANGEENABLESTATUS, iEnable);
|
|
EnableMenuItem(hMenu, ID_FIX_EXECUTEAPPLICATION, iEnable);
|
|
|
|
//
|
|
// Rename
|
|
//
|
|
iEnable = MF_GRAYED;
|
|
|
|
if (!bReadOnly && (hwndGetFocus == DBTree.m_hLibraryTree)) {
|
|
|
|
if (type == TYPE_ENTRY || type == FIX_LAYER || type == DATABASE_TYPE_WORKING) {
|
|
iEnable = MF_ENABLED;
|
|
}
|
|
|
|
} else if (!bReadOnly && (hwndGetFocus == g_hwndContentsList)) {
|
|
|
|
if (type == TYPE_GUI_APPS
|
|
|| type == TYPE_GUI_LAYERS) {
|
|
|
|
iEnable = MF_ENABLED;
|
|
}
|
|
}
|
|
|
|
EnableMenuItem(hMenu, ID_EDIT_RENAME, iEnable);
|
|
|
|
//
|
|
// Configure LUA. Can be true when on entry-fix only
|
|
//
|
|
iEnable = !bReadOnly
|
|
&& (hwndFocus == g_hwndEntryTree)
|
|
&& (type == TYPE_ENTRY)
|
|
&& IsLUARedirectFSPresent(g_pSelEntry) ? MF_ENABLED : MF_GRAYED;
|
|
|
|
EnableMenuItem(hMenu, ID_EDIT_CONFIGURELUA, iEnable);
|
|
|
|
//
|
|
// The db properties. We do not want this to be enabled, if we are on the
|
|
// system database and somebody else is trying to load the exe entries of the
|
|
// system datbase.
|
|
// The reason is that for showing the properties we will
|
|
// have to load the system database exe entries and we do not want 2 threads to load
|
|
// the system database entries
|
|
//
|
|
(pCurrentSelectedDB && !(pCurrentSelectedDB->type == DATABASE_TYPE_GLOBAL && g_bExpanding)) ?
|
|
(iEnable) = MF_ENABLED : MF_GRAYED;
|
|
|
|
EnableMenuItem(hMenu, ID_FILE_PROPERTIES, iEnable);
|
|
}
|
|
|
|
void
|
|
DoInstallUnInstall(
|
|
void
|
|
)
|
|
/*++
|
|
DoInstallUninstall
|
|
|
|
Desc: Will install or uinstall the presently selected database, depending
|
|
upon if the present database is a workign database or an installed
|
|
database
|
|
|
|
Params:
|
|
void
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
|
|
BOOL bReturn = FALSE;
|
|
PDATABASE pDatabaseTemp = NULL;
|
|
PDATABASE pPresentDatabase = GetCurrentDB();
|
|
|
|
if (pPresentDatabase == NULL) {
|
|
Dbg(dlError, "[DoInstallUnInstall], pPresentDatabase is NULL %d ");
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Non admins cannot do a install-uintsall because we need to invoke
|
|
// sdbinst.exe and sdbinst.exe cannot be invoked if we
|
|
// do not have admin rights
|
|
//
|
|
if (g_bAdmin == FALSE) {
|
|
|
|
MessageBox(g_hDlg,
|
|
GetString(IDS_ERRORNOTADMIN),
|
|
g_szAppName,
|
|
MB_ICONINFORMATION);
|
|
return;
|
|
}
|
|
|
|
if (pPresentDatabase->type == DATABASE_TYPE_INSTALLED) {
|
|
|
|
//
|
|
// This will uninstall the database
|
|
//
|
|
bReturn = InstallDatabase(CSTRING(pPresentDatabase->strPath),
|
|
TEXT("-u -q"),
|
|
TRUE);
|
|
|
|
if (bReturn) {
|
|
|
|
pDatabaseTemp = InstalledDataBaseList.FindDB(pPresentDatabase);
|
|
|
|
if (pDatabaseTemp) {
|
|
DBTree.RemoveDataBase(pDatabaseTemp->hItemDB, DATABASE_TYPE_INSTALLED, FALSE);
|
|
InstalledDataBaseList.Remove(pDatabaseTemp);
|
|
} else {
|
|
assert(FALSE);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Check if we have the complete path of the database, that is this has at least been saved once earlier
|
|
// Also check if this is presently dirty, we prompt the user to save the database
|
|
//
|
|
if (NotCompletePath(pPresentDatabase->strPath) ||
|
|
pPresentDatabase->bChanged) {
|
|
|
|
MessageBox(g_hDlg,
|
|
GetString(IDS_NOTSAVEDBEFOREINSTALL),
|
|
g_szAppName,
|
|
MB_ICONINFORMATION);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Install the database
|
|
//
|
|
bReturn = InstallDatabase(CSTRING(pPresentDatabase->strPath),
|
|
TEXT("-q"),
|
|
TRUE);
|
|
|
|
if (bReturn == TRUE) {
|
|
//
|
|
// Check if we have this database already in the DB tree view
|
|
//
|
|
pDatabaseTemp = InstalledDataBaseList.FindDBByGuid(pPresentDatabase->szGUID);
|
|
|
|
if (pDatabaseTemp) {
|
|
//
|
|
// Remove the pre-exising database
|
|
//
|
|
DBTree.RemoveDataBase(pDatabaseTemp->hItemDB, DATABASE_TYPE_INSTALLED, FALSE);
|
|
InstalledDataBaseList.Remove(pDatabaseTemp);
|
|
}
|
|
|
|
//
|
|
// Load the newly installed database
|
|
//
|
|
LoadSpecificInstalledDatabaseGuid(pPresentDatabase->szGUID);
|
|
}
|
|
}
|
|
|
|
SetFocus(g_hDlg);
|
|
}
|
|
|
|
BOOL
|
|
SearchGroupForSID(
|
|
IN DWORD dwGroup,
|
|
OUT BOOL* pfIsMember
|
|
)
|
|
/*++
|
|
SearchGroupForSID
|
|
|
|
Desc: Checks if the current user is a part of a group
|
|
|
|
Params:
|
|
IN DWORD dwGroup: Is the user a part of this group
|
|
|
|
OUT BOOL* pfIsMember: Will be true if the user is a member of the specified group
|
|
FALSE otherwise
|
|
|
|
Return:
|
|
TRUE: The value in pfIsMember is valid, the function executed successfully
|
|
FALSE: Otherwise
|
|
--*/
|
|
{
|
|
PSID pSID = NULL;
|
|
SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
|
|
BOOL fRes = TRUE;
|
|
|
|
if (!AllocateAndInitializeSid(&SIDAuth,
|
|
2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
dwGroup,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
&pSID)) {
|
|
|
|
fRes = FALSE;
|
|
}
|
|
|
|
if (!CheckTokenMembership(NULL, pSID, pfIsMember)) {
|
|
fRes = FALSE;
|
|
}
|
|
|
|
if (pSID) {
|
|
FreeSid(pSID);
|
|
pSID = NULL;
|
|
}
|
|
|
|
return fRes;
|
|
}
|
|
|
|
BOOL
|
|
IsAdmin(
|
|
OUT BOOL* pbGuest
|
|
)
|
|
/*++
|
|
|
|
IsAdmin
|
|
|
|
Desc: Checks if the current user has administrative rights
|
|
|
|
Params:
|
|
OUT BOOL* pbGuest: Is this is a guest account
|
|
|
|
Return:
|
|
TRUE: The current user has admin rights
|
|
FALSE: Otherwise
|
|
--*/
|
|
{
|
|
BOOL bIsAdmin = FALSE, bReturn = TRUE;
|
|
|
|
if (pbGuest) {
|
|
*pbGuest = TRUE;
|
|
}
|
|
|
|
bReturn = SearchGroupForSID(DOMAIN_ALIAS_RID_ADMINS, &bIsAdmin);
|
|
|
|
if (pbGuest) {
|
|
bReturn &= SearchGroupForSID(DOMAIN_ALIAS_RID_GUESTS, pbGuest);
|
|
}
|
|
|
|
if (bReturn == FALSE) {
|
|
Dbg(dlError, "[IsAdmin] SearchGroupForSID failed");
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return (bIsAdmin);
|
|
}
|
|
|
|
|
|
void
|
|
SetCaption(
|
|
IN BOOL bIncludeDataBaseName, // (TRUE)
|
|
IN PDATABASE pDataBase, // (NULL)
|
|
IN BOOL bOnlyTreeItem // (FALSE)
|
|
)
|
|
|
|
/*++
|
|
SetCaption
|
|
|
|
Desc: Sets the caption of the main dialog. It may also set the text of the
|
|
database item in the tree if it is needed.
|
|
This will be needed when we have
|
|
a) Changed the name of the database
|
|
b) We have changed the "save" status of the database so the "*" will
|
|
either need to be added or removed.
|
|
|
|
Params:
|
|
IN BOOL bIncludeDataBaseName (TRUE): Should the name of the database be
|
|
included in the caption? This will be false when we have focus on say the
|
|
"Installed databases" or "Working databases" or "Per-User Settings" tree items
|
|
in the database tree (lhs)
|
|
|
|
IN PDATABASE pDataBase (NULL): If this is NULL, we will set caption
|
|
for the present database. Note that in some circumstances like when
|
|
we have the focus on "Installed databases" or "Working databases"
|
|
or "Per-User Settings" tree items in the database tree (lhs),
|
|
g_pPresentDataBase will be NULL.
|
|
|
|
IN BOOL bOnlyTreeItem (FALSE): Do we only want to change the text for
|
|
the database tree item? This will be true when we handle rename for
|
|
the database. This is used when we do a cut and we only want to indicate that
|
|
the database from which we did a cut has changed by changing its tree label.
|
|
At the time of cut our selected database will be the database
|
|
in which we are doing the paste and we do not want to change the window caption
|
|
--*/
|
|
{
|
|
CSTRING strDefaultCaption(IDS_DEFCAPTION);
|
|
|
|
if (pDataBase == NULL) {
|
|
pDataBase = g_pPresentDataBase;
|
|
}
|
|
|
|
if (bIncludeDataBaseName) {
|
|
|
|
CSTRING strCaption;
|
|
CSTRING strDBItemCaption;
|
|
|
|
if (pDataBase && (pDataBase->type == DATABASE_TYPE_WORKING)) {
|
|
|
|
if (pDataBase->bChanged) {
|
|
|
|
strCaption.Sprintf(TEXT("%s - %s [%s] *"),
|
|
strDefaultCaption.pszString,
|
|
pDataBase->strName.pszString,
|
|
pDataBase->strPath.pszString);
|
|
|
|
strDBItemCaption.Sprintf(TEXT("%s [%s] *"),
|
|
pDataBase->strName.pszString,
|
|
pDataBase->strPath.pszString);
|
|
|
|
} else {
|
|
|
|
strCaption.Sprintf(TEXT("%s - %s [%s]"),
|
|
strDefaultCaption.pszString,
|
|
pDataBase->strName.pszString,
|
|
pDataBase->strPath.pszString);
|
|
|
|
strDBItemCaption.Sprintf(TEXT("%s [%s]"),
|
|
pDataBase->strName.pszString,
|
|
pDataBase->strPath.pszString);
|
|
|
|
}
|
|
|
|
//
|
|
// Change the text for the database in the DB Tree
|
|
//
|
|
TVITEM Item;
|
|
|
|
Item.mask = TVIF_TEXT;
|
|
Item.pszText = strDBItemCaption;
|
|
Item.hItem = pDataBase->hItemDB;
|
|
|
|
TreeView_SetItem(DBTree.m_hLibraryTree, &Item);
|
|
|
|
if (bOnlyTreeItem) {
|
|
return;
|
|
}
|
|
|
|
} else if (pDataBase && (pDataBase->type == DATABASE_TYPE_INSTALLED)) {
|
|
|
|
strCaption.Sprintf(TEXT("%s - %s "), strDefaultCaption.pszString,
|
|
GetString(IDS_CAPTION2));
|
|
|
|
strCaption.Strcat(GetString(IDS_READONLY));
|
|
|
|
} else if (pDataBase && (pDataBase->type == DATABASE_TYPE_GLOBAL)) {
|
|
|
|
strCaption.Sprintf(TEXT("%s - %s "), strDefaultCaption.pszString,
|
|
GetString(IDS_CAPTION3));
|
|
|
|
strCaption.Strcat(GetString(IDS_READONLY));
|
|
}
|
|
|
|
SetWindowText(g_hDlg, strCaption);
|
|
|
|
} else {
|
|
//
|
|
// The focus is on one of the items for which the caption of the
|
|
// main dialog should be the name of the app only. e.g. of such items
|
|
// are: The "System Database" item, the "Installed Databases" item etc.
|
|
//
|
|
SetWindowText(g_hDlg, (LPCTSTR)strDefaultCaption);
|
|
}
|
|
}
|
|
|
|
void
|
|
Dbg(
|
|
IN DEBUGLEVEL debugLevel,
|
|
IN LPSTR pszFmt
|
|
...
|
|
)
|
|
/*++
|
|
LogMsg
|
|
|
|
Desc: Debugging spew
|
|
|
|
Params:
|
|
IN DEBUGLEVEL debugLevel: The debugging level. See values in DEBUGLEVEL enum
|
|
IN LPSTR pszFmt : Format string that has to be passed to va_start
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
K_SIZE k_sz = 1024;
|
|
CHAR szMessage[k_sz];
|
|
va_list arglist;
|
|
|
|
va_start(arglist, pszFmt);
|
|
StringCchVPrintfA(szMessage, k_sz, pszFmt, arglist);
|
|
|
|
va_end(arglist);
|
|
|
|
switch (debugLevel) {
|
|
case dlPrint:
|
|
DbgPrint("[MSG ] ");
|
|
break;
|
|
|
|
case dlError:
|
|
DbgPrint("[FAIL] ");
|
|
break;
|
|
|
|
case dlWarning:
|
|
DbgPrint("[WARN] ");
|
|
break;
|
|
|
|
case dlInfo:
|
|
DbgPrint("[INFO] ");
|
|
break;
|
|
|
|
default:
|
|
DbgPrint("[XXXX] ");
|
|
break;
|
|
}
|
|
|
|
DbgPrint(szMessage);
|
|
DbgPrint("\n");
|
|
}
|
|
|
|
|
|
void
|
|
InsertColumnIntoListView(
|
|
IN HWND hWnd,
|
|
IN LPTSTR lpszColumn,
|
|
IN INT iCol,
|
|
IN DWORD widthPercent
|
|
)
|
|
/*++
|
|
|
|
InsertColumnIntoListView
|
|
|
|
Desc: Inserts a new column to the list view
|
|
|
|
Params:
|
|
IN HWND hWnd: Handle to the list view
|
|
IN LPTSTR lpszColumn: The heading of the column to be added
|
|
IN INT iCol: The sub item, first is 0
|
|
IN DWORD widthPercent: The percentage width of this column
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
LVCOLUMN lvc;
|
|
RECT rcClient;
|
|
DWORD width;
|
|
|
|
GetWindowRect(hWnd, &rcClient);
|
|
|
|
width = rcClient.right - rcClient.left - GetSystemMetrics(SM_CXVSCROLL);
|
|
|
|
width = width * widthPercent / 100;
|
|
|
|
lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
|
|
lvc.fmt = LVCFMT_LEFT;
|
|
lvc.iSubItem = iCol;
|
|
lvc.cx = width;
|
|
lvc.pszText = lpszColumn;
|
|
|
|
ListView_InsertColumn(hWnd, iCol, &lvc);
|
|
}
|
|
|
|
UINT
|
|
LookupFileImage(
|
|
IN HIMAGELIST hImageList,
|
|
IN LPCTSTR szFilename,
|
|
IN UINT uDefault,
|
|
IN OUT UINT *puArray,
|
|
IN UINT uArrayCount
|
|
)
|
|
/*++
|
|
|
|
<TODO>: Re-write this code to use a map
|
|
|
|
LookupFileImage
|
|
|
|
Desc: Adds the icon for the file szFilename in imagelist hImageList
|
|
|
|
Params:
|
|
IN HIMAGELIST hImageList: The imagelist in which we want to add the
|
|
icon for the file
|
|
|
|
IN LPCTSTR szFilename: Path of the file
|
|
IN UINT uDefault: Default icon to be loaded if no icon is found
|
|
IN OUT UINT *puArray: The array that stores the mapping between the
|
|
index of the icon in the system imagelist and that in the imagelist specified
|
|
by hImageList. puArray[X] == A means that the image with Info.iIcon is stored
|
|
at index A in the local imagelist hImageList. It is assumed that caller
|
|
will have a puArray, hImageList pair
|
|
|
|
IN UINT uArrayCount: Number of elements that can be stored in
|
|
puArray
|
|
|
|
Return: Index of the image in hImageList
|
|
--*/
|
|
{
|
|
SHFILEINFO Info;
|
|
HIMAGELIST hList;
|
|
UINT uImage = 0;
|
|
INT iPos = 0;
|
|
|
|
ZeroMemory(&Info, sizeof(Info));
|
|
|
|
hList = (HIMAGELIST)SHGetFileInfo(szFilename,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
&Info,
|
|
sizeof(Info),
|
|
SHGFI_ICON | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES);
|
|
|
|
if (hList && Info.hIcon) {
|
|
|
|
if (puArray == NULL || Info.iIcon >= uArrayCount) {
|
|
uImage = ImageList_AddIcon(hImageList, Info.hIcon);
|
|
goto End;
|
|
}
|
|
|
|
if (puArray[Info.iIcon] == 0) {
|
|
|
|
iPos = ImageList_AddIcon(hImageList, Info.hIcon);
|
|
|
|
if (iPos != -1) {
|
|
puArray[Info.iIcon] = iPos;
|
|
}
|
|
}
|
|
|
|
uImage = puArray[Info.iIcon];
|
|
|
|
} else {
|
|
uImage = uDefault;
|
|
}
|
|
|
|
End:
|
|
|
|
if (Info.hIcon) {
|
|
DestroyIcon(Info.hIcon);
|
|
}
|
|
|
|
ImageList_Destroy(hList);
|
|
|
|
return uImage;
|
|
}
|
|
|
|
void
|
|
AddSingleEntry(
|
|
IN HWND hwndTree,
|
|
IN PDBENTRY pEntry
|
|
)
|
|
/*++
|
|
|
|
AddSingleEntry
|
|
|
|
Desc: Adds a single exe entry to the Exe Tree.
|
|
Entries are sorted by name in the tree
|
|
|
|
Params:
|
|
IN HWND hwndTree: The entry tree, this should be g_hwndEntryTree always.
|
|
IN PDBENTRY pEntry: The entry that has to be shown in the entry tree
|
|
|
|
Notes: The entry tree will eventually show all the entries for an app.
|
|
The entries are sorted by name in non-descending order
|
|
--*/
|
|
{
|
|
HTREEITEM hItemExe;
|
|
HTREEITEM hMatchItem;
|
|
HTREEITEM hItemMatchingFiles;
|
|
HTREEITEM hItemShims;
|
|
HTREEITEM hItemSingleShim;
|
|
PMATCHINGFILE pMatch;
|
|
TVINSERTSTRUCT is;
|
|
TCHAR szText[MAX_PATH];
|
|
UINT uImage; // Image to be displayed in the tree
|
|
|
|
if (hwndTree == NULL || pEntry == NULL) {
|
|
Dbg(dlError, "[AddSingleEntry] Invalid arguments");
|
|
return;
|
|
}
|
|
|
|
*szText = 0;
|
|
|
|
SafeCpyN(szText, (LPCTSTR)pEntry->strExeName, ARRAYSIZE(szText));
|
|
|
|
//
|
|
// Get the image for the entry
|
|
//
|
|
if (pEntry->bDisablePerUser || pEntry->bDisablePerMachine) {
|
|
uImage =IMAGE_WARNING;
|
|
} else {
|
|
|
|
uImage = LookupFileImage(g_hImageList,
|
|
pEntry->strExeName,
|
|
IMAGE_APPLICATION,
|
|
g_uImageRedirector,
|
|
ARRAYSIZE(g_uImageRedirector));
|
|
}
|
|
|
|
is.hParent = TVI_ROOT;
|
|
is.hInsertAfter = TVI_SORT;
|
|
is.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
|
|
is.item.stateMask = TVIS_EXPANDED;
|
|
is.item.lParam = (LPARAM)pEntry;
|
|
is.item.pszText = szText;
|
|
is.item.iImage = uImage;
|
|
is.item.iSelectedImage = uImage;
|
|
|
|
//
|
|
// Insert the item for the entry
|
|
//
|
|
pEntry->hItemExe = hItemExe = TreeView_InsertItem(hwndTree, &is);
|
|
|
|
TreeView_SetItemState(hwndTree, hItemExe, TVIS_BOLD, TVIS_BOLD);
|
|
|
|
//
|
|
// Add apphelp item if apphelp is present
|
|
//
|
|
if (pEntry->appHelp.bPresent) {
|
|
|
|
TCHAR szAppHelpType[128];
|
|
|
|
*szAppHelpType = 0;
|
|
|
|
switch(pEntry->appHelp.severity) {
|
|
case APPTYPE_NONE:
|
|
|
|
GetString(IDS_NONE, szAppHelpType, ARRAYSIZE(szAppHelpType));
|
|
break;
|
|
|
|
case APPTYPE_INC_NOBLOCK:
|
|
|
|
GetString(IDS_NOBLOCK, szAppHelpType, ARRAYSIZE(szAppHelpType));
|
|
break;
|
|
|
|
case APPTYPE_INC_HARDBLOCK:
|
|
|
|
GetString(IDS_HARDBLOCK, szAppHelpType, ARRAYSIZE(szAppHelpType));
|
|
break;
|
|
|
|
case APPTYPE_MINORPROBLEM:
|
|
|
|
GetString(IDS_MINORPROBLEM, szAppHelpType, ARRAYSIZE(szAppHelpType));
|
|
break;
|
|
|
|
case APPTYPE_REINSTALL:
|
|
|
|
GetString(IDS_REINSTALL, szAppHelpType, ARRAYSIZE(szAppHelpType));
|
|
break;
|
|
}
|
|
|
|
*szText = 0;
|
|
if (StringCchPrintf(szText,
|
|
ARRAYSIZE(szText),
|
|
TEXT("%s - %s"),
|
|
CSTRING(IDS_APPHELP).pszString,
|
|
szAppHelpType) != S_OK) {
|
|
|
|
Dbg(dlError, "[AddSingleEntry]: szText has insufficent space");
|
|
}
|
|
|
|
is.hParent = hItemExe;
|
|
is.item.lParam = (LPARAM)pEntry->appHelp.pAppHelpinLib;
|
|
is.item.pszText = szText;
|
|
is.item.iImage = IMAGE_APPHELP;
|
|
is.item.iSelectedImage = IMAGE_APPHELP;
|
|
|
|
TreeView_InsertItem(hwndTree, &is);
|
|
}
|
|
|
|
//
|
|
// Add any shims or flags that are applied to the entry
|
|
//
|
|
if (pEntry->pFirstShim || pEntry->pFirstFlag) {
|
|
//
|
|
// For the user the shims and the flags are the same and so we do not
|
|
// distinguish between shims and flags in the UI
|
|
//
|
|
is.hParent = hItemExe;
|
|
is.hInsertAfter = TVI_SORT;
|
|
is.item.lParam = (TYPE)TYPE_GUI_SHIMS;
|
|
is.item.pszText = GetString(IDS_COMPATFIXES);
|
|
is.item.iImage = IMAGE_SHIM;
|
|
is.item.iSelectedImage = IMAGE_SHIM;
|
|
|
|
hItemShims = TreeView_InsertItem(hwndTree, &is);
|
|
|
|
is.hParent = hItemShims;
|
|
|
|
PSHIM_FIX_LIST pFixList = pEntry->pFirstShim;
|
|
|
|
//
|
|
// Add all the shims for this entry
|
|
//
|
|
while (pFixList) {
|
|
|
|
CSTRING strCommand;
|
|
|
|
if (pFixList->pShimFix == NULL) {
|
|
Dbg(dlError, "[AddSingleEntry]: pFixList->pShimFix == NULL");
|
|
goto Next_Shim;
|
|
}
|
|
|
|
is.hParent = hItemShims;
|
|
is.item.lParam = (LPARAM)pFixList->pShimFix;
|
|
is.item.pszText = pFixList->pShimFix->strName;
|
|
is.item.iImage = IMAGE_SHIM;
|
|
is.item.iSelectedImage = IMAGE_SHIM;
|
|
|
|
hItemSingleShim = TreeView_InsertItem(hwndTree, &is);
|
|
|
|
//
|
|
// Now add the include exclude list (Expert Mode only)
|
|
//
|
|
if (g_bExpert && (!pFixList->strlInExclude.IsEmpty()
|
|
|| !pFixList->pShimFix->strlInExclude.IsEmpty())) {
|
|
|
|
is.hParent = hItemSingleShim;
|
|
//
|
|
// Include-Exclude lists are not shown in a sorted manner and are shown as is..
|
|
//
|
|
is.hInsertAfter = TVI_LAST;
|
|
|
|
PSTRLIST listTemp;
|
|
|
|
if (pFixList->strlInExclude.m_pHead) {
|
|
listTemp = pFixList->strlInExclude.m_pHead;
|
|
} else {
|
|
listTemp = pFixList->pShimFix->strlInExclude.m_pHead;
|
|
}
|
|
|
|
while (listTemp) {
|
|
|
|
if (listTemp->data == INCLUDE) {
|
|
|
|
is.item.iImage = IMAGE_INCLUDE;
|
|
is.item.iSelectedImage = IMAGE_INCLUDE;
|
|
is.item.lParam = TYPE_GUI_INCLUDE;
|
|
|
|
} else {
|
|
|
|
is.item.iImage = IMAGE_EXCLUDE;
|
|
is.item.iSelectedImage = IMAGE_EXCLUDE;
|
|
is.item.lParam = TYPE_GUI_EXCLUDE;
|
|
}
|
|
|
|
is.item.pszText = (LPTSTR)listTemp->szStr;
|
|
listTemp = listTemp->pNext;
|
|
|
|
TreeView_InsertItem(hwndTree, &is);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now add the command line
|
|
//
|
|
if (g_bExpert && pFixList->strCommandLine.Length()) {
|
|
|
|
strCommand.Sprintf(CSTRING(IDS_COMMANDLINE),
|
|
pFixList->strCommandLine);
|
|
|
|
} else if (g_bExpert && pFixList->pShimFix->strCommandLine.Length()) {
|
|
|
|
strCommand.Sprintf(CSTRING(IDS_COMMANDLINE),
|
|
pFixList->pShimFix->strCommandLine);
|
|
}
|
|
|
|
if (strCommand.Length()) {
|
|
|
|
is.hParent = hItemSingleShim;
|
|
is.item.lParam = TYPE_GUI_COMMANDLINE;
|
|
is.item.pszText = strCommand;
|
|
is.item.iImage = IMAGE_COMMANDLINE;
|
|
is.item.iSelectedImage = IMAGE_COMMANDLINE;
|
|
|
|
TreeView_InsertItem(hwndTree, &is);
|
|
}
|
|
|
|
//
|
|
// This might have got changed if we had a InExclude list as they have to
|
|
// be shown as is
|
|
//
|
|
is.hInsertAfter = TVI_SORT;
|
|
|
|
Next_Shim:
|
|
pFixList = pFixList->pNext;
|
|
}
|
|
|
|
TreeView_Expand(hwndTree, hItemShims, TVE_EXPAND);
|
|
}
|
|
|
|
//
|
|
// Add any patches for this entry
|
|
//
|
|
if (pEntry->pFirstPatch) {
|
|
|
|
HTREEITEM hItemPatches;
|
|
|
|
is.hParent = hItemExe;
|
|
is.hInsertAfter = TVI_SORT;
|
|
is.item.lParam = (TYPE)TYPE_GUI_PATCHES;
|
|
is.item.pszText = GetString(IDS_COMPATPATCHES);
|
|
is.item.iImage = IMAGE_PATCHES;
|
|
is.item.iSelectedImage = IMAGE_PATCHES;
|
|
|
|
hItemPatches = TreeView_InsertItem(hwndTree, &is);
|
|
|
|
is.hParent = hItemPatches;
|
|
|
|
PPATCH_FIX_LIST pFixList = pEntry->pFirstPatch;
|
|
|
|
while (pFixList) {
|
|
|
|
if (pFixList->pPatchFix == NULL) {
|
|
Dbg(dlError, "[AddSingleEntry]: pFixList->pPatchFix == NULL");
|
|
goto Next_Patch;
|
|
}
|
|
|
|
is.item.lParam = (LPARAM)pFixList->pPatchFix;
|
|
is.item.pszText = pFixList->pPatchFix->strName;
|
|
|
|
TreeView_InsertItem(hwndTree, &is);
|
|
|
|
Next_Patch:
|
|
|
|
pFixList = pFixList->pNext;
|
|
}
|
|
|
|
TreeView_Expand(hwndTree, hItemPatches, TVE_EXPAND);
|
|
}
|
|
|
|
//
|
|
// Add all the flags for this entry
|
|
//
|
|
if (pEntry->pFirstFlag) {
|
|
|
|
|
|
is.hParent = hItemShims;
|
|
is.hInsertAfter = TVI_SORT;
|
|
is.item.iImage = IMAGE_SHIM;
|
|
is.item.iSelectedImage = IMAGE_SHIM;
|
|
|
|
PFLAG_FIX_LIST pFixList = pEntry->pFirstFlag;
|
|
HTREEITEM hItemSingleFlag = NULL;
|
|
CSTRING strCommand;
|
|
|
|
while (pFixList) {
|
|
|
|
if (pFixList->pFlagFix == NULL) {
|
|
Dbg(dlError, "[AddSingleEntry]: pFixList->pFlagFix == NULL");
|
|
goto Next_Flag;
|
|
}
|
|
|
|
is.item.lParam = (LPARAM)pFixList->pFlagFix;
|
|
is.item.pszText = pFixList->pFlagFix->strName;
|
|
|
|
hItemSingleFlag = TreeView_InsertItem(hwndTree, &is);
|
|
|
|
//
|
|
// Now add the command line
|
|
//
|
|
strCommand.Release();
|
|
|
|
if (g_bExpert) {
|
|
|
|
if (pFixList->strCommandLine.Length()) {
|
|
|
|
strCommand.Sprintf(CSTRING(IDS_COMMANDLINE),
|
|
pFixList->strCommandLine);
|
|
|
|
} else if (pFixList->pFlagFix->strCommandLine.Length()) {
|
|
|
|
strCommand.Sprintf(CSTRING(IDS_COMMANDLINE),
|
|
pFixList->pFlagFix->strCommandLine);
|
|
}
|
|
|
|
if (strCommand.Length()) {
|
|
|
|
is.hParent = hItemSingleFlag;
|
|
is.item.lParam = TYPE_GUI_COMMANDLINE;
|
|
is.item.pszText = strCommand;
|
|
is.item.iImage = IMAGE_COMMANDLINE;
|
|
is.item.iSelectedImage = IMAGE_COMMANDLINE;
|
|
|
|
TreeView_InsertItem(hwndTree, &is);
|
|
}
|
|
}
|
|
|
|
Next_Flag:
|
|
|
|
pFixList = pFixList->pNext;
|
|
}
|
|
|
|
TreeView_Expand(hwndTree, hItemShims, TVE_EXPAND);
|
|
}
|
|
|
|
//
|
|
// Add any layers that are applied to the entry
|
|
//
|
|
if (pEntry->pFirstLayer) {
|
|
|
|
HTREEITEM hItemLayers;
|
|
|
|
is.hParent = hItemExe;
|
|
is.hInsertAfter = TVI_SORT;
|
|
is.item.lParam = TYPE_GUI_LAYERS;
|
|
is.item.pszText = GetString(IDS_COMPATMODES);
|
|
is.item.iImage = IMAGE_LAYERS;
|
|
is.item.iSelectedImage = IMAGE_LAYERS;
|
|
|
|
hItemLayers = TreeView_InsertItem(hwndTree, &is);
|
|
|
|
is.hParent = hItemLayers;
|
|
|
|
PLAYER_FIX_LIST pFixList = pEntry->pFirstLayer;
|
|
|
|
while (pFixList) {
|
|
|
|
if (pFixList->pLayerFix == NULL) {
|
|
Dbg(dlError, "[AddSingleEntry]: pFixList->pLayerFix == NULL");
|
|
goto Next_Layer;
|
|
}
|
|
|
|
is.item.pszText = pFixList->pLayerFix->strName.pszString;
|
|
is.item.lParam = (LPARAM)pFixList->pLayerFix;
|
|
|
|
TreeView_InsertItem(hwndTree, &is);
|
|
|
|
Next_Layer:
|
|
|
|
pFixList = pFixList->pNext;
|
|
}
|
|
|
|
TreeView_Expand(hwndTree, hItemLayers, TVE_EXPAND);
|
|
}
|
|
|
|
//
|
|
// There will be atleast one matching file the program itself
|
|
//
|
|
pMatch = pEntry->pFirstMatchingFile;
|
|
|
|
is.hParent = hItemExe;
|
|
is.item.lParam = TYPE_GUI_MATCHING_FILES;
|
|
is.item.pszText = GetString(IDS_MATCHINGFILES);
|
|
is.item.iImage = IMAGE_MATCHGROUP;
|
|
is.item.iSelectedImage = IMAGE_MATCHGROUP;
|
|
|
|
hItemMatchingFiles = TreeView_InsertItem(hwndTree, &is);
|
|
|
|
//
|
|
// Add all the matching files for this entry
|
|
//
|
|
while (pMatch) {
|
|
|
|
TCHAR* pszMatchName;
|
|
|
|
if (lstrcmpi(pMatch->strMatchName, TEXT("*")) == 0) {
|
|
pszMatchName = pEntry->strExeName;
|
|
} else {
|
|
pszMatchName = pMatch->strMatchName;
|
|
}
|
|
|
|
uImage = LookupFileImage(g_hImageList,
|
|
pszMatchName,
|
|
IMAGE_APPLICATION,
|
|
g_uImageRedirector,
|
|
ARRAYSIZE(g_uImageRedirector));
|
|
|
|
is.hInsertAfter = TVI_SORT;
|
|
is.hParent = hItemMatchingFiles;
|
|
is.item.pszText = pszMatchName;
|
|
is.item.iImage = uImage;
|
|
is.item.iSelectedImage = uImage;
|
|
is.item.lParam = (LPARAM)pMatch;
|
|
|
|
hMatchItem = TreeView_InsertItem(hwndTree, &is);
|
|
|
|
is.hParent = hMatchItem;
|
|
is.hInsertAfter = TVI_LAST;
|
|
is.item.iImage = IMAGE_MATCHINFO;
|
|
is.item.iSelectedImage = IMAGE_MATCHINFO;
|
|
|
|
//
|
|
// Add the individual attributes of the matching file
|
|
//
|
|
PATTRINFO_NEW pAttr = pMatch->attributeList.pAttribute;
|
|
|
|
if (pAttr == NULL) {
|
|
Dbg(dlError, "[AddSingleEntry]: pAttr == NULL");
|
|
goto Next_MatchingFile;
|
|
}
|
|
|
|
for (DWORD dwIndex = 0; dwIndex < ATTRIBUTE_COUNT; ++dwIndex) {
|
|
|
|
*szText = 0;
|
|
|
|
DWORD dwPos = TagToIndex(pAttr[dwIndex].tAttrID);
|
|
|
|
if ((pAttr[dwIndex].dwFlags & ATTRIBUTE_AVAILABLE)
|
|
&& dwPos != -1
|
|
&& (pMatch->dwMask & (1 << (dwPos + 1)))) {
|
|
|
|
switch (pAttr[dwIndex].tAttrID) {
|
|
|
|
case TAG_BIN_PRODUCT_VERSION:
|
|
case TAG_BIN_FILE_VERSION:
|
|
case TAG_UPTO_BIN_PRODUCT_VERSION:
|
|
case TAG_UPTO_BIN_FILE_VERSION:
|
|
{
|
|
//
|
|
// Do our own formatting because SdbFormatAttribute does not
|
|
// show X.FFFF.FFFF.FFFF properly
|
|
// TODO: Remove this once SdbFormatAttribute is corrected
|
|
//
|
|
size_t cchRemaining = 0;
|
|
TCHAR* pchEnd = NULL;
|
|
|
|
if (StringCchPrintfEx(szText,
|
|
ARRAYSIZE(szText),
|
|
&pchEnd,
|
|
&cchRemaining,
|
|
0,
|
|
TEXT("%s="), SdbTagToString(pAttr[dwIndex].tAttrID)) != S_OK) {
|
|
//
|
|
// Insufficient space
|
|
//
|
|
Dbg(dlError, "[AddSingleEntry] Do not have sufficient space in buffer");
|
|
break;
|
|
}
|
|
|
|
FormatVersion(pAttr[dwIndex].ullAttr, pchEnd, cchRemaining);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
SdbFormatAttribute(&pAttr[dwIndex], szText, sizeof(szText)/sizeof(TCHAR));
|
|
}
|
|
|
|
is.item.pszText = szText;
|
|
is.item.lParam = TYPE_NULL + 1 + (1 << (dwPos + 1));
|
|
|
|
TreeView_InsertItem(hwndTree, &is);
|
|
}
|
|
}
|
|
|
|
TreeView_Expand(hwndTree, hMatchItem, TVE_EXPAND);
|
|
|
|
Next_MatchingFile:
|
|
|
|
pMatch = pMatch->pNext;
|
|
}
|
|
|
|
TreeView_Expand(hwndTree, hItemMatchingFiles, TVE_EXPAND);
|
|
TreeView_Expand(hwndTree, hItemExe, TVE_EXPAND);
|
|
}
|
|
|
|
void
|
|
UpdateEntryTreeView(
|
|
IN PDBENTRY pApps,
|
|
IN HWND hwndTree
|
|
)
|
|
/*++
|
|
UpdateEntryTreeView
|
|
|
|
Desc: Shows the entries for the App: pApps in the tree
|
|
|
|
Params:
|
|
IN PDBENTRY pApps: The app
|
|
IN HWND hwndTree: The handle to a tree
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
TCHAR szStatus[MAX_PATH];
|
|
PDBENTRY pEntry;
|
|
UINT uCount;
|
|
|
|
if (pApps == NULL || hwndTree == NULL) {
|
|
Dbg(dlError, "[UpdateEntryTreeView] Invalid arguments");
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Now we are going to show the entry tree on the right hand side
|
|
// instead of the contents list
|
|
//
|
|
g_bIsContentListVisible = FALSE;
|
|
|
|
//
|
|
// Remove all the images of the preceeding app's entries and matching files.
|
|
//
|
|
if (hwndTree == g_hwndEntryTree) {
|
|
|
|
ZeroMemory(g_uImageRedirector, sizeof(g_uImageRedirector));
|
|
|
|
//
|
|
// Remove the images added by the previous app.
|
|
//
|
|
ImageList_SetImageCount(g_hImageList, IMAGE_LAST);
|
|
|
|
ShowWindow(g_hwndContentsList, SW_HIDE);
|
|
ShowWindow(g_hwndEntryTree, SW_SHOWNORMAL);
|
|
}
|
|
|
|
TreeDeleteAll(hwndTree);
|
|
|
|
//
|
|
// We need to set WM_SETREDRAW false after calling TreeDeleteAll because
|
|
// TreeDeleteAll will first set WM_SETREDRAW false before removing the tree items
|
|
// and then will again set it to true. But since we want to make
|
|
// WM_SETREDRAW false, we must explicitely set it to FALSE *AFTER* calling
|
|
// TreeDeleteAll()
|
|
//
|
|
SendMessage(hwndTree, WM_SETREDRAW, FALSE, 0);
|
|
|
|
pEntry = pApps;
|
|
uCount = 0;
|
|
|
|
//
|
|
// Add all the entries for this application to the entry tree
|
|
//
|
|
while(pEntry) {
|
|
|
|
AddSingleEntry(hwndTree, pEntry);
|
|
uCount++;
|
|
pEntry = pEntry->pSameAppExe;
|
|
}
|
|
|
|
SendMessage(hwndTree, WM_NCPAINT, 1, 0);
|
|
SendMessage(hwndTree, WM_SETREDRAW, TRUE, 0);
|
|
|
|
//
|
|
// Select the first item
|
|
//
|
|
HTREEITEM hItem= TreeView_GetChild(hwndTree, TVI_ROOT);
|
|
|
|
if (hItem) {
|
|
TreeView_SelectItem(hwndTree, hItem);
|
|
}
|
|
|
|
//
|
|
// Because tree view has a bug and sometimes the scroll bars are not painted properly
|
|
//
|
|
SendMessage(hwndTree, WM_NCPAINT, 1, 0);
|
|
|
|
*szStatus = 0;
|
|
|
|
StringCchPrintf(szStatus,
|
|
ARRAYSIZE(szStatus),
|
|
GetString(IDS_STA_ENTRYCOUNT),
|
|
pApps->strAppName.pszString,
|
|
uCount);
|
|
|
|
SetStatus(szStatus);
|
|
}
|
|
|
|
BOOL
|
|
CheckAndSave(
|
|
IN PDATABASE pDataBase
|
|
)
|
|
/*++
|
|
|
|
CheckAndSave
|
|
|
|
Desc: Saves a database if it is not saved
|
|
|
|
Params:
|
|
IN PDATABASE pDataBase. The database that has to be saved.
|
|
|
|
Return:
|
|
TRUE: If the database was properly saved
|
|
FALSE: If there were errors while saving. Error can be because of read-only file
|
|
or if the XML is invalid.
|
|
if the user pressed cancel, we return FALSE
|
|
--*/
|
|
{
|
|
CSTRING strDBName;
|
|
|
|
if (pDataBase == NULL) {
|
|
Dbg(dlError, "Invalid parameter passed %X", pDataBase);
|
|
return FALSE;
|
|
}
|
|
|
|
if (pDataBase && pDataBase->bChanged && pDataBase->type == DATABASE_TYPE_WORKING) {
|
|
|
|
strDBName.Sprintf(CSTRING(IDS_ASKSAVE).pszString,
|
|
pDataBase->strName);
|
|
|
|
int nResult = MessageBox(g_hDlg,
|
|
strDBName,
|
|
g_szAppName,
|
|
MB_YESNOCANCEL | MB_ICONWARNING);
|
|
|
|
if (nResult == IDCANCEL) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (nResult == IDYES) {
|
|
|
|
BOOL bReturn;
|
|
|
|
//
|
|
// We check here that do we have the complete path of the .sdb?
|
|
// When we create a new database then we actually give it a name
|
|
// like Untitled_x, where x is an integer starting from 1.
|
|
// So if this is a new database, then we have to prompt for the file
|
|
// name in which this database has to be saved into
|
|
//
|
|
if (NotCompletePath(pDataBase->strPath)) {
|
|
bReturn = SaveDataBaseAs(pDataBase);
|
|
} else {
|
|
bReturn = SaveDataBase(pDataBase, pDataBase->strPath);
|
|
}
|
|
|
|
if (!bReturn) {
|
|
return FALSE;
|
|
}
|
|
|
|
pDataBase->bChanged = FALSE;
|
|
SetCaption();
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
SetDefaultDescription(
|
|
void
|
|
)
|
|
/*++
|
|
SetDefaultDescription
|
|
|
|
Desc: Sets the text for the rich edit control when we have focus on a non-shim/non-flag
|
|
tree/list item or the shim/flag does not have a description
|
|
|
|
Params:
|
|
void
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
CHARFORMAT cf;
|
|
HWND hwndRichEdit = GetDlgItem(g_hDlg, IDC_DESCRIPTION);
|
|
TCHAR szCaption[128];
|
|
TCHAR szToolkit[256];
|
|
|
|
*szCaption = *szToolkit = 0;
|
|
|
|
//
|
|
// Handle "No Information case"
|
|
//
|
|
SafeCpyN(szCaption, GetString(IDS_NODESC), ARRAYSIZE(szCaption));
|
|
SafeCpyN(szToolkit, GetString(IDS_LATEST_TOOLKIT), ARRAYSIZE(szToolkit));
|
|
|
|
StringCchPrintf(g_szDesc,
|
|
ARRAYSIZE(g_szDesc),
|
|
TEXT("%s\r\n\r\n%s"),
|
|
szCaption,
|
|
szToolkit);
|
|
|
|
SetWindowText(hwndRichEdit, g_szDesc);
|
|
|
|
memset(&cf, 0, sizeof(CHARFORMAT));
|
|
|
|
cf.cbSize = sizeof(CHARFORMAT);
|
|
cf.dwMask = CFM_COLOR | CFM_BOLD | CFM_UNDERLINE | CFM_LINK;
|
|
cf.crTextColor = RGB(0, 0, 0);
|
|
cf.dwEffects = 0;
|
|
|
|
SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
|
|
|
|
CHARRANGE cr;
|
|
|
|
cr.cpMin = 0;
|
|
cr.cpMax = wcslen(szCaption);
|
|
SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
|
|
|
|
memset(&cf, 0, sizeof(CHARFORMAT));
|
|
|
|
cf.cbSize = sizeof(CHARFORMAT);
|
|
cf.dwMask = CFM_COLOR | CFM_BOLD;
|
|
cf.crTextColor = RGB(0, 0, 127);
|
|
cf.dwEffects = CFE_BOLD;
|
|
|
|
SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
|
|
|
|
cr.cpMin = 4 + wcslen(szCaption);
|
|
cr.cpMax = 4 + wcslen(szCaption) + wcslen(szToolkit);
|
|
SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
|
|
|
|
memset(&cf, 0, sizeof(CHARFORMAT));
|
|
|
|
cf.cbSize = sizeof(CHARFORMAT);
|
|
cf.dwMask = CFM_COLOR | CFM_BOLD | CFM_LINK | CFM_UNDERLINE;
|
|
cf.crTextColor = RGB(0, 0, 255);
|
|
cf.dwEffects = CFE_LINK | CFE_UNDERLINE;
|
|
|
|
SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
|
|
|
|
SendMessage(hwndRichEdit, EM_SETEVENTMASK, 0, ENM_LINK);
|
|
|
|
cr.cpMin = 0;
|
|
cr.cpMax = 0;
|
|
SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
|
|
}
|
|
|
|
void
|
|
SetDescription(
|
|
IN PCTSTR pszCaption,
|
|
IN PCTSTR pszTip
|
|
)
|
|
/*++
|
|
SetDescription
|
|
|
|
Desc: Sets the text for the rich edit descripotion window
|
|
|
|
Params:
|
|
IN TCHAR* pszCaption: The caption. This will be shown in the first line of the rich edit control
|
|
IN TCHAR* pszTip: The remaining text for the rich edit control
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
CHARFORMAT cf;
|
|
HWND hwndRichEdit = GetDlgItem(g_hDlg, IDC_DESCRIPTION);
|
|
|
|
if (pszCaption == NULL) {
|
|
SetDefaultDescription();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// We have a valid caption, there is a shim whose description has to be shown or may be some
|
|
// apphelp whose message and URL we want to show
|
|
//
|
|
StringCchPrintf(g_szDesc, ARRAYSIZE(g_szDesc), TEXT("%s\r\n\r\n%s"), pszCaption, pszTip);
|
|
|
|
SetWindowText(hwndRichEdit, g_szDesc);
|
|
|
|
memset(&cf, 0, sizeof(CHARFORMAT));
|
|
|
|
cf.cbSize = sizeof(CHARFORMAT);
|
|
cf.dwMask = CFM_COLOR | CFM_BOLD | CFM_UNDERLINE;
|
|
cf.crTextColor = RGB(0, 0, 0);
|
|
cf.dwEffects = 0;
|
|
|
|
SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
|
|
|
|
CHARRANGE cr;
|
|
|
|
cr.cpMin = 0;
|
|
cr.cpMax = wcslen(pszCaption);
|
|
SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
|
|
|
|
memset(&cf, 0, sizeof(CHARFORMAT));
|
|
|
|
cf.cbSize = sizeof(CHARFORMAT);
|
|
cf.dwMask = CFM_COLOR | CFM_BOLD;
|
|
cf.crTextColor = RGB(0, 0, 127);
|
|
cf.dwEffects = CFE_BOLD;
|
|
|
|
SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
|
|
|
|
SendMessage(hwndRichEdit, EM_SETEVENTMASK, 0, 0);
|
|
|
|
cr.cpMin = 0;
|
|
cr.cpMax = 0;
|
|
SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
|
|
}
|
|
|
|
BOOL
|
|
HandleNotifyContentsList(
|
|
IN HWND hdlg,
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
HandleNotifyContentsList
|
|
|
|
Desc: Handles the notification messages for the contents list (RHS)
|
|
|
|
Params:
|
|
IN HWND hdlg: The main dialog box for the app
|
|
IN LPARAM lParam: The lParam associated with WM_NOTIFY
|
|
|
|
--*/
|
|
{
|
|
LPNMHDR pnm = (LPNMHDR)lParam;
|
|
LV_DISPINFO* pnmv = (LV_DISPINFO FAR *)lParam;
|
|
|
|
if (pnmv == NULL) {
|
|
Dbg(dlError, "Invalid Input parameter lParam == NULL");
|
|
return FALSE;
|
|
}
|
|
|
|
switch (pnm->code) {
|
|
|
|
case LVN_BEGINLABELEDIT:
|
|
{
|
|
g_hwndEditText = (HWND)SendMessage(g_hwndContentsList,
|
|
LVM_GETEDITCONTROL,
|
|
0,
|
|
0);
|
|
|
|
if (g_hwndEditText) {
|
|
|
|
SendMessage(g_hwndEditText,
|
|
EM_LIMITTEXT,
|
|
(WPARAM)LIMIT_APP_NAME,
|
|
0);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case LVN_ENDLABELEDIT:
|
|
|
|
EndListViewLabelEdit(lParam);
|
|
break;
|
|
|
|
case LVN_KEYDOWN:
|
|
{
|
|
NMLVKEYDOWN FAR *plvkd = (NMLVKEYDOWN FAR*)lParam;
|
|
|
|
if (plvkd && plvkd->wVKey == 13) {
|
|
//
|
|
// Enter was pressed. We will send it the double click message.
|
|
//
|
|
NMITEMACTIVATE nmactivate;
|
|
|
|
nmactivate.hdr.hwndFrom = g_hwndContentsList;
|
|
nmactivate.hdr.idFrom = IDC_CONTENTSLIST;
|
|
nmactivate.hdr.code = NM_DBLCLK;
|
|
nmactivate.iItem = g_iContentsListSelectIndex;
|
|
|
|
SendMessage(GetParent(g_hwndContentsList),
|
|
WM_NOTIFY,
|
|
IDC_ENTRY,
|
|
(LPARAM)&nmactivate);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case LVN_ITEMCHANGED:
|
|
{
|
|
LPNMLISTVIEW pnmlv = (LPNMLISTVIEW)lParam;
|
|
|
|
if (pnmlv && (pnmlv->uChanged & LVIF_STATE)) {
|
|
|
|
if (pnmlv->uNewState & LVIS_SELECTED) {
|
|
|
|
g_iContentsListSelectIndex = pnmlv->iItem;
|
|
|
|
TCHAR szText[256];
|
|
|
|
*szText = 0;
|
|
|
|
LVITEM lvItem;
|
|
|
|
lvItem.mask = TVIF_PARAM | LVIF_TEXT;
|
|
lvItem.iItem = pnmlv->iItem;
|
|
lvItem.iSubItem = 0;
|
|
lvItem.pszText = szText;
|
|
lvItem.cchTextMax = ARRAYSIZE(szText);
|
|
|
|
if (!ListView_GetItem(g_hwndContentsList, &lvItem)) {
|
|
Dbg(dlWarning, "[HandleNotifyContentsList] could not get listview item");
|
|
break;
|
|
}
|
|
|
|
if (GetFocus() == g_hwndContentsList) {
|
|
|
|
//
|
|
// Set the text in the status bar, as if we had selected the corresponding
|
|
// htreeitem in the db tree.
|
|
//
|
|
HTREEITEM hItemInDBTree = DBTree.FindChild(TreeView_GetSelection(DBTree.m_hLibraryTree),
|
|
lvItem.lParam);
|
|
|
|
SetStatusStringDBTree(hItemInDBTree);
|
|
}
|
|
|
|
//
|
|
// BUGBUG: This is only required if we ever show the databases in
|
|
// the contents list. Presently we do not show them in the
|
|
// contents list.
|
|
//
|
|
TYPE type = ConvertLparam2Type(lvItem.lParam);
|
|
|
|
if (type == DATABASE_TYPE_INSTALLED || type == DATABASE_TYPE_WORKING) {
|
|
g_pPresentDataBase = (PDATABASE) lvItem.lParam;
|
|
}
|
|
|
|
CSTRING strToolTip;
|
|
|
|
GetDescriptionString(lvItem.lParam,
|
|
strToolTip,
|
|
NULL,
|
|
szText,
|
|
NULL,
|
|
g_hwndContentsList,
|
|
pnmlv->iItem);
|
|
|
|
if (strToolTip.Length() > 0) {
|
|
SetDescription(szText, strToolTip.pszString);
|
|
} else {
|
|
SetDescription(NULL, TEXT(""));
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case NM_DBLCLK:
|
|
{
|
|
LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE)lParam;
|
|
|
|
if (lpnmitem == NULL) {
|
|
break;
|
|
}
|
|
|
|
LVITEM lvItem;
|
|
|
|
lvItem.mask = TVIF_PARAM;
|
|
lvItem.iItem = lpnmitem->iItem;
|
|
lvItem.iSubItem = 0;
|
|
|
|
if (!ListView_GetItem(g_hwndContentsList, &lvItem)) {
|
|
break;
|
|
}
|
|
|
|
TYPE type = ConvertLparam2Type(lvItem.lParam);
|
|
|
|
if (type == TYPE_ENTRY
|
|
|| type == FIX_LAYER
|
|
|| type == FIX_SHIM) {
|
|
|
|
HTREEITEM hItem = DBTree.FindChild(TreeView_GetSelection(DBTree.m_hLibraryTree),
|
|
lvItem.lParam);
|
|
if (hItem) {
|
|
TreeView_SelectItem(DBTree.m_hLibraryTree, hItem);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default: return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
HandleNotifyDBTree(
|
|
IN HWND hdlg,
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
HandleNotifyDBTree
|
|
|
|
Desc: Handles the notification messages for the db tree (LHS)
|
|
|
|
Params:
|
|
IN HWND hdlg: The main dialog box for the app
|
|
IN LPARAM lParam: The lParam associated with WM_NOTIFY
|
|
|
|
--*/
|
|
{
|
|
LPNMHDR pnm = (LPNMHDR)lParam;
|
|
LPNMTREEVIEW pnmtv = (LPNMTREEVIEW)lParam;
|
|
|
|
switch (pnm->code) {
|
|
case NM_RCLICK:
|
|
{
|
|
|
|
HWND hwndTree = pnm->hwndFrom;
|
|
TVHITTESTINFO ht;
|
|
|
|
GetCursorPos(&ht.pt);
|
|
ScreenToClient(hwndTree, &ht.pt);
|
|
|
|
TreeView_HitTest(hwndTree, &ht);
|
|
|
|
if (0 != ht.hItem) {
|
|
TreeView_SelectItem(hwndTree, ht.hItem);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case TVN_SELCHANGED:
|
|
{
|
|
//
|
|
// Warning: Do not change the code so that we do a post message for UpdateEntryTreeView
|
|
//
|
|
// BOOL DatabaseTree::AddNewExe() Selects an app and assumes that the entry tree will be
|
|
// populated with correct values after TreeView_SelectItem() returns
|
|
//
|
|
TCHAR szText[256];
|
|
CSTRING strDesc;
|
|
LPARAM lParamTreeItem;
|
|
|
|
if (pnmtv == NULL) {
|
|
break;
|
|
}
|
|
|
|
*szText = 0;
|
|
|
|
if(pnmtv->itemNew.hItem != 0) {
|
|
|
|
HandleDBTreeSelChange(pnmtv->itemNew.hItem);
|
|
|
|
CTree::GetTreeItemText(DBTree.m_hLibraryTree,
|
|
pnmtv->itemNew.hItem,
|
|
szText,
|
|
ARRAYSIZE(szText));
|
|
|
|
DBTree.GetLParam(pnmtv->itemNew.hItem, &lParamTreeItem);
|
|
|
|
//
|
|
// Get the description string
|
|
//
|
|
GetDescriptionString(lParamTreeItem,
|
|
strDesc,
|
|
NULL,
|
|
szText,
|
|
pnmtv->itemNew.hItem,
|
|
DBTree.m_hLibraryTree);
|
|
|
|
if (strDesc.Length() > 0) {
|
|
SetDescription(szText, strDesc.pszString);
|
|
} else {
|
|
SetDescription(NULL, TEXT(""));
|
|
}
|
|
}
|
|
|
|
SetStatusStringDBTree(pnmtv->itemNew.hItem);
|
|
//
|
|
// Some buttons need to be disabled/enabled depending upon
|
|
// what database we are on
|
|
//
|
|
SetTBButtonStatus(g_hwndToolBar, DBTree.m_hLibraryTree);
|
|
|
|
break;
|
|
}
|
|
|
|
case TVN_ITEMEXPANDING:
|
|
{
|
|
if (pnmtv->action & TVE_EXPAND) {
|
|
|
|
if (pnmtv->itemNew.hItem == GlobalDataBase.hItemAllApps
|
|
&& !g_bMainAppExpanded) {
|
|
|
|
//
|
|
// If we have not already loaded the apps for the main database then
|
|
// load it. When we start up we load only the library section of the main
|
|
// database and the layers in the main database. There are lots
|
|
// of apps in the system database and loading them at start up time
|
|
// will take some time and also consume lots of memory. Also
|
|
// normally people will not need to look at the system database
|
|
//
|
|
SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
|
|
INT iResult = ShowMainEntries(hdlg);
|
|
|
|
if (iResult == -1) {
|
|
|
|
//
|
|
// It is being loaded by somebody else. If we are using the query
|
|
// database feature then there we have a modeless window that
|
|
// creates a thread that calls ShowMainEntries(). We
|
|
// do not want that we should have two threads calling
|
|
// ShowMainEntries() at any given time
|
|
//
|
|
SetWindowLongPtr(hdlg, DWLP_MSGRESULT, TRUE);
|
|
|
|
//
|
|
// The status message for the main dialog is changed to normal
|
|
// when we finish ShowMainEntries()
|
|
//
|
|
SetStatus(g_hwndStatus, CSTRING(IDS_LOADINGMAIN));
|
|
SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
|
|
} else {
|
|
SetCursor(LoadCursor(NULL, IDC_ARROW));
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case TVN_BEGINLABELEDIT:
|
|
{
|
|
if (g_pPresentDataBase == NULL || g_pPresentDataBase->type != DATABASE_TYPE_WORKING) {
|
|
return TRUE;
|
|
}
|
|
|
|
LPNMTVDISPINFO ptvdi = (LPNMTVDISPINFO)lParam;
|
|
|
|
if (ptvdi == NULL) {
|
|
break;
|
|
}
|
|
|
|
HTREEITEM hItem = ptvdi->item.hItem;
|
|
|
|
if (hItem == NULL) {
|
|
break;
|
|
}
|
|
|
|
TYPE type = (TYPE)GetItemType(DBTree.m_hLibraryTree, hItem);
|
|
|
|
switch(type) {
|
|
case TYPE_ENTRY:
|
|
case FIX_LAYER:
|
|
case DATABASE_TYPE_WORKING:
|
|
|
|
g_hwndEditText = (HWND)SendMessage(DBTree.m_hLibraryTree,
|
|
TVM_GETEDITCONTROL,
|
|
0,
|
|
0);
|
|
break;
|
|
|
|
default: return TRUE;
|
|
}
|
|
|
|
if (g_hwndEditText) {
|
|
|
|
SendMessage(g_hwndEditText, EM_LIMITTEXT, (WPARAM)LIMIT_APP_NAME, (LPARAM)0);
|
|
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
if (type == DATABASE_TYPE_WORKING) {
|
|
|
|
SetWindowText(g_hwndEditText, g_pPresentDataBase->strName);
|
|
//
|
|
// Select the text
|
|
//
|
|
SendMessage(g_hwndEditText, EM_SETSEL, (WPARAM)0, (LPARAM)-1);
|
|
|
|
}
|
|
|
|
return FALSE; // Allow the editting
|
|
break;
|
|
}
|
|
|
|
case TVN_ENDLABELEDIT:
|
|
{
|
|
g_hwndEditText = NULL;
|
|
|
|
LPNMTVDISPINFO ptvdi = (LPNMTVDISPINFO)lParam;
|
|
BOOL fValid = TRUE;
|
|
|
|
if (ptvdi == NULL || g_pPresentDataBase == NULL) {
|
|
Dbg(dlError, "[HandleNotifyDBTree] ptvdi == NULL || g_pPresentDataBase == NULL");
|
|
break;
|
|
}
|
|
|
|
HTREEITEM hItem = ptvdi->item.hItem;
|
|
|
|
if (hItem == NULL) {
|
|
break;
|
|
}
|
|
|
|
if (ptvdi->item.pszText == NULL) {
|
|
fValid = FALSE;
|
|
goto end;
|
|
}
|
|
|
|
TYPE type = (TYPE)GetItemType(DBTree.m_hLibraryTree, ptvdi->item.hItem);
|
|
TCHAR szText[256];
|
|
|
|
*szText = 0;
|
|
|
|
SafeCpyN(szText, ptvdi->item.pszText, ARRAYSIZE(szText));
|
|
|
|
if (CSTRING::Trim(szText) == 0) {
|
|
fValid = FALSE;
|
|
goto end;
|
|
}
|
|
|
|
switch (type) {
|
|
case TYPE_ENTRY:
|
|
{
|
|
PDBENTRY pEntry = (PDBENTRY)ptvdi->item.lParam;
|
|
PDBENTRY pApp = g_pPresentDataBase->pEntries;
|
|
|
|
if (!IsValidAppName(szText)) {
|
|
//
|
|
// The app name contains invalid chars
|
|
//
|
|
DisplayInvalidAppNameMessage(g_hDlg);
|
|
break;
|
|
}
|
|
|
|
while (pApp) {
|
|
|
|
if (pApp->strAppName == szText) {
|
|
//
|
|
// There already exists an app of the same name
|
|
// in the present database
|
|
//
|
|
MessageBox(g_hDlg,
|
|
GetString(IDS_SAMEAPPEXISTS),
|
|
g_szAppName,
|
|
MB_ICONWARNING);
|
|
fValid = FALSE;
|
|
}
|
|
|
|
pApp = pApp->pNext;
|
|
}
|
|
|
|
while (pEntry) {
|
|
pEntry->strAppName = szText;
|
|
pEntry = pEntry->pSameAppExe;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case FIX_LAYER:
|
|
{
|
|
PLAYER_FIX plf = (PLAYER_FIX)ptvdi->item.lParam;
|
|
|
|
if (plf == NULL) {
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
if (FindFix(szText, FIX_LAYER, g_pPresentDataBase)) {
|
|
//
|
|
// A layer of this name already exists in the system database
|
|
// or the present database
|
|
//
|
|
MessageBox(g_hDlg,
|
|
GetString(IDS_LAYEREXISTS),
|
|
g_szAppName,
|
|
MB_ICONWARNING);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
plf->strName = szText;
|
|
}
|
|
|
|
break;
|
|
|
|
case DATABASE_TYPE_WORKING:
|
|
|
|
g_pPresentDataBase->strName = szText;
|
|
break;
|
|
|
|
default: fValid = FALSE;
|
|
}
|
|
|
|
end:
|
|
INT_PTR iStyle = GetWindowLongPtr(DBTree.m_hLibraryTree, GWL_STYLE);
|
|
iStyle &= ~TVS_EDITLABELS;
|
|
|
|
//
|
|
// Disable label editing. We need to do this, other
|
|
// wise whenever we have focus on some tree item after some time
|
|
// the edit box will appear there. We want that to appear only if we
|
|
// actually want to rename the stuff. The rename menu will be enabled
|
|
// only for items that can be renamed. We cannot rename anything that is in
|
|
// the system or the installed database
|
|
//
|
|
SetWindowLongPtr(DBTree.m_hLibraryTree, GWL_STYLE, iStyle);
|
|
|
|
if (fValid) {
|
|
//
|
|
// The handler for this message will now do the actual renaming of the tree
|
|
// item
|
|
//
|
|
g_pPresentDataBase->bChanged;
|
|
PostMessage(hdlg,
|
|
WM_USER_REPAINT_TREEITEM,
|
|
(WPARAM)ptvdi->item.hItem,
|
|
(LPARAM)ptvdi->item.lParam);
|
|
|
|
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default: return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
HandleNotifyExeTree(
|
|
IN HWND hdlg,
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
HandleNotifyExeTree
|
|
|
|
Desc: Handles the notification messages for the entry tree (RHS)
|
|
|
|
Params:
|
|
IN HWND hdlg: The main dialog box for the app
|
|
IN LPARAM lParam: The lParam associated with WM_NOTIFY
|
|
--*/
|
|
{
|
|
LPNMHDR pnm = (LPNMHDR)lParam;
|
|
|
|
if (pnm == NULL) {
|
|
assert(FALSE);
|
|
Dbg(dlError, "[HandleNotifyExeTree] pnm == NULL");
|
|
return;
|
|
}
|
|
|
|
switch (pnm->code) {
|
|
|
|
case NM_RCLICK:
|
|
{
|
|
|
|
HWND hwndTree = pnm->hwndFrom;
|
|
TVHITTESTINFO ht;
|
|
|
|
GetCursorPos(&ht.pt);
|
|
ScreenToClient(hwndTree, &ht.pt);
|
|
|
|
TreeView_HitTest(hwndTree, &ht);
|
|
|
|
if (0 != ht.hItem) {
|
|
TreeView_SelectItem(hwndTree, ht.hItem);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case TVN_SELCHANGED:
|
|
|
|
if (g_bDeletingEntryTree == FALSE) {
|
|
OnEntryTreeSelChange(lParam);
|
|
} else {
|
|
Dbg(dlWarning, "HandleNotifyExeTree : Got TVN_SELCHANGED for entry tree when we were deleting the entry tree");
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
GetFileName(
|
|
IN HWND hWnd,
|
|
IN LPCTSTR szTitle,
|
|
IN LPCTSTR szFilter,
|
|
IN LPCTSTR szDefaultFile,
|
|
IN LPCTSTR szDefExt,
|
|
IN DWORD dwFlags,
|
|
IN BOOL bOpen,
|
|
OUT CSTRING& szStr,
|
|
IN BOOL bDoNotVerifySDB // DEF = FALSE
|
|
)
|
|
/*++
|
|
Desc: Wrapper for GetOpenFileName() and GetSaveFileName()
|
|
|
|
Params:
|
|
IN HWND hWnd: Parent for the dialog
|
|
IN LPCTSTR szTitle:
|
|
IN LPCTSTR szFilter:
|
|
IN LPCTSTR szDefaultFile:
|
|
IN LPCTSTR szDefExt:
|
|
IN DWORD dwFlags:
|
|
IN BOOL bOpen: Whether we should show the open or save dialog
|
|
OUT CSTRING& szStr: This variable stores the name of the file
|
|
IN BOOL bDoNotVerifySDB (FALSE): When we use this routine to
|
|
get the file name with bOpen == FALSE, then this varible
|
|
determines whether we should check and add a .sdb at the end
|
|
of the file name, in case there is none.
|
|
--*/
|
|
{
|
|
OPENFILENAME ofn;
|
|
TCHAR szFilename[MAX_PATH_BUFFSIZE];
|
|
BOOL bResult;
|
|
|
|
ZeroMemory(&ofn, sizeof(OPENFILENAME));
|
|
|
|
SafeCpyN(szFilename, szDefaultFile, ARRAYSIZE(szFilename));
|
|
|
|
ofn.lStructSize = sizeof(OPENFILENAME);
|
|
ofn.hwndOwner = hWnd;
|
|
ofn.hInstance = g_hInstance;
|
|
ofn.lpstrFilter = szFilter;
|
|
ofn.lpstrFile = szFilename;
|
|
ofn.nMaxFile = MAX_PATH;
|
|
ofn.lpstrInitialDir = szDefaultFile;
|
|
ofn.lpstrTitle = szTitle;
|
|
ofn.Flags = dwFlags | OFN_NOREADONLYRETURN | OFN_HIDEREADONLY;
|
|
ofn.lpstrDefExt = szDefExt;
|
|
ofn.lpstrCustomFilter = NULL;
|
|
ofn.nMaxCustFilter = 0;
|
|
ofn.nFilterIndex = 0;
|
|
|
|
|
|
BOOL valid = FALSE; // Whether path is too long / ends with .SDB or not. Applicable for save mode only
|
|
|
|
while (!valid) {
|
|
|
|
if (bOpen) {
|
|
bResult = GetOpenFileName(&ofn);
|
|
} else {
|
|
bResult = GetSaveFileName(&ofn);
|
|
}
|
|
|
|
if (!bResult) {
|
|
return FALSE;
|
|
}
|
|
|
|
szStr = szFilename;
|
|
|
|
if (bOpen || bDoNotVerifySDB) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Do stuff to make sure that the file being saved has a .SDB extension
|
|
// and the filename is not too long so that a .SDB file name cannot
|
|
// not get appended to it.
|
|
//
|
|
if (szStr.Length() == 0) {
|
|
continue;
|
|
}
|
|
|
|
if (!szStr.EndsWith(TEXT(".sdb"))) {
|
|
|
|
if (szStr.Length() <= (MAX_PATH - 1 - 4)) {
|
|
szStr.Strcat(TEXT(".sdb"));
|
|
valid = TRUE;
|
|
}
|
|
|
|
} else {
|
|
valid = TRUE;
|
|
}
|
|
|
|
if (!valid) {
|
|
|
|
//
|
|
// The path did not have a .sdb extension and we were not able to append one, because it was
|
|
// a long path
|
|
//
|
|
CSTRING message(IDS_PATHENTERED1);
|
|
|
|
message.Strcat(szStr);
|
|
message.Strcat(GetString(IDS_PATHENTERED2));
|
|
|
|
MessageBox(hWnd, message, g_szAppName, MB_ICONWARNING);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
OpenDatabaseFiles(
|
|
IN HWND hdlg
|
|
)
|
|
/*++
|
|
OpenDatabaseFiles
|
|
|
|
Desc: Shows the open common dialog box and opens the database file(s)
|
|
selected
|
|
|
|
Params:
|
|
IN HWND hdlg: Parent for the open common dialog box
|
|
|
|
Return:
|
|
TRUE: The user selected a sdb file and at least one sdb was opened,
|
|
or highlighted because it was already opened.
|
|
|
|
FALSE: Otherwise
|
|
--*/
|
|
{
|
|
OPENFILENAME ofn;
|
|
TCHAR szCaption[128];
|
|
TCHAR szFilter[128];
|
|
TCHAR szExt[8];
|
|
CSTRINGLIST strlPaths;
|
|
TCHAR szFullPath[MAX_PATH * 2];
|
|
PSTRLIST pstrlIndex = NULL;
|
|
BOOL bRemaining = TRUE;
|
|
PCTSTR pszIndex = NULL;
|
|
INT iIndexToInsert = 0;
|
|
INT iLengthFileName = 0;
|
|
BOOL bOk = FALSE;
|
|
PTSTR pszFilesList = NULL;
|
|
K_SIZE k_pszFilesList = MAX_BUFF_OPENMULTIPLE_FILES;
|
|
|
|
pszFilesList = new TCHAR[k_pszFilesList];
|
|
|
|
if (pszFilesList == NULL) {
|
|
MEM_ERR;
|
|
goto End;
|
|
}
|
|
|
|
*szCaption = *pszFilesList = *szFilter = *szExt = 0;
|
|
|
|
GetString(IDS_OPENDATABASE, szCaption, ARRAYSIZE(szCaption));
|
|
|
|
ofn.lStructSize = sizeof(OPENFILENAME);
|
|
ofn.hwndOwner = hdlg;
|
|
ofn.hInstance = NULL;
|
|
ofn.lpstrFilter = GetString(IDS_FILTER, szFilter, ARRAYSIZE(szFilter));
|
|
ofn.lpstrCustomFilter = NULL;
|
|
ofn.nMaxCustFilter = 0;
|
|
ofn.nFilterIndex = 0;
|
|
ofn.lpstrFile = pszFilesList;
|
|
ofn.nMaxFile = k_pszFilesList;
|
|
ofn.lpstrFileTitle = NULL;
|
|
ofn.nMaxFileTitle = 0;
|
|
ofn.lpstrInitialDir = NULL;
|
|
ofn.lpstrTitle = szCaption;
|
|
ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_ALLOWMULTISELECT | OFN_EXPLORER;
|
|
ofn.lpstrDefExt = GetString(IDS_FILTER, szExt, ARRAYSIZE(szExt));
|
|
|
|
if (GetOpenFileName(&ofn)) {
|
|
|
|
//
|
|
// If the database is a big one, then the open dialog box stays put, so
|
|
// we update the controls forcibly
|
|
//
|
|
UpdateControls();
|
|
|
|
if (pszFilesList[ofn.nFileOffset - 1] == 0) {
|
|
//
|
|
// User has selected more than one file
|
|
//
|
|
SafeCpyN(szFullPath, pszFilesList, MAX_PATH);
|
|
|
|
ADD_PATH_SEPARATOR(szFullPath, ARRAYSIZE(szFullPath));
|
|
|
|
iIndexToInsert = lstrlen(szFullPath);
|
|
}
|
|
|
|
//
|
|
// Point to the first file
|
|
//
|
|
pszIndex = pszFilesList + ofn.nFileOffset;
|
|
|
|
while (bRemaining) {
|
|
|
|
if (pszFilesList[ofn.nFileOffset - 1] != 0) {
|
|
//
|
|
// User has selected only a single file
|
|
//
|
|
bRemaining = FALSE;
|
|
|
|
SafeCpyN(szFullPath, pszFilesList, MAX_PATH);
|
|
|
|
} else {
|
|
|
|
iLengthFileName = lstrlen(pszIndex);
|
|
|
|
if (*(pszIndex + iLengthFileName + 1) == 0) {
|
|
//
|
|
// This is the last component
|
|
//
|
|
bRemaining = FALSE;
|
|
}
|
|
|
|
SafeCpyN(szFullPath + iIndexToInsert, pszIndex, ARRAYSIZE(szFullPath) - iIndexToInsert);
|
|
}
|
|
|
|
//
|
|
// Test to see if we have the database open already.
|
|
// If it is open, we just highlight that and return
|
|
//
|
|
PDATABASE pDataBase = DataBaseList.pDataBaseHead;
|
|
|
|
while (pDataBase) {
|
|
|
|
if (pDataBase->strPath == szFullPath) {
|
|
|
|
TreeView_SelectItem(DBTree.m_hLibraryTree, pDataBase->hItemDB);
|
|
bOk = TRUE;
|
|
goto Next_File;
|
|
}
|
|
|
|
pDataBase = pDataBase->pNext;
|
|
}
|
|
|
|
//
|
|
// Read the database
|
|
//
|
|
pDataBase = new DATABASE(DATABASE_TYPE_WORKING);
|
|
|
|
if (pDataBase == NULL) {
|
|
MEM_ERR;
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL bReturn = GetDatabaseEntries(szFullPath, pDataBase);
|
|
|
|
if (!bReturn) {
|
|
//
|
|
// Cleanup has been called in GetDatabaseEntries
|
|
//
|
|
delete pDataBase;
|
|
pDataBase = NULL;
|
|
|
|
goto Next_File;
|
|
}
|
|
|
|
if (!DBTree.AddWorking(pDataBase)) {
|
|
|
|
CleanupDbSupport(pDataBase);
|
|
|
|
delete pDataBase;
|
|
pDataBase = NULL;
|
|
|
|
goto Next_File;
|
|
}
|
|
|
|
if (g_pPresentDataBase) {
|
|
//
|
|
// g_PresentDataBase is set properly in GetDatabaseEntries. This will be set to pDatabase
|
|
//
|
|
AddToMRU(g_pPresentDataBase->strPath);
|
|
bOk = TRUE;
|
|
}
|
|
|
|
Next_File:
|
|
if (bRemaining) {
|
|
pszIndex = pszIndex + iLengthFileName + 1;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
if (CommDlgExtendedError() == FNERR_BUFFERTOOSMALL) {
|
|
//
|
|
// We cannot select so many files at one go...
|
|
//
|
|
MessageBox(hdlg, GetString(IDS_TOO_MANYFILES), g_szAppName, MB_ICONINFORMATION);
|
|
bOk = FALSE;
|
|
}
|
|
}
|
|
|
|
End:
|
|
|
|
if (pszFilesList) {
|
|
delete[] pszFilesList;
|
|
pszFilesList = NULL;
|
|
}
|
|
|
|
RefreshMRUMenu();
|
|
SetCaption();
|
|
|
|
return bOk;
|
|
}
|
|
|
|
BOOL
|
|
SaveMRUList(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
SaveMRUList
|
|
|
|
Desc: Saves the list of MRU files in the registry.
|
|
Should be called just before exiting. When this is called we are sure
|
|
that we are going to exit, databases have already been closed
|
|
--*/
|
|
{
|
|
HKEY hKey = NULL, hSubKey = NULL;
|
|
BOOL bOk = TRUE;
|
|
DWORD dwDisposition;
|
|
|
|
if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_CURRENT_USER,
|
|
APPCOMPAT_KEY_PATH,
|
|
0,
|
|
KEY_READ,
|
|
&hKey)) {
|
|
|
|
assert(FALSE);
|
|
bOk = FALSE;
|
|
goto End;
|
|
}
|
|
|
|
if (ERROR_SUCCESS != RegCreateKeyEx(hKey,
|
|
TEXT("CompatAdmin"),
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
NULL,
|
|
&hSubKey,
|
|
&dwDisposition)) {
|
|
|
|
|
|
REGCLOSEKEY(hKey);
|
|
|
|
Dbg(dlError, "[SaveMRUList] Could not create key for CompatAdmin");
|
|
|
|
bOk = FALSE;
|
|
goto End;
|
|
}
|
|
|
|
REGCLOSEKEY(hKey);
|
|
hKey = hSubKey;
|
|
|
|
SHDeleteKey(hKey, TEXT("MRU"));
|
|
|
|
if (ERROR_SUCCESS != RegCreateKeyEx(hKey,
|
|
TEXT("MRU"),
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
NULL,
|
|
&hSubKey,
|
|
&dwDisposition)) {
|
|
REGCLOSEKEY(hKey);
|
|
|
|
Dbg(dlError, "[SaveMRUList] Could not create key for MRU");
|
|
|
|
bOk = FALSE;
|
|
goto End;
|
|
}
|
|
|
|
REGCLOSEKEY(hKey);
|
|
hKey = hSubKey;
|
|
|
|
UINT uCount = 0;
|
|
TCHAR szCount[3];
|
|
|
|
*szCount = 0;
|
|
|
|
PSTRLIST pStrListHead = g_strlMRU.m_pHead;
|
|
|
|
while (pStrListHead && uCount < MAX_MRU_COUNT) {
|
|
//
|
|
// Now add this to the registry.
|
|
//
|
|
*szCount = 0;
|
|
|
|
if (ERROR_SUCCESS != RegSetValueEx(hKey,
|
|
_itot(uCount, szCount, 10),
|
|
0,
|
|
REG_SZ,
|
|
(LPBYTE)pStrListHead->szStr.pszString,
|
|
(pStrListHead->szStr.Length() + 1) * sizeof(TCHAR))) {
|
|
|
|
REGCLOSEKEY(hKey);
|
|
|
|
Dbg(dlError, "[SaveMRUList] Could not save MRU settings");
|
|
|
|
bOk = FALSE;
|
|
goto End;
|
|
}
|
|
|
|
++uCount;
|
|
pStrListHead = pStrListHead->pNext;
|
|
}
|
|
|
|
REGCLOSEKEY(hKey);
|
|
|
|
End:
|
|
|
|
return bOk;
|
|
}
|
|
|
|
BOOL
|
|
SaveDisplaySettings(
|
|
void
|
|
)
|
|
/*++
|
|
SaveDisplaySettings
|
|
|
|
Desc: Saves the display settings in the registry
|
|
|
|
Return:
|
|
FALSE: If there was some error
|
|
TRUE: Otherwise
|
|
--*/
|
|
{
|
|
HKEY hKey = NULL, hSubKey = NULL;
|
|
DWORD dwDisposition;
|
|
RECT r, rectDBTree;
|
|
DWORD dwPos;
|
|
BOOL bOk = TRUE;
|
|
|
|
if (IsIconic(g_hDlg)) {
|
|
//
|
|
// We do not want to save the settings when we are minimized
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_CURRENT_USER,
|
|
APPCOMPAT_KEY_PATH,
|
|
0,
|
|
KEY_READ,
|
|
&hKey)) {
|
|
|
|
bOk = FALSE;
|
|
Dbg(dlError, "[SaveMRUList] Could not open key for APPCOMPAT_KEY_PATH");
|
|
goto End;
|
|
}
|
|
|
|
if (ERROR_SUCCESS != RegCreateKeyEx(hKey,
|
|
TEXT("CompatAdmin"),
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
NULL,
|
|
&hSubKey,
|
|
&dwDisposition)) {
|
|
|
|
bOk = FALSE;
|
|
Dbg(dlError, "[SaveMRUList] Could not create key for CompatAdmin");
|
|
goto End;
|
|
}
|
|
|
|
REGCLOSEKEY(hKey);
|
|
hKey = hSubKey;
|
|
|
|
if (ERROR_SUCCESS != RegCreateKeyEx(hKey,
|
|
TEXT("Display"),
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
NULL,
|
|
&hSubKey,
|
|
&dwDisposition)) {
|
|
|
|
bOk = FALSE;
|
|
Dbg(dlError, "[SaveMRUList] Could not create key for Display");
|
|
goto End;
|
|
}
|
|
|
|
REGCLOSEKEY(hKey);
|
|
hKey = hSubKey;
|
|
|
|
//
|
|
// Now save the settings in the key
|
|
//
|
|
|
|
//
|
|
// Fist the left-top
|
|
//
|
|
GetWindowRect(g_hDlg, &r);
|
|
|
|
dwPos = r.left;
|
|
|
|
if (ERROR_SUCCESS != RegSetValueEx(hKey,
|
|
TEXT("LEFT"),
|
|
0,
|
|
REG_DWORD,
|
|
(CONST BYTE*)&dwPos,
|
|
sizeof(DWORD))) {
|
|
|
|
bOk = FALSE;
|
|
Dbg(dlError, "[SaveMRUList] Could not save value for left");
|
|
goto End;
|
|
}
|
|
|
|
dwPos = r.top;
|
|
|
|
if (ERROR_SUCCESS != RegSetValueEx(hKey,
|
|
TEXT("TOP"),
|
|
0,
|
|
REG_DWORD,
|
|
(CONST BYTE*)&dwPos,
|
|
sizeof(DWORD))) {
|
|
|
|
bOk = FALSE;
|
|
Dbg(dlError, "[SaveMRUList] Could not save value for top");
|
|
goto End;
|
|
}
|
|
|
|
//
|
|
// Then the right bottom
|
|
//
|
|
dwPos = r.right;
|
|
|
|
if (ERROR_SUCCESS != RegSetValueEx(hKey,
|
|
TEXT("RIGHT"),
|
|
0,
|
|
REG_DWORD,
|
|
(CONST BYTE*)&dwPos,
|
|
sizeof(DWORD))) {
|
|
bOk = FALSE;
|
|
Dbg(dlError, "[SaveMRUList] Could not save value for right");
|
|
goto End;
|
|
}
|
|
|
|
dwPos = r.bottom;
|
|
|
|
if (ERROR_SUCCESS != RegSetValueEx(hKey,
|
|
TEXT("BOTTOM"),
|
|
0,
|
|
REG_DWORD,
|
|
(CONST BYTE*)&dwPos,
|
|
sizeof(DWORD))) {
|
|
|
|
bOk = FALSE;
|
|
Dbg(dlError, "[SaveMRUList] Could not save value for bottom");
|
|
goto End;
|
|
}
|
|
|
|
//
|
|
// Percentage Width of the db tree next.
|
|
//
|
|
GetWindowRect(DBTree.m_hLibraryTree, &rectDBTree);
|
|
dwPos = (rectDBTree.right-rectDBTree.left) ;
|
|
|
|
if (ERROR_SUCCESS != RegSetValueEx(hKey,
|
|
TEXT("DBTREE-WIDTH"),
|
|
0,
|
|
REG_DWORD,
|
|
(CONST BYTE*)&dwPos,
|
|
sizeof(DWORD))) {
|
|
|
|
bOk = FALSE;
|
|
Dbg(dlError, "[SaveMRUList] Could not save value for DBTREE-WIDTH");
|
|
goto End;
|
|
}
|
|
|
|
End:
|
|
|
|
REGCLOSEKEY(hKey);
|
|
|
|
return bOk;
|
|
}
|
|
|
|
void
|
|
LoadDisplaySettings(
|
|
void
|
|
)
|
|
/*++
|
|
LoadDisplaySettings
|
|
|
|
Desc: Loads the positional settings from the registry.
|
|
Also adjusts the splitter bar.
|
|
|
|
Warn: Even if we do some error handling and bail out, make sure that this routine
|
|
calls MoveWindow() for the main dialog window so that it gets a WM_SIZE
|
|
We arrange the controls on the handler of WM_SIZE, so it is important
|
|
that it gets a WM_SIZE from here
|
|
|
|
--*/
|
|
{
|
|
RECT r, rectDBTree;
|
|
DWORD dwType = 0;
|
|
DWORD cbData = 0;
|
|
DWORD dwFinalDBWidth = 0;
|
|
DWORD dwInitialWidth = 0;
|
|
DWORD dwInitialHeight = 0;
|
|
HKEY hKey = NULL;
|
|
LONG lResult = -1;
|
|
MENUITEMINFO mii = {0};
|
|
|
|
//
|
|
// Set the default width, height and postition etc. If this is the first
|
|
// time that the user is running CompatAdmin, CompatAdmin will start with
|
|
// these settings. The next time the user runs CompatAdmin, we will make
|
|
// the position and size as it was the last time the user ran it
|
|
//
|
|
dwInitialHeight = GetSystemMetrics(SM_CYSCREEN) / 2 + 100;
|
|
dwInitialWidth = GetSystemMetrics(SM_CXSCREEN) / 2 + 200;
|
|
|
|
r.left = 0;
|
|
r.top = 0;
|
|
r.right = r.left + dwInitialWidth;
|
|
r.bottom = r.top + dwInitialHeight;
|
|
|
|
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER,
|
|
DISPLAY_KEY,
|
|
0,
|
|
KEY_READ,
|
|
&hKey)) {
|
|
dwType = REG_DWORD;
|
|
cbData = sizeof(DWORD);
|
|
|
|
lResult = RegQueryValueEx(hKey,
|
|
TEXT("LEFT"),
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)&r.left,
|
|
&cbData);
|
|
|
|
if (lResult != ERROR_SUCCESS || dwType != REG_DWORD) {
|
|
goto End;
|
|
}
|
|
|
|
lResult = RegQueryValueEx(hKey,
|
|
TEXT("TOP"),
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)&r.top,
|
|
&cbData);
|
|
|
|
if (lResult != ERROR_SUCCESS || dwType != REG_DWORD) {
|
|
goto End;
|
|
}
|
|
|
|
lResult = RegQueryValueEx(hKey,
|
|
TEXT("RIGHT"),
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)&r.right,
|
|
&cbData);
|
|
|
|
if (lResult != ERROR_SUCCESS || dwType != REG_DWORD) {
|
|
goto End;
|
|
}
|
|
|
|
lResult = RegQueryValueEx(hKey,
|
|
TEXT("BOTTOM"),
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)&r.bottom,
|
|
&cbData);
|
|
|
|
if (lResult != ERROR_SUCCESS || dwType != REG_DWORD) {
|
|
goto End;
|
|
}
|
|
|
|
lResult = RegQueryValueEx(hKey,
|
|
TEXT("DBTREE-WIDTH"),
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)&dwFinalDBWidth,
|
|
&cbData);
|
|
|
|
if (lResult != ERROR_SUCCESS || dwType != REG_DWORD) {
|
|
goto End;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We are doing this so that we do get a WM_SIZE now. Otherwise the controls do
|
|
// not appear properly
|
|
//
|
|
MoveWindow(g_hDlg,
|
|
r.left,
|
|
r.top,
|
|
r.right - r.left,
|
|
r.bottom - r.top,
|
|
TRUE);
|
|
|
|
|
|
if (dwFinalDBWidth != 0) {
|
|
|
|
GetWindowRect(DBTree.m_hLibraryTree, &rectDBTree);
|
|
|
|
dwInitialWidth = rectDBTree.right - rectDBTree.left;
|
|
|
|
LPARAM lParam = rectDBTree.top + 2;
|
|
|
|
lParam = lParam << 16; // The y pos of the imaginary mouse
|
|
lParam |= rectDBTree.right + 2; //The x pos of the imaginary mouse
|
|
|
|
//
|
|
// Position the split bar proerply
|
|
//
|
|
OnMoveSplitter(g_hDlg, lParam, TRUE, dwFinalDBWidth - dwInitialWidth);
|
|
}
|
|
|
|
End:
|
|
REGCLOSEKEY(hKey);
|
|
}
|
|
|
|
INT
|
|
LoadSpecificInstalledDatabasePath(
|
|
IN PCTSTR pszPath
|
|
)
|
|
/*++
|
|
LoadSpecificInstalledDatabasePath
|
|
|
|
Desc: Loads an installed database from the AppPatch\custom directory
|
|
and shows that on UI
|
|
|
|
Params:
|
|
IN PCTSTR pszPath: The full path of the database in the AppPatch\custom directory
|
|
that we want to load
|
|
|
|
Return:
|
|
0: There was some critical error, like memory allocation failure,
|
|
could not add to the UI etc
|
|
|
|
-1: The database does not exist at the specified location
|
|
|
|
1: Successful
|
|
|
|
*****************************************************************************************
|
|
Warning: This routine is called by LoadInstalledDataBases(...), which has done a
|
|
EnterCriticalSection(&g_csInstalledList) before calling this, so do not do a
|
|
EnterCriticalSection(&g_csInstalledList) anywhere in this routine
|
|
*****************************************************************************************
|
|
--*/
|
|
{
|
|
INT iReturn = 1;
|
|
PDATABASE pOldPresentDatabase = NULL;
|
|
PDATABASE pDataBase = NULL;
|
|
BOOL bReturn = FALSE;
|
|
HCURSOR hCursor;
|
|
|
|
hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
|
|
pDataBase = new DATABASE(DATABASE_TYPE_INSTALLED);
|
|
|
|
if (pDataBase == NULL) {
|
|
|
|
MEM_ERR;
|
|
iReturn = 0;
|
|
goto End;
|
|
}
|
|
|
|
//
|
|
// NOTE: If GetDatabaseEntries() returns succeeds then it set the g_pPresentDataBase to pDataBase,
|
|
// so after it returns successfully, the g_pPresentDataBase is changed.
|
|
//
|
|
pOldPresentDatabase = g_pPresentDataBase;
|
|
|
|
bReturn = GetDatabaseEntries(pszPath, pDataBase);
|
|
|
|
g_pPresentDataBase = pOldPresentDatabase;
|
|
|
|
if (bReturn == FALSE) {
|
|
|
|
if (pDataBase) {
|
|
//
|
|
// Cleanup done in GetDatabaseEntries()
|
|
//
|
|
delete pDataBase;
|
|
}
|
|
|
|
//
|
|
// User might have manually deleted the file
|
|
//
|
|
return -1;
|
|
}
|
|
|
|
InstalledDataBaseList.Add(pDataBase);
|
|
|
|
if (!DBTree.AddInstalled(pDataBase)) {
|
|
InstalledDataBaseList.Remove(pDataBase);
|
|
|
|
if (pDataBase) {
|
|
delete pDataBase;
|
|
pDataBase = NULL;
|
|
}
|
|
|
|
iReturn = 0;
|
|
}
|
|
|
|
End:
|
|
hCursor ? SetCursor(hCursor) : SetCursor(LoadCursor(NULL, IDC_ARROW));
|
|
|
|
return iReturn;
|
|
}
|
|
|
|
INT
|
|
LoadSpecificInstalledDatabaseGuid(
|
|
IN PCTSTR pszGuid
|
|
)
|
|
/*++
|
|
LoadSpecificInstalledDatabaseGuid
|
|
|
|
Desc: Loads an installed database given a GUID
|
|
|
|
Params:
|
|
IN PCTSTR pszGuid: The guid of the database that we want to load
|
|
|
|
Return:
|
|
0: Failure
|
|
Otherwise returns LoadSpecificInstalledDatabasePath(...)
|
|
*/
|
|
{
|
|
TCHAR szPath[MAX_PATH * 2];
|
|
INT iLength = 0;
|
|
INT ichSizeRemaining = 0;
|
|
UINT uResult = 0;
|
|
|
|
*szPath = 0;
|
|
|
|
if (pszGuid == NULL) {
|
|
assert(FALSE);
|
|
Dbg(dlError, "LoadSpecificInstalledDatabaseGuid NULL Guid passed");
|
|
return 0;
|
|
}
|
|
|
|
uResult = GetSystemWindowsDirectory(szPath, MAX_PATH);
|
|
|
|
if (uResult == 0 || uResult >= MAX_PATH) {
|
|
Dbg(dlError, "LoadSpecificInstalledDatabaseGuid GetSystemWindowsDirectory failed");
|
|
return 0;
|
|
}
|
|
|
|
ADD_PATH_SEPARATOR(szPath, ARRAYSIZE(szPath));
|
|
|
|
iLength = lstrlen(szPath);
|
|
|
|
ichSizeRemaining = ARRAYSIZE(szPath) - iLength;
|
|
|
|
StringCchPrintf(szPath + iLength, ichSizeRemaining, TEXT("AppPatch\\Custom\\%s.sdb"), pszGuid);
|
|
|
|
return LoadSpecificInstalledDatabasePath(szPath);
|
|
}
|
|
|
|
BOOL
|
|
LoadInstalledDataBases(
|
|
void
|
|
)
|
|
/*++
|
|
LoadInstalledDataBases
|
|
|
|
Desc: First of all removes the list of installed databases, and re-loads it
|
|
|
|
Params:
|
|
void
|
|
|
|
Return:
|
|
TRUE: If the list of databases could be reloaded
|
|
FALSE: Otherwise
|
|
--*/
|
|
{
|
|
TCHAR szFileName[MAX_PATH];
|
|
TCHAR szwName[MAX_PATH];
|
|
DWORD dwchSizeSubKeyName;
|
|
HKEY hKey = NULL, hSubKey = NULL;
|
|
LPARAM lParam;
|
|
PDATABASE pOldPresentDatabase = NULL;
|
|
BOOL bOk = TRUE;
|
|
|
|
*szFileName = 0;
|
|
|
|
SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
|
|
EnterCriticalSection(&g_csInstalledList);
|
|
|
|
//
|
|
// Remove the Installed Database All Items
|
|
//
|
|
if (DBTree.m_hItemAllInstalled) {
|
|
|
|
TreeView_DeleteItem(DBTree.m_hLibraryTree, DBTree.m_hItemAllInstalled);
|
|
InstalledDataBaseList.RemoveAll();
|
|
DBTree.m_hItemAllInstalled = NULL;
|
|
}
|
|
|
|
if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
APPCOMPAT_KEY_PATH_INSTALLEDSDB,
|
|
0,
|
|
KEY_READ,
|
|
&hKey)) {
|
|
bOk = FALSE;
|
|
Dbg(dlWarning, "[LoadInstalledDataBases] Could not open APPCOMPAT_KEY_PATH_INSTALLEDSDB");
|
|
goto End;
|
|
}
|
|
|
|
DWORD dwIndex = 0;
|
|
|
|
while (TRUE) {
|
|
|
|
dwchSizeSubKeyName = ARRAYSIZE(szwName);
|
|
*szwName = 0;
|
|
|
|
if (ERROR_SUCCESS != RegEnumKeyEx(hKey,
|
|
dwIndex++,
|
|
szwName,
|
|
&dwchSizeSubKeyName,
|
|
0,
|
|
0,
|
|
0,
|
|
0)) {
|
|
break;
|
|
}
|
|
|
|
if (ERROR_SUCCESS != RegOpenKeyEx(hKey,
|
|
szwName,
|
|
0,
|
|
KEY_READ,
|
|
&hSubKey)) {
|
|
|
|
bOk = FALSE;
|
|
goto End;
|
|
}
|
|
|
|
*szFileName = 0;
|
|
|
|
DWORD dwType = REG_SZ;
|
|
DWORD dwFileNameSize = sizeof(szFileName);
|
|
LONG lResult = 0;
|
|
|
|
lResult = RegQueryValueEx(hSubKey,
|
|
TEXT("DatabasePath"),
|
|
0,
|
|
&dwType,
|
|
(LPBYTE)szFileName,
|
|
&dwFileNameSize);
|
|
|
|
|
|
if (lResult != ERROR_SUCCESS || dwType != REG_SZ) {
|
|
bOk = FALSE;
|
|
goto End;
|
|
}
|
|
|
|
if (LoadSpecificInstalledDatabasePath(szFileName) == 0) {
|
|
bOk = FALSE;
|
|
goto End;
|
|
}
|
|
|
|
REGCLOSEKEY(hSubKey);
|
|
hSubKey = NULL;
|
|
}
|
|
|
|
End:
|
|
|
|
REGCLOSEKEY(hKey);
|
|
|
|
if (hSubKey) {
|
|
REGCLOSEKEY(hSubKey);
|
|
hSubKey = NULL;
|
|
}
|
|
|
|
LeaveCriticalSection(&g_csInstalledList);
|
|
|
|
if (g_hdlgSearchDB || g_hdlgQueryDB) {
|
|
|
|
//
|
|
// Either the query or the search window is open, we should prompt
|
|
// that for installed databases, some results might now show up correctly as the
|
|
// entire list has been refreshed.
|
|
// The database and entries now will have different pointer values
|
|
//
|
|
MessageBox(g_hDlg,
|
|
GetString(IDS_SOMESEARCHWINDOW),
|
|
g_szAppName,
|
|
MB_ICONINFORMATION);
|
|
}
|
|
|
|
SetCursor(LoadCursor(NULL, IDC_ARROW));
|
|
|
|
return bOk;
|
|
}
|
|
|
|
void
|
|
SetImageList(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
SetImageList
|
|
|
|
Desc: Create our global ImageList and Add images to the ImageList and associate it
|
|
with the tree controls
|
|
|
|
--*/
|
|
{
|
|
g_hImageList = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 30, 1);
|
|
|
|
ImageList_AddIcon(g_hImageList, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_FIXES)));
|
|
ImageList_AddIcon(g_hImageList, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_HELP)));
|
|
ImageList_AddIcon(g_hImageList, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_MODE)));
|
|
ImageList_AddIcon(g_hImageList, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_PATCHES)));
|
|
|
|
ImageList_AddIcon(g_hImageList, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_ATTRIBUTE)));
|
|
ImageList_AddIcon(g_hImageList, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_MATCHHEAD)));
|
|
ImageList_AddIcon(g_hImageList, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_DISABLED)));
|
|
ImageList_AddIcon(g_hImageList, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_GLOBAL)));
|
|
ImageList_AddIcon(g_hImageList, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_LOCAL)));
|
|
ImageList_AddIcon(g_hImageList, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_CMDLINE)));
|
|
ImageList_AddIcon(g_hImageList, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_INCLUDE)));
|
|
ImageList_AddIcon(g_hImageList, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_EXCLUDE)));
|
|
|
|
|
|
ImageList_AddIcon(g_hImageList, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_APP)));
|
|
|
|
ImageList_AddIcon(g_hImageList, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_INSTALLED)));
|
|
ImageList_AddIcon(g_hImageList, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_DATABASE)));
|
|
ImageList_AddIcon(g_hImageList, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_SINGLEAPP)));
|
|
|
|
ImageList_AddIcon(g_hImageList, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_ICONALLUSERS)));
|
|
ImageList_AddIcon(g_hImageList, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_ICONSINGLEUSER)));
|
|
|
|
ImageList_AddIcon(g_hImageList, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_FILE)));
|
|
|
|
ImageList_AddIcon(g_hImageList, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_EV_ERROR)));
|
|
ImageList_AddIcon(g_hImageList, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_EV_WARNING)));
|
|
ImageList_AddIcon(g_hImageList, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_EV_INFO)));
|
|
}
|
|
|
|
HWND
|
|
InitToolBar(
|
|
IN HWND hwndParent
|
|
)
|
|
/*++
|
|
|
|
InitToolBar
|
|
|
|
Desc: Creates the tool bar for the app
|
|
|
|
Params:
|
|
IN HWND hwndParent: The parent for the tool bar
|
|
|
|
Return: The handle to the tool bar window
|
|
--*/
|
|
{
|
|
HWND hwndTB;
|
|
TBBUTTON tbbAr[BUTTON_COUNT];
|
|
DEVMODE dm;
|
|
|
|
//
|
|
// Create a toolbar that has a ToolTip associated with it.
|
|
//
|
|
hwndTB = CreateWindowEx(WS_EX_TOOLWINDOW,
|
|
TOOLBARCLASSNAME,
|
|
NULL,
|
|
WS_CHILD | WS_CLIPCHILDREN | TBSTYLE_TOOLTIPS
|
|
| CCS_ADJUSTABLE | TBSTYLE_LIST | TBSTYLE_TRANSPARENT
|
|
| TBSTYLE_FLAT,
|
|
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
hwndParent,
|
|
(HMENU)ID_TOOLBAR,
|
|
g_hInstance,
|
|
NULL);
|
|
|
|
//
|
|
// Send the TB_BUTTONSTRUCTSIZE message, which is required for
|
|
// backward compatibility
|
|
//
|
|
SendMessage(hwndTB, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
|
|
|
|
//
|
|
// Add the strings for the tool bar buttons text
|
|
//
|
|
int iIndexes[] = {
|
|
|
|
SendMessage(hwndTB, TB_ADDSTRING, 0, (LPARAM)GetString(IDS_TB_NEW)),
|
|
SendMessage(hwndTB, TB_ADDSTRING, 0, (LPARAM)GetString(IDS_TB_OPEN)),
|
|
SendMessage(hwndTB, TB_ADDSTRING, 0, (LPARAM)GetString(IDS_TB_SAVE)),
|
|
|
|
SendMessage(hwndTB, TB_ADDSTRING, 0, (LPARAM)GetString(IDS_TB_CREATEFIX)),
|
|
SendMessage(hwndTB, TB_ADDSTRING, 0, (LPARAM)GetString(IDS_TB_CREATEAPPHELP)),
|
|
SendMessage(hwndTB, TB_ADDSTRING, 0, (LPARAM)GetString(IDS_TB_CREATEMODE)),
|
|
SendMessage(hwndTB, TB_ADDSTRING, 0, (LPARAM)GetString(IDS_TB_RUN)),
|
|
|
|
SendMessage(hwndTB, TB_ADDSTRING, 0, (LPARAM)GetString(IDS_TB_SEARCH)),
|
|
SendMessage(hwndTB, TB_ADDSTRING, 0, (LPARAM)GetString(IDS_TB_QUERY)),
|
|
};
|
|
|
|
dm.dmSize = sizeof(dm);
|
|
|
|
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm);
|
|
|
|
if (dm.dmBitsPerPel >= 32) {
|
|
//
|
|
// The present settings can support >= 32 bit colors
|
|
//
|
|
|
|
//
|
|
// Create the imagelist for the toolbar and set the bitmap
|
|
//
|
|
s_hImageListToolBar = ImageList_Create(IMAGE_WIDTH,
|
|
IMAGE_HEIGHT,
|
|
ILC_COLOR32 | ILC_MASK,
|
|
8,
|
|
1);
|
|
|
|
ImageList_Add(s_hImageListToolBar,
|
|
LoadBitmap(g_hInstance, MAKEINTRESOURCE(IDB_TOOLBAR)),
|
|
NULL);
|
|
|
|
SendMessage(hwndTB, TB_SETIMAGELIST, 0, (LPARAM)(HIMAGELIST)s_hImageListToolBar);
|
|
|
|
//
|
|
// Create the hot imagelist for the toolbar and set the bitmap
|
|
//
|
|
s_hImageListToolBarHot = ImageList_Create(IMAGE_WIDTH,
|
|
IMAGE_HEIGHT,
|
|
ILC_COLOR32 | ILC_MASK,
|
|
8,
|
|
1);
|
|
|
|
ImageList_Add(s_hImageListToolBarHot,
|
|
LoadBitmap(g_hInstance, MAKEINTRESOURCE(IDB_TOOLBAR_HOT)),
|
|
NULL);
|
|
|
|
SendMessage(hwndTB, TB_SETHOTIMAGELIST, 0, (LPARAM)(HIMAGELIST)s_hImageListToolBarHot);
|
|
|
|
} else {
|
|
//
|
|
// The present settings cannot support >= 32 bit colors
|
|
//
|
|
|
|
//
|
|
// Get the normal imagelist for the tool bar when we are on low colors
|
|
//
|
|
s_hImageListToolBar = ImageList_LoadImage(g_hInstance,
|
|
MAKEINTRESOURCE(IDB_256NORMAL),
|
|
IMAGE_WIDTH,
|
|
0,
|
|
CLR_DEFAULT,
|
|
IMAGE_BITMAP,
|
|
LR_CREATEDIBSECTION);
|
|
|
|
SendMessage(hwndTB, TB_SETIMAGELIST, 0, (LPARAM)(HIMAGELIST)s_hImageListToolBar);
|
|
|
|
//
|
|
// Get the hot imagelist for the tool bar when we are on low colors
|
|
//
|
|
s_hImageListToolBarHot = ImageList_LoadImage(g_hInstance,
|
|
MAKEINTRESOURCE(IDB_256HOT),
|
|
IMAGE_WIDTH,
|
|
0,
|
|
CLR_DEFAULT,
|
|
IMAGE_BITMAP,
|
|
LR_CREATEDIBSECTION);
|
|
|
|
SendMessage(hwndTB, TB_SETHOTIMAGELIST, 0, (LPARAM)(HIMAGELIST)s_hImageListToolBarHot);
|
|
}
|
|
|
|
INT iIndex = 0, iStringIndex = 0;
|
|
|
|
// New DataBase
|
|
tbbAr[iIndex].iBitmap = IMAGE_TB_NEW;
|
|
tbbAr[iIndex].idCommand = ID_FILE_NEW;
|
|
tbbAr[iIndex].fsState = TBSTATE_ENABLED;
|
|
tbbAr[iIndex].fsStyle = BTNS_SHOWTEXT;
|
|
tbbAr[iIndex].dwData = 0;
|
|
tbbAr[iIndex++].iString = iIndexes[iStringIndex++];
|
|
|
|
// Open Database
|
|
tbbAr[iIndex].iBitmap = IMAGE_TB_OPEN;
|
|
tbbAr[iIndex].idCommand = ID_FILE_OPEN;
|
|
tbbAr[iIndex].fsState = TBSTATE_ENABLED;
|
|
tbbAr[iIndex].fsStyle = BTNS_SHOWTEXT;
|
|
tbbAr[iIndex].dwData = 0;
|
|
tbbAr[iIndex++].iString = iIndexes[iStringIndex++];
|
|
|
|
// Save Database
|
|
tbbAr[iIndex].iBitmap = IMAGE_TB_SAVE;
|
|
tbbAr[iIndex].idCommand = ID_FILE_SAVE;
|
|
tbbAr[iIndex].fsState = TBSTATE_ENABLED;
|
|
tbbAr[iIndex].fsStyle = BTNS_SHOWTEXT;
|
|
tbbAr[iIndex].dwData = 0;
|
|
tbbAr[iIndex++].iString = iIndexes[iStringIndex++];
|
|
|
|
// Add the separator
|
|
tbbAr[iIndex].iBitmap = 0;
|
|
tbbAr[iIndex].idCommand = 0;
|
|
tbbAr[iIndex].fsState = TBSTATE_ENABLED;
|
|
tbbAr[iIndex].fsStyle = BTNS_SEP;
|
|
tbbAr[iIndex].dwData = 0;
|
|
tbbAr[iIndex++].iString = 0;
|
|
|
|
// Create Fix
|
|
tbbAr[iIndex].iBitmap = IMAGE_TB_NEWFIX;
|
|
tbbAr[iIndex].idCommand = ID_FIX_CREATEANAPPLICATIONFIX;
|
|
tbbAr[iIndex].fsState = TBSTATE_ENABLED;
|
|
tbbAr[iIndex].fsStyle = BTNS_SHOWTEXT;
|
|
tbbAr[iIndex].dwData = 0;
|
|
tbbAr[iIndex++].iString = iIndexes[iStringIndex++];
|
|
|
|
// Create AppHelp
|
|
tbbAr[iIndex].iBitmap = IMAGE_TB_NEWAPPHELP;
|
|
tbbAr[iIndex].idCommand = ID_FIX_CREATEANEWAPPHELPMESSAGE;
|
|
tbbAr[iIndex].fsState = TBSTATE_ENABLED;
|
|
tbbAr[iIndex].fsStyle = BTNS_SHOWTEXT;
|
|
tbbAr[iIndex].dwData = 0;
|
|
tbbAr[iIndex++].iString = iIndexes[iStringIndex++];
|
|
|
|
// Create mode
|
|
tbbAr[iIndex].iBitmap = IMAGE_TB_NEWMODE;
|
|
tbbAr[iIndex].idCommand = ID_FIX_CREATENEWLAYER;
|
|
tbbAr[iIndex].fsState = TBSTATE_ENABLED;
|
|
tbbAr[iIndex].fsStyle = BTNS_SHOWTEXT;
|
|
tbbAr[iIndex].dwData = 0;
|
|
tbbAr[iIndex++].iString = iIndexes[iStringIndex++];
|
|
|
|
// Run
|
|
tbbAr[iIndex].iBitmap = IMAGE_TB_RUN;
|
|
tbbAr[iIndex].idCommand = ID_FIX_EXECUTEAPPLICATION;
|
|
tbbAr[iIndex].fsState = TBSTATE_ENABLED;
|
|
tbbAr[iIndex].fsStyle = BTNS_SHOWTEXT;
|
|
tbbAr[iIndex].dwData = 0;
|
|
tbbAr[iIndex++].iString = iIndexes[iStringIndex++];
|
|
|
|
// Add the separator
|
|
tbbAr[iIndex].iBitmap = 0;
|
|
tbbAr[iIndex].idCommand = 0;
|
|
tbbAr[iIndex].fsState = TBSTATE_ENABLED;
|
|
tbbAr[iIndex].fsStyle = BTNS_SEP;
|
|
tbbAr[iIndex].dwData = 0;
|
|
tbbAr[iIndex++].iString = 0;
|
|
|
|
// Search
|
|
tbbAr[iIndex].iBitmap = IMAGE_TB_SEARCH;
|
|
tbbAr[iIndex].idCommand = ID_TOOLS_SEARCHFORFIXES;
|
|
tbbAr[iIndex].fsState = TBSTATE_ENABLED;
|
|
tbbAr[iIndex].fsStyle = BTNS_SHOWTEXT;
|
|
tbbAr[iIndex].dwData = 0;
|
|
tbbAr[iIndex++].iString = iIndexes[iStringIndex++];
|
|
|
|
// Query
|
|
tbbAr[iIndex].iBitmap = IMAGE_TB_QUERY;
|
|
tbbAr[iIndex].idCommand = ID_SEARCH_QUERYDATABASE;
|
|
tbbAr[iIndex].fsState = TBSTATE_ENABLED;
|
|
tbbAr[iIndex].fsStyle = BTNS_SHOWTEXT;
|
|
tbbAr[iIndex].dwData = 0;
|
|
tbbAr[iIndex++].iString = iIndexes[iStringIndex];
|
|
|
|
SendMessage(hwndTB, TB_ADDBUTTONS, BUTTON_COUNT, (LPARAM) (LPTBBUTTON)tbbAr);
|
|
|
|
SendMessage(hwndTB, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_MIXEDBUTTONS);
|
|
|
|
SendMessage(hwndTB, TB_AUTOSIZE, 0, 0);
|
|
ShowWindow(hwndTB, SW_SHOWNORMAL);
|
|
|
|
return hwndTB;
|
|
}
|
|
|
|
void
|
|
DoInitDialog(
|
|
IN HWND hdlg
|
|
)
|
|
/*++
|
|
|
|
DoInitDialog
|
|
|
|
Desc: Does a lot of initialization stuff, loads up the library section of the
|
|
system database.
|
|
Also sets up the status bar, loads the list of installed databases,
|
|
loads the display settings
|
|
|
|
Params:
|
|
IN HWND hdlg: The main dialog.
|
|
--*/
|
|
{
|
|
HICON hIcon;
|
|
HMENU hMenu, hSubMenu;
|
|
RECT r;
|
|
RECT rectMainClient, rect;
|
|
PDATABASE pCurrentDatabase = NULL;
|
|
|
|
GetClientRect(hdlg, &rectMainClient);
|
|
|
|
//
|
|
// Check if the APPCOMPAT keys are there if not add them.
|
|
//
|
|
AddRegistryKeys();
|
|
|
|
SetImageList();
|
|
|
|
g_hwndToolBar = InitToolBar(hdlg);
|
|
|
|
GetWindowRect(g_hwndToolBar, &rect);
|
|
|
|
INT iHeightToolbar = rect.bottom - rect.top;
|
|
|
|
g_hwndStatus = CreateWindowEx(0,
|
|
STATUSCLASSNAME,
|
|
(LPCTSTR) NULL,
|
|
SBARS_SIZEGRIP |
|
|
WS_CHILD | WS_VISIBLE,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
hdlg,
|
|
(HMENU)IDC_STATUSBAR,
|
|
g_hInstance,
|
|
NULL);
|
|
|
|
GetWindowRect(g_hwndStatus, &rect);
|
|
|
|
INT iHeightStatusbar = rect.bottom - rect.top;
|
|
|
|
DBTree.Init(hdlg, iHeightToolbar, iHeightStatusbar, &rectMainClient);
|
|
|
|
g_hDlg = hdlg;
|
|
|
|
g_hwndEntryTree = GetDlgItem(hdlg, IDC_ENTRY);
|
|
TreeView_SetImageList(g_hwndEntryTree, g_hImageList, TVSIL_NORMAL);
|
|
|
|
g_hwndContentsList = GetDlgItem(hdlg, IDC_CONTENTSLIST);
|
|
|
|
ListView_SetImageList(g_hwndContentsList, g_hImageList, LVSIL_SMALL);
|
|
ListView_SetExtendedListViewStyleEx(g_hwndContentsList,
|
|
0,
|
|
LVS_EX_LABELTIP | LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP);
|
|
|
|
g_hwndRichEdit = GetDlgItem(hdlg, IDC_DESCRIPTION);
|
|
|
|
//
|
|
// Show the app icon.
|
|
//
|
|
hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_APPICON));
|
|
|
|
SetClassLongPtr(hdlg, GCLP_HICON, (LONG_PTR)hIcon);
|
|
|
|
hMenu = LoadMenu(g_hInstance, MAKEINTRESOURCE(IDR_MENU));
|
|
|
|
//
|
|
// Get the file sub menu
|
|
//
|
|
hSubMenu = GetSubMenu(hMenu, 0);
|
|
AddMRUToFileMenu(hSubMenu);
|
|
|
|
SetMenu(hdlg, hMenu);
|
|
|
|
GetWindowRect(hdlg, &r);
|
|
|
|
g_cWidth = r.right - r.left;
|
|
g_cHeight = r.bottom - r.top;
|
|
|
|
if (!ReadMainDataBase()) {
|
|
|
|
MessageBox(g_hDlg,
|
|
GetString(IDS_UNABLETO_OPEN),
|
|
g_szAppName,
|
|
MB_ICONERROR);
|
|
|
|
PostQuitMessage(0);
|
|
return;
|
|
}
|
|
|
|
DBTree.PopulateLibraryTreeGlobal();
|
|
|
|
//
|
|
// Load the Installed databases
|
|
//
|
|
LoadInstalledDataBases();
|
|
|
|
//
|
|
// Create the first empty database, and initialize pCurrentDatabase
|
|
//
|
|
pCurrentDatabase = new DATABASE(DATABASE_TYPE_WORKING);
|
|
|
|
if (pCurrentDatabase == NULL) {
|
|
MEM_ERR;
|
|
return;
|
|
}
|
|
|
|
DataBaseList.Add(pCurrentDatabase);
|
|
|
|
pCurrentDatabase->bChanged = FALSE;
|
|
g_pEntrySelApp = NULL;
|
|
g_pSelEntry = NULL;
|
|
|
|
//
|
|
// Increase the index. The next new database will have the default path of say UNTITLED_2.SDB
|
|
//
|
|
++g_uNextDataBaseIndex;
|
|
|
|
SetCaption();
|
|
|
|
//
|
|
// Now update the screen
|
|
//
|
|
DBTree.AddWorking(pCurrentDatabase);
|
|
|
|
SetCaption();
|
|
|
|
//
|
|
// Set the new procs for the tree views and the content list, to handle the tab.
|
|
//
|
|
g_PrevTreeProc = (WNDPROC)GetWindowLongPtr(g_hwndEntryTree, GWLP_WNDPROC);
|
|
g_PrevListProc = (WNDPROC)GetWindowLongPtr(g_hwndContentsList, GWLP_WNDPROC);
|
|
|
|
SetWindowLongPtr(g_hwndEntryTree, GWLP_WNDPROC,(LONG_PTR) TreeViewProc);
|
|
SetWindowLongPtr(DBTree.m_hLibraryTree, GWLP_WNDPROC,(LONG_PTR) TreeViewProc);
|
|
SetWindowLongPtr(g_hwndContentsList, GWLP_WNDPROC,(LONG_PTR) ListViewProc);
|
|
|
|
//
|
|
// The events for the per-user and the installed datbase modifications
|
|
//
|
|
g_arrhEventNotify[IND_PERUSER] = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
g_arrhEventNotify[IND_ALLUSERS] = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
//
|
|
// Listen for changes in the per-user settings
|
|
//
|
|
if (RegOpenKeyEx(HKEY_CURRENT_USER,
|
|
APPCOMPAT_KEY_PATH,
|
|
0,
|
|
KEY_READ,
|
|
&g_hKeyNotify[IND_PERUSER]) == ERROR_SUCCESS) {
|
|
|
|
RegNotifyChangeKeyValue(g_hKeyNotify[IND_PERUSER],
|
|
TRUE,
|
|
REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES |
|
|
REG_NOTIFY_CHANGE_LAST_SET,
|
|
g_arrhEventNotify[IND_PERUSER],
|
|
TRUE);
|
|
}
|
|
|
|
//
|
|
// Listen for installation or un-installation of databases
|
|
//
|
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
APPCOMPAT_KEY_PATH_INSTALLEDSDB,
|
|
0,
|
|
KEY_READ,
|
|
&g_hKeyNotify[IND_ALLUSERS]) == ERROR_SUCCESS) {
|
|
|
|
RegNotifyChangeKeyValue(g_hKeyNotify[IND_ALLUSERS],
|
|
TRUE,
|
|
REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES |
|
|
REG_NOTIFY_CHANGE_LAST_SET,
|
|
g_arrhEventNotify[IND_ALLUSERS],
|
|
TRUE);
|
|
}
|
|
|
|
//
|
|
// Create the thread that will do the action if the registry keys on which we
|
|
// are listening gets modified
|
|
//
|
|
if (g_hKeyNotify[IND_PERUSER] || g_hKeyNotify[IND_ALLUSERS]) {
|
|
|
|
DWORD dwId;
|
|
|
|
g_hThreadWait = (HANDLE)_beginthreadex(NULL, 0, (PTHREAD_START)ThreadEventKeyNotify, NULL, 0, (unsigned int*)&dwId);
|
|
}
|
|
|
|
//
|
|
// Size the main app window as it was the last time CompatAdmin was used and position the
|
|
// split bar just as it was the last time
|
|
//
|
|
LoadDisplaySettings();
|
|
|
|
//
|
|
// We will always have focus on a working database, so set the status bar to that
|
|
//
|
|
SetStatus(IDS_STA_WORKINGDB);
|
|
|
|
//
|
|
// Initialize html help
|
|
//
|
|
HtmlHelp(NULL, NULL, HH_INITIALIZE, (DWORD_PTR)&g_dwCookie);
|
|
}
|
|
|
|
void
|
|
HandleSizing(
|
|
IN HWND hDlg
|
|
)
|
|
/*++
|
|
|
|
HandleSizing
|
|
|
|
Desc: Handles the WM_SIZE for the main app dialog
|
|
|
|
Params:
|
|
IN HWND hDlg: The main dialog window
|
|
|
|
Return:
|
|
void
|
|
|
|
--*/
|
|
{
|
|
int nWidth;
|
|
int nHeight;
|
|
int nStatusbarTop;
|
|
int nWidthEntryTree; // Width of the entry tree, contents list and the rich edit
|
|
RECT rDlg;
|
|
|
|
if (g_cWidth == 0 || g_cHeight == 0) {
|
|
return;
|
|
}
|
|
|
|
GetWindowRect(hDlg, &rDlg);
|
|
|
|
nWidth = rDlg.right - rDlg.left;
|
|
nHeight = rDlg.bottom - rDlg.top;
|
|
|
|
int deltaW = nWidth - g_cWidth;
|
|
int deltaH = nHeight - g_cHeight;
|
|
|
|
HWND hwnd;
|
|
RECT r;
|
|
LONG height;
|
|
|
|
HDWP hdwp = BeginDeferWindowPos(MAIN_WINDOW_CONTROL_COUNT);
|
|
|
|
if (hdwp == NULL) {
|
|
//
|
|
// NULL indicates that insufficient system resources are available to
|
|
// allocate the structure. To get extended error information, call GetLastError.
|
|
//
|
|
assert(FALSE);
|
|
goto End;
|
|
}
|
|
|
|
//
|
|
// Status Bar
|
|
//
|
|
hwnd = GetDlgItem(hDlg, IDC_STATUSBAR);
|
|
GetWindowRect(hwnd, &r);
|
|
MapWindowPoints(NULL, hDlg, (LPPOINT)&r, 2);
|
|
|
|
DeferWindowPos(hdwp,
|
|
hwnd,
|
|
NULL,
|
|
r.left,
|
|
nStatusbarTop = r.top + deltaH,
|
|
r.right - r.left + deltaW,
|
|
r.bottom - r.top,
|
|
SWP_NOZORDER | SWP_NOACTIVATE);
|
|
|
|
//
|
|
// DataBase Tree
|
|
//
|
|
hwnd = GetDlgItem(hDlg, IDC_LIBRARY);
|
|
|
|
GetWindowRect(hwnd, &r);
|
|
MapWindowPoints(NULL, hDlg, (LPPOINT)&r, 2);
|
|
|
|
DeferWindowPos(hdwp,
|
|
hwnd,
|
|
NULL,
|
|
r.left,
|
|
r.top,
|
|
r.right - r.left,
|
|
nStatusbarTop - r.top,
|
|
SWP_NOZORDER | SWP_NOACTIVATE);
|
|
|
|
height = r.bottom - r.top + deltaH;
|
|
|
|
if (g_bDescWndOn) {
|
|
height -= 100;
|
|
}
|
|
|
|
//
|
|
// Entry Tree. NOTE that the the code for sizing the contents list and the
|
|
// rich edit control should immediately follow the code for resizing the
|
|
// entry tree
|
|
//
|
|
hwnd = GetDlgItem(hDlg, IDC_ENTRY);
|
|
|
|
GetWindowRect(hwnd, &r);
|
|
|
|
//
|
|
// Note that we must calculate the width this way before
|
|
// we Map the coords of the Entry tree wrt to the dialog box.
|
|
// I had to get the width this way and forcibly set the width rather than
|
|
// using r.right - r.left + deltaW where r is the mapped cords of the entry
|
|
// tree, because there were some problems with 640x480 resol, in which
|
|
// the entry tree, contents list and the rich edit control were getting out
|
|
// of the dialog box on their right hand side. So we have to make sure that
|
|
// they do not overrun the dialog box anyway anytime
|
|
//
|
|
nWidthEntryTree = rDlg.right - r.left - GetSystemMetrics(SM_CXBORDER) - 1;
|
|
MapWindowPoints(NULL, hDlg, (LPPOINT)&r, 2);
|
|
|
|
DeferWindowPos(hdwp,
|
|
hwnd,
|
|
NULL,
|
|
r.left,
|
|
r.top,
|
|
nWidthEntryTree,
|
|
height,
|
|
SWP_NOZORDER | SWP_NOACTIVATE);
|
|
|
|
//
|
|
// Contents list.
|
|
//
|
|
hwnd = GetDlgItem(hDlg, IDC_CONTENTSLIST);
|
|
|
|
DeferWindowPos(hdwp,
|
|
hwnd,
|
|
NULL,
|
|
r.left,
|
|
r.top,
|
|
nWidthEntryTree,
|
|
height,
|
|
SWP_NOZORDER | SWP_NOACTIVATE);
|
|
|
|
|
|
//
|
|
// Description control
|
|
//
|
|
hwnd = GetDlgItem(hDlg, IDC_DESCRIPTION);
|
|
|
|
if (g_bDescWndOn) {
|
|
|
|
DeferWindowPos(hdwp,
|
|
hwnd,
|
|
NULL,
|
|
r.left,
|
|
r.top + height,
|
|
nWidthEntryTree,
|
|
100,
|
|
SWP_NOZORDER | SWP_NOACTIVATE);
|
|
|
|
} else {
|
|
|
|
DeferWindowPos(hdwp,
|
|
hwnd,
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
SWP_NOZORDER | SWP_NOACTIVATE);
|
|
}
|
|
|
|
|
|
//
|
|
// ToolBar
|
|
//
|
|
hwnd = GetDlgItem(hDlg, ID_TOOLBAR);
|
|
|
|
GetWindowRect(hwnd, &r);
|
|
MapWindowPoints(NULL, hDlg, (LPPOINT)&r, 2);
|
|
|
|
DeferWindowPos(hdwp,
|
|
hwnd,
|
|
NULL,
|
|
r.left,
|
|
r.top,
|
|
r.right - r.left + deltaW,
|
|
r.bottom - r.top,
|
|
SWP_NOZORDER | SWP_NOACTIVATE);
|
|
|
|
EndDeferWindowPos(hdwp);
|
|
|
|
//
|
|
// The rich edit sometimes does not paint itself properly
|
|
//
|
|
hwnd = GetDlgItem(hDlg, IDC_DESCRIPTION);
|
|
InvalidateRect(hwnd, NULL, TRUE);
|
|
UpdateWindow(hwnd);
|
|
|
|
//
|
|
// Set the column width of the list view appropriately to cover the width of the list view
|
|
//
|
|
ListView_SetColumnWidth(GetDlgItem(hDlg, IDC_CONTENTSLIST), 0, LVSCW_AUTOSIZE_USEHEADER);
|
|
|
|
InvalidateRect(hDlg, NULL, TRUE);
|
|
UpdateWindow(hDlg);
|
|
|
|
g_cWidth = nWidth;
|
|
g_cHeight = nHeight;
|
|
|
|
End:
|
|
return;
|
|
}
|
|
|
|
void
|
|
ContextMenuContentsList(
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
|
|
ContextMenuContentsList
|
|
|
|
Desc: Pops up the context menu when we right click on the contents list.
|
|
This is the list view that shows up in the RHS
|
|
|
|
Params:
|
|
IN LPARAM lParam: the lParam of WM_CONTEXTMENU.
|
|
Horizontal and vertical position of the cursor, in screen coordinates,
|
|
at the time of the mouse click.
|
|
--*/
|
|
{
|
|
|
|
TYPE type;
|
|
UINT uX = LOWORD(lParam);
|
|
UINT uY = HIWORD(lParam);
|
|
|
|
HMENU hMenu = LoadMenu(g_hInstance, MAKEINTRESOURCE(IDR_CONTEXT));
|
|
|
|
if (hMenu == NULL) {
|
|
return;
|
|
}
|
|
|
|
HMENU hContextMenu = NULL;
|
|
|
|
//
|
|
// BUGBUG: This is only required if we ever show the databases in
|
|
// the contents list. Presently we do not show them in the
|
|
// contents list.
|
|
//
|
|
LPARAM lParamOfSelectedItem = NULL;
|
|
LVITEM lvi;
|
|
|
|
lvi.mask = LVIF_PARAM;
|
|
lvi.iItem = ListView_GetSelectionMark(g_hwndContentsList);
|
|
lvi.iSubItem = 0;
|
|
|
|
if (!ListView_GetItem(g_hwndContentsList, &lvi)) {
|
|
assert(FALSE);
|
|
goto End;
|
|
}
|
|
|
|
type = ConvertLparam2Type(lvi.lParam);
|
|
|
|
if (type == DATABASE_TYPE_INSTALLED || type == DATABASE_TYPE_WORKING) {
|
|
//
|
|
// If we ever decide to show the datbases in the context list
|
|
// presently we do not
|
|
//
|
|
hContextMenu = GetSubMenu(hMenu, MENU_CONTEXT_DATABASE);
|
|
} else {
|
|
hContextMenu = GetSubMenu(hMenu, MENU_CONTEXT_LIST);
|
|
}
|
|
|
|
TrackPopupMenuEx(hContextMenu,
|
|
TPM_LEFTALIGN | TPM_TOPALIGN,
|
|
uX,
|
|
uY,
|
|
g_hDlg,
|
|
NULL);
|
|
End:
|
|
DestroyMenu(hMenu);
|
|
}
|
|
|
|
|
|
void
|
|
ContextMenuExeTree(
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
|
|
ContextMenuExeTree
|
|
|
|
Desc: Pops up the context menu when we right click on the contents tree.
|
|
This is the tree view that shows up in the RHS
|
|
|
|
Params:
|
|
IN LPARAM lParam: the lParam of WM_CONTEXTMENU.
|
|
Horizontal and vertical position of the cursor, in screen coordinates,
|
|
at the time of the mouse click.
|
|
--*/
|
|
{
|
|
UINT uX = LOWORD(lParam);
|
|
UINT uY = HIWORD(lParam);
|
|
|
|
HTREEITEM hItem = TreeView_GetSelection(g_hwndEntryTree);
|
|
|
|
if ((TYPE)GetItemType(g_hwndEntryTree, hItem) != TYPE_ENTRY) {
|
|
return;
|
|
}
|
|
|
|
HMENU hMenu = LoadMenu(g_hInstance, MAKEINTRESOURCE(IDR_CONTEXT));
|
|
|
|
if (hMenu == NULL) {
|
|
return;
|
|
}
|
|
|
|
HMENU hContextMenu = NULL;
|
|
|
|
hContextMenu = GetSubMenu(hMenu, MENU_CONTEXT_FIX);
|
|
|
|
TrackPopupMenuEx(hContextMenu,
|
|
TPM_LEFTALIGN | TPM_TOPALIGN,
|
|
uX,
|
|
uY,
|
|
g_hDlg,
|
|
NULL);
|
|
|
|
DestroyMenu(hMenu);
|
|
}
|
|
|
|
void
|
|
ContextMenuLib(
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
|
|
ContextMenuLib
|
|
|
|
Desc: Pops up the context menu when we right click on the db tree.
|
|
This is the tree view that shows up in the LHS
|
|
|
|
Params:
|
|
IN LPARAM lParam: the lParam of WM_CONTEXTMENU.
|
|
Horizontal and vertical position of the cursor,
|
|
in screen coordinates, at the time of the mouse click.
|
|
--*/
|
|
{
|
|
UINT uX = LOWORD(lParam);
|
|
UINT uY = HIWORD(lParam);
|
|
|
|
HMENU hMenu = LoadMenu(g_hInstance, MAKEINTRESOURCE(IDR_CONTEXT)), hContextMenu = NULL;
|
|
|
|
if (hMenu == NULL) {
|
|
return;
|
|
}
|
|
|
|
HTREEITEM hItemSelected = TreeView_GetSelection(DBTree.m_hLibraryTree);
|
|
|
|
if (hItemSelected == NULL) {
|
|
goto END;
|
|
}
|
|
|
|
TYPE type = (TYPE)GetItemType(DBTree.m_hLibraryTree, hItemSelected);
|
|
|
|
switch (type) {
|
|
|
|
case DATABASE_TYPE_INSTALLED:
|
|
case DATABASE_TYPE_GLOBAL:
|
|
case DATABASE_TYPE_WORKING:
|
|
|
|
hContextMenu = GetSubMenu(hMenu, MENU_CONTEXT_DATABASE);
|
|
break;
|
|
|
|
case FIX_SHIM:
|
|
case FIX_FLAG:
|
|
case FIX_LAYER:
|
|
|
|
hContextMenu = GetSubMenu(hMenu, MENU_CONTEXT_APP_LAYER);
|
|
|
|
if (type == FIX_LAYER) {
|
|
|
|
//
|
|
// A layer can be modified as well.
|
|
//
|
|
InsertMenu(hContextMenu,
|
|
ID_EDIT_RENAME,
|
|
MF_BYCOMMAND,
|
|
ID_MODIFY_COMPATIBILITYMODE,
|
|
CSTRING(IDS_MODIFY));
|
|
}
|
|
|
|
break;
|
|
|
|
case TYPE_ENTRY:
|
|
|
|
hContextMenu = GetSubMenu(hMenu, MENU_CONTEXT_APP_LAYER);
|
|
break;
|
|
|
|
case TYPE_GUI_APPS:
|
|
case TYPE_GUI_LAYERS:
|
|
|
|
//
|
|
// We cannot rename if the focus is on the root of applications or layers
|
|
//
|
|
hContextMenu = GetSubMenu(hMenu, MENU_CONTEXT_APP_LAYER);
|
|
EnableMenuItem(hContextMenu, ID_EDIT_RENAME, MF_GRAYED);
|
|
break;
|
|
|
|
case TYPE_GUI_DATABASE_WORKING_ALL:
|
|
|
|
hContextMenu = GetSubMenu(hMenu, MENU_CONTEXT_DATABASE_ALL);
|
|
break;
|
|
}
|
|
|
|
if (hContextMenu == NULL) {
|
|
goto END;
|
|
}
|
|
|
|
TrackPopupMenuEx(hContextMenu,
|
|
TPM_LEFTALIGN | TPM_TOPALIGN,
|
|
uX,
|
|
uY,
|
|
g_hDlg,
|
|
NULL);
|
|
|
|
END:
|
|
DestroyMenu(hMenu);
|
|
}
|
|
|
|
BOOL
|
|
HandleDBTreeSelChange(
|
|
IN HTREEITEM hItem
|
|
)
|
|
/*++
|
|
|
|
HandleDBTreeSelChange
|
|
|
|
Desc: Handles the TVN_SELCHANGE for the database tree (LHS)
|
|
|
|
Params:
|
|
IN HTREEITEM hItem: The newly selected tree item
|
|
|
|
Return:
|
|
FALSE: If some error occurs, like invalid hItem
|
|
TRUE: Otherwise
|
|
|
|
Notes: A new item has been selected. If this item is an app,
|
|
then we set the g_pSelEntry and set the focus to the first entry of the EXE tree.
|
|
|
|
Anyway we move till we find a DATABASE HTREEITEM and if the type of the database is
|
|
TYPE_DATABASE_WORKING, then we check if the database is the same as the
|
|
g_pPresentDataBase.
|
|
|
|
If not then we change the g_pPresentDataBase, and also
|
|
delete all the items on the EXE Tree. and set both the g_pSelEntry and the
|
|
g_pEntrySelApp to NULL.
|
|
--*/
|
|
{
|
|
HTREEITEM hItemTemp = hItem;
|
|
PDATABASE pDataBase = NULL;
|
|
LPARAM lParam;
|
|
PDBENTRY pApp;
|
|
TYPE type;
|
|
|
|
type = TYPE_UNKNOWN;
|
|
|
|
//
|
|
// If the selected item is not an app/entry then we need to disable the run
|
|
// and the change enable status options. We do this by making the pointers to the currently
|
|
// selected app or entry as NULL so that everybody knows that we are not on an app
|
|
//
|
|
type = GetItemType(DBTree.m_hLibraryTree, hItem);
|
|
|
|
if (type != TYPE_ENTRY) {
|
|
//
|
|
// We are not on some application node
|
|
//
|
|
g_pSelEntry = NULL;
|
|
g_pEntrySelApp = NULL;
|
|
}
|
|
|
|
while (hItemTemp) {
|
|
|
|
if (!DBTree.GetLParam(hItemTemp, &lParam)) {
|
|
return FALSE;
|
|
}
|
|
|
|
type = (TYPE)lParam;
|
|
|
|
if (type > TYPE_NULL) {
|
|
type = ((PDS_TYPE)lParam)->type;
|
|
}
|
|
|
|
if (type == DATABASE_TYPE_WORKING
|
|
|| type == DATABASE_TYPE_INSTALLED
|
|
|| type == DATABASE_TYPE_GLOBAL) {
|
|
|
|
pDataBase = (PDATABASE)lParam;
|
|
g_pPresentDataBase = pDataBase;
|
|
|
|
SetCaption();
|
|
break;
|
|
|
|
} else {
|
|
hItemTemp = TreeView_GetParent(DBTree.m_hLibraryTree, hItemTemp);
|
|
}
|
|
}
|
|
|
|
if (hItemTemp == NULL) {
|
|
|
|
//
|
|
// Selected item is above the database, such as the "Working DataBases" Item etc.
|
|
//
|
|
g_pPresentDataBase = NULL;
|
|
g_pEntrySelApp = g_pSelEntry = NULL;
|
|
SetCaption(FALSE);
|
|
}
|
|
|
|
if (hItemTemp == NULL) {
|
|
TreeDeleteAll(g_hwndEntryTree);
|
|
g_pEntrySelApp = g_pSelEntry = NULL;
|
|
}
|
|
|
|
//
|
|
// If the selected item is an app then we have to update the g_hwndEntryTree
|
|
//
|
|
if (!DBTree.GetLParam(hItem, &lParam)) {
|
|
return FALSE;
|
|
}
|
|
|
|
type = (TYPE)lParam;
|
|
|
|
if (type > TYPE_NULL) {
|
|
|
|
type = ((PDS_TYPE)lParam)->type;
|
|
|
|
if (type == TYPE_ENTRY) {
|
|
|
|
pApp = (PDBENTRY)lParam;
|
|
|
|
SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
UpdateEntryTreeView(pApp, g_hwndEntryTree);
|
|
SetCursor(LoadCursor(NULL, IDC_ARROW));
|
|
|
|
g_pEntrySelApp = pApp;
|
|
}
|
|
}
|
|
|
|
if (hItem == GlobalDataBase.hItemAllApps && !g_bMainAppExpanded) {
|
|
//
|
|
// We have clicked on the "Applications" tree item of the main database
|
|
// and we have not loaded the exe entries of the main database as
|
|
// yet, so let's do it now
|
|
//
|
|
SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
INT iResult = ShowMainEntries(g_hDlg);
|
|
|
|
if (iResult == -1) {
|
|
//
|
|
// It is being loaded by somebody else. If we are using the query
|
|
// database feature then there we have a modeless window that
|
|
// creates a thread that calls ShowMainEntries(). We
|
|
// do not want that we should have two threads calling
|
|
// ShowMainEntries() at any given time
|
|
//
|
|
|
|
//
|
|
// The status message for the main dialog is changed to normal
|
|
// when we finish ShowMainEntries()
|
|
//
|
|
SetStatus(g_hwndStatus, CSTRING(IDS_LOADINGMAIN));
|
|
|
|
return TRUE;
|
|
|
|
} else {
|
|
SetCursor(LoadCursor(NULL, IDC_ARROW));
|
|
}
|
|
}
|
|
|
|
if (type != TYPE_ENTRY) {
|
|
|
|
//
|
|
// The entry tree will need to be shown if type == TYPE_ENTRY
|
|
//
|
|
PostMessage(g_hDlg, WM_USER_POPULATECONTENTSLIST, 0, (LPARAM)hItem);
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
void
|
|
UpdateDescWindowStatus(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
UpdateDescWindowStatus
|
|
|
|
Desc: Shows/hides the rich edit control aka Description window.
|
|
This will depend on the value of g_bDescWndOn.
|
|
If this is TRUE, we need to show the control otherwise
|
|
hide it
|
|
--*/
|
|
{
|
|
HWND hwnd;
|
|
RECT r;
|
|
LONG height;
|
|
|
|
hwnd = GetDlgItem(g_hDlg, IDC_LIBRARY);
|
|
GetWindowRect(hwnd, &r);
|
|
|
|
height = r.bottom - r.top;
|
|
|
|
if (g_bDescWndOn) {
|
|
height -= 100;
|
|
}
|
|
|
|
//
|
|
// ENTRY TREE
|
|
//
|
|
hwnd = GetDlgItem(g_hDlg, IDC_ENTRY);
|
|
|
|
GetWindowRect(hwnd, &r);
|
|
MapWindowPoints(NULL, g_hDlg, (LPPOINT)&r, 2);
|
|
|
|
MoveWindow(hwnd,
|
|
r.left,
|
|
r.top,
|
|
r.right - r.left,
|
|
height,
|
|
TRUE);
|
|
|
|
//
|
|
// Contents list.
|
|
//
|
|
hwnd = GetDlgItem(g_hDlg, IDC_CONTENTSLIST);
|
|
|
|
MoveWindow(hwnd,
|
|
r.left,
|
|
r.top,
|
|
r.right - r.left,
|
|
height,
|
|
TRUE);
|
|
|
|
hwnd = GetDlgItem(g_hDlg, IDC_DESCRIPTION);
|
|
|
|
if (g_bDescWndOn) {
|
|
|
|
MoveWindow(hwnd,
|
|
r.left,
|
|
r.top + height + 1,
|
|
r.right - r.left,
|
|
100,
|
|
TRUE);
|
|
//
|
|
// We need to show the control again if it was hidden.
|
|
//
|
|
ShowWindow(hwnd, SW_SHOWNORMAL);
|
|
|
|
} else {
|
|
|
|
MoveWindow(hwnd,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
TRUE);
|
|
//
|
|
// We need to hide the control so that the tab ordering
|
|
// is done properly
|
|
//
|
|
ShowWindow(hwnd, SW_HIDE);
|
|
}
|
|
}
|
|
|
|
INT_PTR CALLBACK
|
|
CompatAdminDlgProc(
|
|
IN HWND hdlg,
|
|
IN UINT uMsg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
|
|
CompatAdminDlgProc
|
|
|
|
Desc: The main message handler for the app. This routine handles the
|
|
messages for the main modeless dialog box
|
|
|
|
Params: Standard dialog handler parameters
|
|
|
|
IN HWND hDlg
|
|
IN UINT uMsg
|
|
IN WPARAM wParam
|
|
IN LPARAM lParam
|
|
|
|
Return: Standard dialog handler return
|
|
--*/
|
|
|
|
{
|
|
UINT uMRUSelPos = 0;
|
|
int wCode = LOWORD(wParam);
|
|
int wNotifyCode = HIWORD(wParam);
|
|
|
|
switch (uMsg) {
|
|
|
|
case WM_INITDIALOG:
|
|
|
|
ProcessSwitches();
|
|
|
|
InitializeCriticalSection(&g_critsectShowMain);
|
|
InitializeCriticalSection(&s_csExpanding);
|
|
InitializeCriticalSection(&g_csInstalledList);
|
|
|
|
DoInitDialog(hdlg);
|
|
|
|
//
|
|
// Load any databases that were passed through the command line
|
|
//
|
|
PostMessage(hdlg, WM_USER_LOAD_COMMANDLINE_DATABASES, 0, 0);
|
|
|
|
//
|
|
// Load per-user settings.
|
|
//
|
|
LoadPerUserSettings();
|
|
|
|
SetFocus(DBTree.m_hLibraryTree);
|
|
break;
|
|
|
|
case WM_USER_LOAD_COMMANDLINE_DATABASES:
|
|
{
|
|
int iArgc = 0;
|
|
|
|
SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
|
|
LPWSTR* arParams = CommandLineToArgvW(GetCommandLineW(), &iArgc);
|
|
|
|
if (arParams) {
|
|
|
|
for (int iIndex = 1; iIndex < iArgc; ++iIndex) {
|
|
|
|
if (arParams[iIndex][0] == TEXT('-') || arParams[iIndex][0] == TEXT('/')) {
|
|
//
|
|
// Ignore the switches
|
|
//
|
|
continue;
|
|
}
|
|
|
|
LoadDataBase(arParams[iIndex]);
|
|
}
|
|
|
|
GlobalFree(arParams);
|
|
arParams = NULL;
|
|
}
|
|
|
|
SetCursor(LoadCursor(NULL, IDC_ARROW));
|
|
break;
|
|
}
|
|
|
|
case WM_USER_ACTIVATE:
|
|
//
|
|
// Some thread asked us to become the active window
|
|
//
|
|
SetActiveWindow(hdlg);
|
|
SetFocus(hdlg);
|
|
break;
|
|
|
|
case WM_USER_UPDATEPERUSER:
|
|
|
|
LoadPerUserSettings();
|
|
break;
|
|
|
|
case WM_USER_UPDATEINSTALLED:
|
|
|
|
//
|
|
// We should not update the list if we are doing a test run.
|
|
// Note that because of this, if the installed database list gets changed when
|
|
// we are doing a test run, we will not be able to see the changes, till the
|
|
// next time we will have to refresh the list
|
|
//
|
|
if (!g_bUpdateInstalledRequired) {
|
|
g_bUpdateInstalledRequired = TRUE;
|
|
break;
|
|
}
|
|
|
|
LoadInstalledDataBases();
|
|
break;
|
|
|
|
case WM_USER_POPULATECONTENTSLIST:
|
|
|
|
PopulateContentsList((HTREEITEM)lParam);
|
|
break;
|
|
|
|
case WM_INITMENUPOPUP:
|
|
|
|
HandlePopUp(hdlg, wParam, lParam);
|
|
break;
|
|
|
|
case WM_USER_REPAINT_LISTITEM:
|
|
{
|
|
//
|
|
// Here we will actually do the renaming of items of
|
|
// the items in the contents list view
|
|
//
|
|
LVITEM lvItem;
|
|
|
|
lvItem.iItem = (INT)wParam;
|
|
lvItem.iSubItem = 0;
|
|
lvItem.mask = LVIF_PARAM;
|
|
|
|
if (!ListView_GetItem(g_hwndContentsList, &lvItem)) {
|
|
break;
|
|
}
|
|
|
|
TYPE type = ConvertLparam2Type(lvItem.lParam);
|
|
TCHAR* pchText = NULL;
|
|
|
|
switch (type) {
|
|
case TYPE_ENTRY:
|
|
|
|
pchText = ((PDBENTRY)lParam)->strAppName.pszString;
|
|
break;
|
|
|
|
case FIX_LAYER:
|
|
|
|
pchText = ((PLAYER_FIX)lParam)->strName.pszString;
|
|
break;
|
|
}
|
|
|
|
ListView_SetItemText(g_hwndContentsList, lvItem.iItem, 0, pchText);
|
|
break;
|
|
}
|
|
|
|
case WM_USER_REPAINT_TREEITEM:
|
|
{
|
|
//
|
|
// Here we will actually do the renaming of items of
|
|
// the items in the db tree (LHS)
|
|
//
|
|
HTREEITEM hItem = (HTREEITEM)wParam;
|
|
TCHAR* pszText = NULL;
|
|
TYPE type = (TYPE)GetItemType(DBTree.m_hLibraryTree, hItem);
|
|
|
|
switch (type) {
|
|
case TYPE_ENTRY:
|
|
|
|
pszText = ((PDBENTRY)lParam)->strAppName.pszString;
|
|
break;
|
|
|
|
case FIX_LAYER:
|
|
|
|
pszText = ((PLAYER_FIX)lParam)->strName.pszString;
|
|
break;
|
|
}
|
|
|
|
|
|
TVITEM Item;
|
|
|
|
Item.mask = TVIF_TEXT;
|
|
Item.pszText = pszText;
|
|
Item.hItem = hItem;
|
|
|
|
TreeView_SetItem(DBTree.m_hLibraryTree, &Item);
|
|
|
|
if (g_pPresentDataBase) {
|
|
g_pPresentDataBase->bChanged = TRUE;
|
|
|
|
//
|
|
// We might need to change the caption to show that
|
|
// the database has changed. i.e. put a * there
|
|
//
|
|
SetCaption();
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_CONTEXTMENU:
|
|
{
|
|
HWND hWnd = (HWND)wParam;
|
|
|
|
if (hWnd == g_hwndEntryTree) {
|
|
ContextMenuExeTree(lParam);
|
|
|
|
} else if (hWnd == DBTree.m_hLibraryTree) {
|
|
ContextMenuLib(lParam);
|
|
|
|
} else if (hWnd == g_hwndContentsList) {
|
|
ContextMenuContentsList(lParam);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_LBUTTONDOWN:
|
|
{
|
|
RECT rectDBTree, rectEntryTree;
|
|
|
|
GetWindowRect(GetDlgItem(hdlg, IDC_LIBRARY), &rectDBTree);
|
|
MapWindowPoints(NULL, hdlg, (LPPOINT)&rectDBTree, 2);
|
|
|
|
GetWindowRect(GetDlgItem(hdlg, IDC_ENTRY), &rectEntryTree);
|
|
MapWindowPoints(NULL, hdlg, (LPPOINT)&rectEntryTree, 2);
|
|
|
|
int iMX = (int)LOWORD(lParam);
|
|
int iMY = (int)HIWORD(lParam);
|
|
|
|
//
|
|
// Check if we are on top of the split bar
|
|
//
|
|
if (iMX > rectDBTree.right &&
|
|
iMX < rectEntryTree.left &&
|
|
iMY > rectDBTree.top &&
|
|
iMY < rectDBTree.bottom) {
|
|
|
|
SetCapture(hdlg);
|
|
g_bDrag = TRUE;
|
|
//
|
|
// We need this to calculate how much and in which
|
|
// direction we are moving the split bar
|
|
//
|
|
g_iMousePressedX = iMX;
|
|
|
|
SetCursor(LoadCursor(NULL, IDC_SIZEWE));
|
|
|
|
} else {
|
|
g_bDrag = FALSE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_MOUSEMOVE:
|
|
|
|
//
|
|
// If g_bDrag is true then this routine will drag the split bar,
|
|
// otherwise it will change the cursor to an WE arrow if we are on top
|
|
// of the split bar
|
|
//
|
|
OnMoveSplitter(hdlg,
|
|
lParam,
|
|
(wParam & MK_LBUTTON) && g_bDrag,
|
|
LOWORD(lParam) - g_iMousePressedX);
|
|
|
|
|
|
break;
|
|
|
|
case WM_LBUTTONUP:
|
|
|
|
if (g_bDrag) {
|
|
|
|
g_bDrag = FALSE;
|
|
ReleaseCapture();
|
|
|
|
//
|
|
// Set the column width of the list view appropriately to cover the width
|
|
// of the list view
|
|
//
|
|
ListView_SetColumnWidth(g_hwndContentsList,
|
|
0,
|
|
LVSCW_AUTOSIZE_USEHEADER);
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_NOTIFY:
|
|
{
|
|
LPNMTREEVIEW pnmtv = (LPNMTREEVIEW)lParam;
|
|
LPNMHDR lpnmhdr = (LPNMHDR)lParam;
|
|
|
|
if (lpnmhdr == NULL) {
|
|
break;
|
|
}
|
|
|
|
if (lpnmhdr->code == TTN_GETDISPINFO) {
|
|
ShowToolBarToolTips(hdlg, lParam);
|
|
break;
|
|
}
|
|
|
|
if (lpnmhdr->idFrom == IDC_ENTRY) {
|
|
HandleNotifyExeTree(hdlg, lParam);
|
|
break;
|
|
}
|
|
|
|
if (lpnmhdr->idFrom == IDC_LIBRARY) {
|
|
return HandleNotifyDBTree(hdlg, lParam);
|
|
}
|
|
|
|
if (lpnmhdr->idFrom == IDC_CONTENTSLIST) {
|
|
return HandleNotifyContentsList(hdlg, lParam);
|
|
}
|
|
|
|
if (lpnmhdr->idFrom == IDC_DESCRIPTION) {
|
|
|
|
if (lpnmhdr->code == EN_LINK) {
|
|
|
|
ENLINK* penl = (ENLINK*)lParam;
|
|
|
|
if (penl->msg == WM_LBUTTONUP) {
|
|
|
|
SHELLEXECUTEINFO sei = { 0 };
|
|
|
|
sei.cbSize = sizeof(SHELLEXECUTEINFO);
|
|
sei.fMask = SEE_MASK_DOENVSUBST;
|
|
sei.hwnd = hdlg;
|
|
sei.nShow = SW_SHOWNORMAL;
|
|
sei.lpFile = g_szToolkitURL;
|
|
|
|
//
|
|
// Get more information about CompatAdmin stuff
|
|
//
|
|
ShellExecuteEx(&sei);
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_SIZE:
|
|
|
|
if (wParam != SIZE_MINIMIZED) {
|
|
|
|
HandleSizing(hdlg);
|
|
|
|
if (wParam == SIZE_RESTORED) {
|
|
|
|
//
|
|
// We might have got minimized because some other
|
|
// app came at the top. So when we again become
|
|
// the top-level window, the user pressed alt-tab or clicked
|
|
// on the icon in the taskbar then we should show the other
|
|
// modeless dialog boxes as well, if they were visible earlier
|
|
//
|
|
if (g_hdlgSearchDB) {
|
|
ShowWindow(g_hdlgSearchDB, SW_RESTORE);
|
|
}
|
|
|
|
if (g_hdlgQueryDB) {
|
|
ShowWindow(g_hdlgQueryDB, SW_RESTORE);
|
|
}
|
|
|
|
if (g_hwndEventsWnd) {
|
|
ShowWindow(g_hwndEventsWnd, SW_RESTORE);
|
|
}
|
|
|
|
RECT r;
|
|
|
|
if (g_hwndToolBar) {
|
|
//
|
|
// This is required to handle the case when we restore
|
|
// the window after minimizing and changing the theme
|
|
//
|
|
SendMessage(g_hwndToolBar, WM_SIZE, wParam, lParam);
|
|
SendMessage(g_hwndStatus, WM_SIZE, wParam, lParam);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// If the main app window is getting minimized the other modeless
|
|
// dialog boxes should also get minimized. We have to handled it this
|
|
// way because our modeless dialog boxes have the desktop as their parent
|
|
// This was needed so that we could tab between our main window and
|
|
// the other windows
|
|
//
|
|
if (g_hdlgSearchDB) {
|
|
ShowWindow(g_hdlgSearchDB, SW_MINIMIZE);
|
|
}
|
|
|
|
if (g_hdlgQueryDB) {
|
|
ShowWindow(g_hdlgQueryDB, SW_MINIMIZE);
|
|
}
|
|
|
|
if (g_hwndEventsWnd) {
|
|
ShowWindow(g_hwndEventsWnd, SW_MINIMIZE);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
|
|
SendMessage(hdlg, WM_COMMAND, (WPARAM)ID_FILE_EXIT, 0);
|
|
break;
|
|
|
|
case WM_PAINT:
|
|
|
|
DrawSplitter(hdlg);
|
|
return FALSE;
|
|
|
|
case WM_GETMINMAXINFO:
|
|
{
|
|
//
|
|
// Limit the minimum size of the app window
|
|
//
|
|
MINMAXINFO* pmmi = (MINMAXINFO*)lParam;
|
|
|
|
pmmi->ptMinTrackSize.x = 300;
|
|
pmmi->ptMinTrackSize.y = 300;
|
|
|
|
return 0;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
|
|
switch (wCode) {
|
|
|
|
case ID_FILE_MRU1:
|
|
case ID_FILE_MRU2:
|
|
case ID_FILE_MRU3:
|
|
case ID_FILE_MRU4:
|
|
case ID_FILE_MRU5:
|
|
|
|
HandleMRUActivation(wCode);
|
|
break;
|
|
|
|
case ID_FILE_OPEN:
|
|
|
|
OpenDatabaseFiles(hdlg);
|
|
break;
|
|
|
|
case ID_FIX_CREATEANEWAPPHELPMESSAGE:
|
|
|
|
g_bSomeWizardActive = TRUE;
|
|
CreateNewAppHelp();
|
|
g_bSomeWizardActive = FALSE;
|
|
break;
|
|
|
|
case ID_MODIFY_APPHELPMESSAGE:
|
|
|
|
g_bSomeWizardActive = TRUE;
|
|
ModifyAppHelp();
|
|
g_bSomeWizardActive = FALSE;
|
|
break;
|
|
|
|
case ID_FIX_CREATEANAPPLICATIONFIX:
|
|
|
|
g_bSomeWizardActive = TRUE;
|
|
CreateNewAppFix();
|
|
g_bSomeWizardActive = FALSE;
|
|
|
|
break;
|
|
|
|
case ID_FIX_EXECUTEAPPLICATION:
|
|
|
|
if (g_bAdmin == FALSE) {
|
|
//
|
|
// Non admins cannot do a test run because we need to invoke sdbinst.exe
|
|
// to install the test database and sdbinst.exe cannot be invoked if we
|
|
// do not have admin rights
|
|
//
|
|
MessageBox(hdlg,
|
|
GetString(IDS_ERRORNOTADMIN),
|
|
g_szAppName,
|
|
MB_ICONINFORMATION);
|
|
break;
|
|
}
|
|
|
|
if (g_pSelEntry) {
|
|
|
|
TestRun(g_pSelEntry, &g_pSelEntry->strFullpath, NULL, g_hDlg);
|
|
|
|
SetActiveWindow(hdlg);
|
|
SetFocus(hdlg);
|
|
}
|
|
|
|
break;
|
|
|
|
case ID_FIX_CHANGEENABLESTATUS:
|
|
|
|
if (g_bAdmin) {
|
|
ChangeEnableStatus();
|
|
} else {
|
|
//
|
|
// Non admins cannot change the enable-disable status
|
|
// because they do not have rights to HKLM reg key
|
|
//
|
|
MessageBox(hdlg,
|
|
GetString(IDS_ERRORNOTADMIN),
|
|
g_szAppName,
|
|
MB_ICONINFORMATION);
|
|
}
|
|
|
|
break;
|
|
|
|
case ID_MODIFY_APPLICATIONFIX:
|
|
|
|
g_bSomeWizardActive = TRUE;
|
|
ModifyAppFix();
|
|
g_bSomeWizardActive = FALSE;
|
|
break;
|
|
|
|
case ID_FIX_CREATENEWLAYER:
|
|
|
|
g_bSomeWizardActive = TRUE;
|
|
CreateNewLayer();
|
|
g_bSomeWizardActive = FALSE;
|
|
break;
|
|
|
|
case ID_FILE_SAVE:
|
|
{
|
|
BOOL bReturn;
|
|
|
|
if (g_pPresentDataBase &&
|
|
(g_pPresentDataBase->bChanged
|
|
|| NotCompletePath(g_pPresentDataBase->strPath))) {
|
|
|
|
//
|
|
// Error message will be displayed in the SaveDataBase func.
|
|
//
|
|
if (NotCompletePath(g_pPresentDataBase->strPath)) {
|
|
bReturn = SaveDataBaseAs(g_pPresentDataBase);
|
|
} else {
|
|
bReturn = SaveDataBase(g_pPresentDataBase, g_pPresentDataBase->strPath);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case ID_VIEW_EVENTS:
|
|
|
|
ShowEventsWindow();
|
|
break;
|
|
|
|
case ID_HELP_INDEX:
|
|
case ID_HELP_SEARCH:
|
|
case ID_HELP_TOPICS:
|
|
|
|
ShowHelp(hdlg, wCode);
|
|
break;
|
|
|
|
case ID_HELP_ABOUT:
|
|
|
|
ShellAbout(hdlg,
|
|
GetString(IDS_APPLICATION_NAME),
|
|
NULL,
|
|
LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_APPICON)));
|
|
break;
|
|
|
|
case ID_DATABASE_INSTALL_UNINSTALL:
|
|
|
|
DoInstallUnInstall();
|
|
break;
|
|
|
|
case ID_FILE_SAVEAS:
|
|
|
|
PreprocessForSaveAs(g_pPresentDataBase);
|
|
SaveDataBaseAs(g_pPresentDataBase);
|
|
break;
|
|
|
|
case IDCANCEL:
|
|
|
|
TreeView_EndEditLabelNow(DBTree.m_hLibraryTree, TRUE);
|
|
break;
|
|
|
|
case ID_FILE_EXIT:
|
|
|
|
TreeView_Expand(DBTree.m_hLibraryTree, DBTree.m_hItemGlobal, TVE_COLLAPSE);
|
|
|
|
if (!CloseAllDatabases()) {
|
|
//
|
|
// Select the database that the user refused to save. (Pressed CANCEL)
|
|
//
|
|
TreeView_SelectItem(DBTree.m_hLibraryTree, g_pPresentDataBase->hItemDB);
|
|
|
|
} else {
|
|
|
|
s_bProcessExiting = TRUE;
|
|
|
|
SaveDisplaySettings();
|
|
SaveMRUList();
|
|
|
|
CoUninitialize();
|
|
|
|
DestroyWindow(hdlg);
|
|
PostQuitMessage(0);
|
|
|
|
HtmlHelp(NULL, NULL, HH_CLOSE_ALL, 0) ;
|
|
HtmlHelp(NULL, NULL, HH_UNINITIALIZE, (DWORD)g_dwCookie);
|
|
|
|
g_hDlg = NULL;
|
|
|
|
#ifdef HELP_BOUND_CHECK
|
|
OnExitCleanup();
|
|
#endif
|
|
}
|
|
|
|
break;
|
|
|
|
case ID_EDIT_DELETE:
|
|
|
|
OnDelete();
|
|
break;
|
|
|
|
case ID_FILE_PROPERTIES:
|
|
|
|
DialogBoxParam(g_hInstance,
|
|
MAKEINTRESOURCE(IDD_DBPROPERTIES),
|
|
g_hDlg,
|
|
ShowDBPropertiesDlgProc,
|
|
(LPARAM)g_pPresentDataBase);
|
|
break;
|
|
|
|
case ID_FILE_NEW:
|
|
|
|
CreateNewDatabase();
|
|
break;
|
|
|
|
case ID_TOOLS_SEARCHFORFIXES:
|
|
{
|
|
if (g_hdlgSearchDB) {
|
|
//
|
|
// We must not allow more than one instance of
|
|
// the search window, because we use some global variables there.
|
|
// If it is already there set the focus to the search window
|
|
//
|
|
ShowWindow(g_hdlgSearchDB, SW_SHOWNORMAL);
|
|
SetFocus(g_hdlgSearchDB);
|
|
|
|
} else {
|
|
//
|
|
// This object is deleted in the WM_DESTROY of the search dialog box.
|
|
// Note that this has to be allocated on the heap, because this object
|
|
// is used in the UI for the search dialog, which is implemented as
|
|
// a modeless dialog box. So the lifetime of this object exceeds that
|
|
// of the block in which it is defined
|
|
//
|
|
CSearch* pSearch = new CSearch;
|
|
|
|
if (pSearch == NULL) {
|
|
MEM_ERR;
|
|
break;
|
|
}
|
|
|
|
pSearch->Begin();
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case ID_DATABASE_CLOSE:
|
|
|
|
OnDatabaseClose();
|
|
break;
|
|
|
|
case ID_SEARCH_QUERYDATABASE:
|
|
|
|
if (g_hdlgQueryDB) {
|
|
//
|
|
// We must not allow more than one instance of
|
|
// the query window, because we use some global variables there.
|
|
// If it is already there set the focus to the search window
|
|
//
|
|
ShowWindow(g_hdlgQueryDB, SW_SHOWNORMAL);
|
|
SetFocus(g_hdlgQueryDB);
|
|
|
|
} else {
|
|
|
|
HWND hwnd = CreateDialog(g_hInstance,
|
|
MAKEINTRESOURCE(IDD_QDB),
|
|
GetDesktopWindow(),
|
|
QueryDBDlg);
|
|
|
|
ShowWindow(hwnd, SW_SHOWNORMAL);
|
|
}
|
|
|
|
break;
|
|
|
|
case ID_DATABASE_CLOSEALL:
|
|
{
|
|
|
|
HTREEITEM hItemSelected = TreeView_GetSelection(DBTree.m_hLibraryTree);
|
|
|
|
//
|
|
// Note that it is possible that this function returns false.
|
|
// This will happen if the user presses CANCEL for "Database Saveas"
|
|
// dialog for some "untitled_x" database
|
|
//
|
|
if (!CloseAllDatabases()) {
|
|
//
|
|
// Select the database that the user refused to save.
|
|
//
|
|
TreeView_SelectItem(DBTree.m_hLibraryTree, g_pPresentDataBase->hItemDB);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case ID_DATABASE_SAVEALL:
|
|
|
|
DatabaseSaveAll();
|
|
break;
|
|
|
|
case ID_EDIT_SELECTALL:
|
|
|
|
if (g_bIsContentListVisible) {
|
|
ListViewSelectAll(g_hwndContentsList);
|
|
}
|
|
|
|
break;
|
|
|
|
case ID_EDIT_INVERTSELECTION:
|
|
|
|
if (g_bIsContentListVisible) {
|
|
ListViewInvertSelection(g_hwndContentsList);
|
|
}
|
|
|
|
break;
|
|
|
|
case ID_EDIT_CUT:
|
|
case ID_EDIT_COPY:
|
|
|
|
SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
CopyToClipBoard(wCode);
|
|
SetCursor(LoadCursor(NULL, IDC_ARROW));
|
|
break;
|
|
|
|
case ID_EDIT_PASTE:
|
|
{
|
|
//
|
|
// This variable is presently not used
|
|
//
|
|
g_bEntryConflictDonotShow = FALSE;
|
|
|
|
PasteFromClipBoard();
|
|
|
|
//
|
|
// Now we may need to refresh the contents list.
|
|
//
|
|
HTREEITEM hItem = TreeView_GetSelection(DBTree.m_hLibraryTree);
|
|
TYPE type = (TYPE)GetItemType(DBTree.m_hLibraryTree, hItem);
|
|
|
|
if (type == TYPE_GUI_APPS || type == TYPE_GUI_LAYERS || type == FIX_LAYER) {
|
|
PopulateContentsList(hItem);
|
|
SetStatusStringDBTree(hItem);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case ID_MODIFY_COMPATIBILITYMODE:
|
|
|
|
g_bSomeWizardActive = TRUE;
|
|
ModifyLayer();
|
|
g_bSomeWizardActive = FALSE;
|
|
break;
|
|
|
|
case ID_EDIT_RENAME:
|
|
|
|
OnRename();
|
|
break;
|
|
|
|
case ID_EDIT_CONFIGURELUA:
|
|
|
|
if (g_bAdmin == FALSE) {
|
|
MessageBox(hdlg, GetString(IDS_ERRORNOTADMIN), g_szAppName, MB_ICONINFORMATION);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Start the LUA Wizard.
|
|
//
|
|
if (g_pSelEntry == NULL) {
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
g_bSomeWizardActive = TRUE;
|
|
LuaBeginWizard(g_hDlg, g_pSelEntry, g_pPresentDataBase);
|
|
g_bSomeWizardActive = FALSE;
|
|
|
|
break;
|
|
|
|
case ID_VIEW_DESCRIPTION:
|
|
{
|
|
HMENU hMenu = GetMenu(g_hDlg);
|
|
HMENU hMenuView = GetSubMenu(hMenu, 2);
|
|
MENUITEMINFO mii = {0};
|
|
|
|
mii.cbSize = sizeof(mii);
|
|
mii.fMask = MIIM_STATE;
|
|
|
|
GetMenuItemInfo(hMenu, ID_VIEW_DESCRIPTION, FALSE, &mii);
|
|
|
|
if (mii.fState & MFS_CHECKED) {
|
|
mii.fState &= ~MFS_CHECKED;
|
|
} else {
|
|
mii.fState |= MFS_CHECKED;
|
|
}
|
|
|
|
g_bDescWndOn = !g_bDescWndOn;
|
|
|
|
SetMenuItemInfo(hMenu, ID_VIEW_DESCRIPTION, FALSE, &mii);
|
|
|
|
UpdateDescWindowStatus();
|
|
|
|
break;
|
|
}
|
|
|
|
case ID_VIEW_LOG:
|
|
|
|
if (g_bAdmin == FALSE) {
|
|
|
|
MessageBox(hdlg,
|
|
GetString(IDS_ERRORNOTADMIN),
|
|
g_szAppName,
|
|
MB_ICONINFORMATION);
|
|
break;
|
|
}
|
|
|
|
ShowShimLog();
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int
|
|
WINAPI
|
|
WinMain(
|
|
IN HINSTANCE hInstance,
|
|
IN HINSTANCE hPrevInstance,
|
|
IN LPSTR lpCmdLine,
|
|
IN int nCmdShow
|
|
)
|
|
/*++
|
|
WinMain
|
|
|
|
Desc: The WinMain
|
|
|
|
Params: Standard WinMain params
|
|
IN HINSTANCE hInstance
|
|
IN HINSTANCE hPrevInstance
|
|
IN LPSTR lpCmdLine
|
|
IN int nCmdShow
|
|
|
|
Return: Standard WinMain return
|
|
--*/
|
|
{
|
|
HINSTANCE hmodRichEdit = NULL;
|
|
TCHAR szLibPath[MAX_PATH * 2];
|
|
BOOL bIsGuest = TRUE;
|
|
UINT uResult = 0;
|
|
|
|
//
|
|
// Let the system handle data misalignment on Itanium.
|
|
//
|
|
SetErrorMode(SEM_NOALIGNMENTFAULTEXCEPT);
|
|
|
|
*szLibPath = 0;
|
|
|
|
uResult = GetSystemDirectory(szLibPath, MAX_PATH);
|
|
|
|
if (uResult == 0 || uResult >= MAX_PATH) {
|
|
assert(FALSE);
|
|
Dbg(dlError, "WinMain", "GetSytemDirectory failed");
|
|
//
|
|
// We do NOT eject out from here as this is not critical
|
|
//
|
|
} else {
|
|
ADD_PATH_SEPARATOR(szLibPath, ARRAYSIZE(szLibPath));
|
|
|
|
StringCchCat(szLibPath, ARRAYSIZE(szLibPath), TEXT("RICHED32.DLL"));
|
|
|
|
hmodRichEdit = LoadLibrary(szLibPath);
|
|
}
|
|
|
|
//
|
|
// The App Name
|
|
//
|
|
LoadString(g_hInstance, IDS_COMPATADMIN, g_szAppName, ARRAYSIZE(g_szAppName));
|
|
|
|
if (!IsAdmin(&bIsGuest)) {
|
|
|
|
if (bIsGuest) {
|
|
//
|
|
// Ok, guests cannot run compatadmin.
|
|
//
|
|
MessageBox(NULL, GetString(IDS_GUEST), g_szAppName, MB_ICONINFORMATION);
|
|
goto End;
|
|
}
|
|
|
|
//
|
|
// Not an admin, some features are disabled. Cannot do a test run and install databases
|
|
//
|
|
MessageBox(NULL, GetString(IDS_NEEDTOBEADMIN), g_szAppName, MB_ICONINFORMATION);
|
|
g_bAdmin = FALSE;
|
|
}
|
|
|
|
g_hInstance = hInstance;
|
|
|
|
INITCOMMONCONTROLSEX icex;
|
|
|
|
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
|
|
icex.dwICC = ICC_LISTVIEW_CLASSES | ICC_TREEVIEW_CLASSES | ICC_TAB_CLASSES | ICC_INTERNET_CLASSES | ICC_BAR_CLASSES ;
|
|
|
|
if (!InitCommonControlsEx(&icex)) {
|
|
InitCommonControls();
|
|
}
|
|
|
|
//
|
|
// Check if the OS is Win2k
|
|
//
|
|
OSVERSIONINFOEX osvi;
|
|
|
|
ZeroMemory(&osvi, sizeof (OSVERSIONINFOEX));
|
|
|
|
g_fSPGreaterThan2 = FALSE;
|
|
|
|
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
|
GetVersionEx((LPOSVERSIONINFO)&osvi);
|
|
|
|
if ((osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion == 0)) {
|
|
|
|
g_bWin2K = TRUE;
|
|
|
|
if (osvi.wServicePackMajor > 2) {
|
|
g_fSPGreaterThan2 = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Attempt to locate the SDB in the AppPatch directory.
|
|
//
|
|
if (!CheckForSDB()) {
|
|
|
|
if (g_bWin2K) {
|
|
|
|
DialogBoxParam(g_hInstance,
|
|
MAKEINTRESOURCE(IDD_MSGBOX_SDB),
|
|
NULL,
|
|
MsgBoxDlgProc,
|
|
(LPARAM)0);
|
|
goto End;
|
|
|
|
} else {
|
|
|
|
MessageBox(GetDesktopWindow(),
|
|
GetString(IDS_XP_NO_SDB),
|
|
g_szAppName,
|
|
MB_OK | MB_ICONEXCLAMATION);
|
|
|
|
goto End;
|
|
}
|
|
}
|
|
|
|
if (g_bWin2K && !CheckProperSP()) {
|
|
|
|
DialogBoxParam(g_hInstance,
|
|
MAKEINTRESOURCE(IDD_MSGBOX_SDB),
|
|
GetDesktopWindow(),
|
|
MsgBoxDlgProc,
|
|
(LPARAM)0);
|
|
goto End;
|
|
}
|
|
|
|
CoInitialize(NULL);
|
|
|
|
g_hAccelerator = LoadAccelerators(g_hInstance, MAKEINTRESOURCE(IDR_ACCL));
|
|
|
|
MSG msg ;
|
|
WNDCLASS wndclass ;
|
|
|
|
wndclass.style = CS_HREDRAW | CS_VREDRAW;
|
|
wndclass.lpfnWndProc = (WNDPROC)CompatAdminDlgProc;
|
|
wndclass.cbClsExtra = 0;
|
|
wndclass.cbWndExtra = 0;
|
|
wndclass.hInstance = g_hInstance;
|
|
wndclass.hIcon = NULL;
|
|
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
|
|
wndclass.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
|
|
wndclass.lpszMenuName = NULL;
|
|
wndclass.lpszClassName = g_szAppName;
|
|
|
|
if (!RegisterClass (&wndclass)) {
|
|
|
|
MessageBox (NULL,
|
|
GetString(IDS_INVALIDOS),
|
|
g_szAppName,
|
|
MB_ICONERROR) ;
|
|
|
|
goto End;
|
|
}
|
|
|
|
g_hDlg = CreateDialog(g_hInstance,
|
|
MAKEINTRESOURCE(IDD_DIALOG),
|
|
GetDesktopWindow(),
|
|
CompatAdminDlgProc);
|
|
|
|
ShowWindow(g_hDlg, SW_SHOWNORMAL);
|
|
|
|
BOOL bProcessMessage = TRUE;
|
|
HWND hwndActiveWindow;
|
|
|
|
//try {
|
|
|
|
while (GetMessage(&msg, NULL, 0, 0) > 0) {
|
|
|
|
bProcessMessage = TRUE;
|
|
|
|
if (g_hdlgSearchDB && IsDialogMessage(g_hdlgSearchDB, &msg)) {
|
|
//
|
|
// This is a message for the Search modeless dialog box
|
|
//
|
|
bProcessMessage = FALSE;
|
|
|
|
} else if (g_hdlgQueryDB && IsDialogMessage(g_hdlgQueryDB, &msg)) {
|
|
//
|
|
// This is a message for the Query modeless dialog box
|
|
//
|
|
bProcessMessage = FALSE;
|
|
|
|
} else if (g_hwndEventsWnd && IsDialogMessage(g_hwndEventsWnd, &msg)) {
|
|
//
|
|
// This is a message for the Events modeless dialog box
|
|
//
|
|
bProcessMessage = FALSE;
|
|
}
|
|
|
|
if (bProcessMessage) {
|
|
//
|
|
// Check if we have the text box for in-place renaming. If yes, we must
|
|
// not call TranslateAccelerator() for the main app window.
|
|
//
|
|
if (GetFocus() == g_hwndRichEdit) {
|
|
//
|
|
// If we are on the rich edit control, we want to be able to do copy
|
|
// but also we want the tabs to be processed by IsDialogMessage()
|
|
//
|
|
if (g_hDlg && IsDialogMessage(g_hDlg, &msg)) {
|
|
//
|
|
// It was possibly a tab or a shift-tab
|
|
//
|
|
continue;
|
|
} else {
|
|
//
|
|
// Process all other rich edit messages
|
|
//
|
|
goto Translate;
|
|
}
|
|
}
|
|
|
|
if (!g_hwndEditText) {
|
|
|
|
if (TranslateAccelerator(g_hDlg, g_hAccelerator, &msg)) {
|
|
//
|
|
// Accelerator for the main window, do not process this message.
|
|
//
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Process the tabs for the main window
|
|
//
|
|
if (g_hDlg && IsDialogMessage(g_hDlg, &msg)) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
Translate:
|
|
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
|
|
/*} catch (...) {
|
|
|
|
MessageBox(GetDesktopWindow(),
|
|
GetString(IDS_EXCEPTION),
|
|
g_szAppName,
|
|
MB_ICONERROR);
|
|
}*/
|
|
|
|
End:
|
|
if (hmodRichEdit) {
|
|
FreeLibrary(hmodRichEdit);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
BOOL
|
|
HandleFirstEntryofAppDeletion(
|
|
IN PDATABASE pDataBase,
|
|
IN PDBENTRY pApp,
|
|
IN BOOL bDelete
|
|
)
|
|
/*++
|
|
HandleFirstEntryofAppDeletion
|
|
|
|
Desc: Handles the special case, when the entry being deleted because of a cut
|
|
or a delete is the first entry for the application
|
|
|
|
Params:
|
|
IN PDATABASE pDataBase: The database
|
|
IN PDBENTRY pApp: The application whose first enty is being deleted
|
|
IN BOOL bDelete: TRUE, if this function has been called because of
|
|
a delete, FALSE if this function has been called because of a cut.If we
|
|
are removing the entry because of a cut,
|
|
then we should not change the value of g_pEntrySelApp. If bCut is true that
|
|
means focus is on some other app (may be in same diff. database) and we should
|
|
be changing any focus etc.
|
|
|
|
Return:
|
|
FALSE: There was some error
|
|
TRUE: Otherwise
|
|
|
|
Notes: This is a special case because when we are deleting the first entry of an
|
|
app, we have to check whether this is the only entry and if yes, then we might
|
|
need to delete the entire app from the db tree.
|
|
Otherwise: The app tree item in the db tree contain a pointer to the first entry
|
|
of the app in its lParam, this will need to be modified to point to the next item
|
|
--*/
|
|
{
|
|
if (pApp == NULL || pDataBase == NULL) {
|
|
assert(FALSE);
|
|
Dbg(dlError, "[HandleFirstEntryofAppDeletion] Invalid Arguments");
|
|
return FALSE;
|
|
}
|
|
|
|
HTREEITEM hItemApp = DBTree.FindChild(pDataBase->hItemAllApps, (LPARAM)pApp);
|
|
PDBENTRY pEntryTemp = pDataBase->pEntries;
|
|
PDBENTRY pEntryPrev = NULL;
|
|
|
|
//
|
|
// Find the App Prev. to the one whose first entry is being deleted
|
|
//
|
|
while (pEntryTemp) {
|
|
|
|
if (pEntryTemp == pApp) {
|
|
break;
|
|
}
|
|
|
|
pEntryPrev = pEntryTemp;
|
|
pEntryTemp = pEntryTemp->pNext;
|
|
}
|
|
|
|
if (pEntryPrev == NULL) {
|
|
//
|
|
// First Entry and first app.
|
|
//
|
|
if (pApp->pSameAppExe) {
|
|
pDataBase->pEntries = pApp->pSameAppExe;
|
|
pApp->pSameAppExe->pNext = pApp->pNext;
|
|
} else {
|
|
pDataBase->pEntries = pApp->pNext;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// The next pointer of the app previous to the pApp should now point
|
|
// to the second entry for pApp as we are removing the first entry of pApp.
|
|
// if pApp has only one entry then we should make sure that the next pointer
|
|
// to the app prev to pApp should point to the next app after pApp
|
|
//
|
|
if (pApp->pSameAppExe) {
|
|
pEntryPrev->pNext = pApp->pSameAppExe;
|
|
pApp->pSameAppExe->pNext = pApp->pNext;
|
|
} else {
|
|
pEntryPrev->pNext = pApp->pNext;
|
|
}
|
|
}
|
|
|
|
if (pApp->pSameAppExe == NULL) {
|
|
//
|
|
// This was the only entry. We have to delete the application from the database tree
|
|
//
|
|
g_pEntrySelApp = NULL;
|
|
|
|
DBTree.DeleteAppLayer(pDataBase, TRUE, hItemApp);
|
|
|
|
--pDataBase->uAppCount;
|
|
|
|
} else {
|
|
//
|
|
// We have to set the lParam of the app properly
|
|
//
|
|
DBTree.SetLParam(hItemApp, (LPARAM)pApp->pSameAppExe);
|
|
|
|
if (bDelete) {
|
|
//
|
|
// If the deletion is because of a cut do not modify g_pEntrySelApp.
|
|
// Because focus is on some other app (may be in some other database) and
|
|
// we do not want to change the present active app pointer.
|
|
// Please note that we do the actual deletion part of a cut-paste after we
|
|
// have done the paste. So when this routine gets called because of a cut,
|
|
// then we already have focus on the newly pasted item and g_pEntrySelApp
|
|
// will be set to the first entry of the app in which the entry was pasted
|
|
// This is correct, verified and needed. Do not change this.
|
|
//
|
|
g_pEntrySelApp = pApp->pSameAppExe;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
GetDescriptionString(
|
|
IN LPARAM itemlParam,
|
|
OUT CSTRING& strDesc,
|
|
IN HWND hwndToolTip,
|
|
IN PCTSTR pszCaption, // (NULL)
|
|
IN HTREEITEM hItem, // (NULL)
|
|
IN HWND hwnd, // (NULL)
|
|
IN INT iListViewIndex // (-1)
|
|
)
|
|
/*++
|
|
GetDescriptionString
|
|
|
|
Desc: Gets the description for the tree item hItem in tree hwnd or for the element
|
|
at index iListViewIndex in list view hwnd.
|
|
|
|
Params:
|
|
IN LPARAM itemlParam: In case we are using a list view, the lParam of the item
|
|
OUT CSTRING& strDesc: The description will be stored in this
|
|
IN HWND hwndToolTip: In case the description has to be shown in a tool-tip
|
|
the handle to the tool tip window
|
|
|
|
IN PCTSTR pszCaption (NULL): In case the description has to be shown in a tool-tip
|
|
the caption of the tool-tip window
|
|
|
|
IN HTREEITEM hItem (NULL): If we want to get the description of a tree item, the handle
|
|
to the tree item.
|
|
|
|
IN HWND hwnd (NULL): The handle to the list view or the tree view
|
|
IN INT iListViewIndex (-1): In case we are using a list view, the index of the item
|
|
for which we want to get the description
|
|
|
|
Notes: If we are calling this routine for a list view, we must set correct iListViewIndex
|
|
and itemlParam.
|
|
itemlParam is ignored if we calling this routine for a tree view
|
|
--*/
|
|
{
|
|
TYPE type;
|
|
LPARAM lParam = itemlParam;
|
|
|
|
strDesc = TEXT("");
|
|
|
|
if (hItem) {
|
|
|
|
if (hwnd == NULL) {
|
|
assert(FALSE);
|
|
goto End;
|
|
}
|
|
|
|
type = GetItemType(hwnd, hItem);
|
|
CTree::GetLParam(hwnd, hItem, &lParam);
|
|
|
|
} else {
|
|
|
|
if (iListViewIndex != -1) {
|
|
|
|
if (ListView_GetItemState(hwnd, iListViewIndex, LVIS_SELECTED) != LVIS_SELECTED) {
|
|
|
|
if (hwndToolTip) {
|
|
SendMessage(hwndToolTip, TTM_SETTITLE, 0, (LPARAM)NULL);
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (lParam) {
|
|
type = ConvertLparam2Type(lParam);
|
|
} else {
|
|
assert(FALSE);
|
|
}
|
|
}
|
|
|
|
switch (type) {
|
|
case FIX_LIST_SHIM:
|
|
|
|
if (lParam) {
|
|
lParam = (LPARAM)((PSHIM_FIX_LIST)lParam)->pShimFix;
|
|
}
|
|
//
|
|
// CAUTION: case FIX_LIST_SHIM and FIX_SHIM should be one after another.
|
|
// NO break; The next FIX_SHIM case will now handle this.
|
|
//
|
|
case FIX_SHIM:
|
|
{
|
|
PSHIM_FIX pFix = (PSHIM_FIX)lParam;
|
|
|
|
if (pFix == NULL) {
|
|
assert(FALSE);
|
|
goto End;
|
|
}
|
|
|
|
strDesc = (pFix->strDescription.Length() > 0) ? pFix->strDescription : TEXT("");
|
|
break;
|
|
}
|
|
|
|
case FIX_LIST_FLAG:
|
|
|
|
if (lParam) {
|
|
lParam = (LPARAM)((PFLAG_FIX_LIST)lParam)->pFlagFix;
|
|
}
|
|
|
|
//
|
|
// CAUTION: case FIX_LIST_FLAG and FIX_FLAG should be one after another.
|
|
// NO break; The next FIX_FLAG case will now handle this.
|
|
//
|
|
case FIX_FLAG:
|
|
{
|
|
PFLAG_FIX pFix = (PFLAG_FIX)lParam;
|
|
|
|
if (pFix == NULL) {
|
|
assert(FALSE);
|
|
goto End;
|
|
}
|
|
|
|
strDesc = (pFix->strDescription.Length() > 0) ? pFix->strDescription : TEXT("");
|
|
break;
|
|
}
|
|
|
|
case FIX_PATCH:
|
|
{
|
|
PPATCH_FIX pFix = (PPATCH_FIX)lParam;
|
|
|
|
if (pFix == NULL) {
|
|
assert(FALSE);
|
|
goto End;
|
|
}
|
|
|
|
strDesc = (pFix->strDescription.Length() > 0) ? pFix->strDescription : TEXT("");
|
|
break;
|
|
}
|
|
|
|
case FIX_LAYER:
|
|
{
|
|
PLAYER_FIX pFix = (PLAYER_FIX)lParam;
|
|
|
|
if (pFix == NULL) {
|
|
assert(FALSE);
|
|
goto End;
|
|
}
|
|
|
|
strDesc = TEXT("");
|
|
break;
|
|
}
|
|
|
|
case TYPE_APPHELP_ENTRY:
|
|
{
|
|
PAPPHELP pAppHelp = (PAPPHELP)lParam;
|
|
|
|
if (pAppHelp == NULL) {
|
|
break;
|
|
}
|
|
|
|
if (g_pPresentDataBase->type == DATABASE_TYPE_GLOBAL) {
|
|
|
|
//
|
|
// The AppHelp for the system database has to be picked up. This is not
|
|
// kept in the sysmain.sdb.
|
|
// Also we do not keep this in the database data structure.
|
|
// The apphelp messages for custom are kept in the .sdb and also they
|
|
// are kept in the database data structure.
|
|
//
|
|
PDB pdbAppHelp = SdbOpenApphelpDetailsDatabase(NULL);
|
|
|
|
APPHELP_DATA AppHelpData;
|
|
|
|
ZeroMemory(&AppHelpData, sizeof(APPHELP_DATA));
|
|
|
|
if (g_pSelEntry == NULL) {
|
|
break;
|
|
}
|
|
|
|
AppHelpData.dwHTMLHelpID = g_pSelEntry->appHelp.HTMLHELPID;
|
|
|
|
if (pdbAppHelp == NULL) {
|
|
strDesc = TEXT("");
|
|
break;
|
|
}
|
|
|
|
if (!SdbReadApphelpDetailsData(pdbAppHelp, &AppHelpData)) {
|
|
strDesc = TEXT("");
|
|
break;
|
|
}
|
|
|
|
strDesc.Sprintf(TEXT("%s %s"), CSTRING(IDS_DESC_APPHELP1).pszString, AppHelpData.szDetails);
|
|
|
|
if (AppHelpData.szURL) {
|
|
strDesc.Strcat(CSTRING(IDS_DESC_APPHELP2));
|
|
strDesc.Strcat(AppHelpData.szURL);
|
|
}
|
|
|
|
if (pdbAppHelp) {
|
|
SdbCloseDatabase(pdbAppHelp);
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// This is a custom database and we have the apphelp message loaded with the
|
|
// database
|
|
//
|
|
strDesc.Sprintf(TEXT("%s %s"), CSTRING(IDS_DESC_APPHELP1).pszString, pAppHelp->strMessage);
|
|
|
|
if (pAppHelp->strURL.Length()) {
|
|
strDesc.Strcat(CSTRING(IDS_DESC_APPHELP2));
|
|
strDesc.Strcat(pAppHelp->strURL);
|
|
}
|
|
}
|
|
|
|
if (g_pSelEntry == NULL) {
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
switch (g_pSelEntry->appHelp.severity) {
|
|
case APPTYPE_INC_HARDBLOCK:
|
|
|
|
strDesc.Strcat(CSTRING(IDS_APPHELP_DESC_HARDBLOCK));
|
|
break;
|
|
|
|
case APPTYPE_NONE:
|
|
|
|
strDesc.Strcat(CSTRING(IDS_APPHELP_DESC_NONE));
|
|
break;
|
|
|
|
default:
|
|
|
|
strDesc.Strcat(CSTRING(IDS_APPHELP_DESC_SOFTBLOCK));
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
|
|
strDesc = TEXT("");
|
|
break;
|
|
}
|
|
|
|
if (strDesc.Length() && pszCaption && hwndToolTip) {
|
|
//
|
|
// We have a valid caption, so set it only if we have a tooltip
|
|
//
|
|
SendMessage(hwndToolTip, TTM_SETTITLE, 1, (LPARAM)pszCaption);
|
|
} else if (hwndToolTip) {
|
|
//
|
|
// We do not have a caption, so tell the tool-tip that
|
|
// we do not want to have any caption
|
|
//
|
|
SendMessage(hwndToolTip, TTM_SETTITLE, 0, (LPARAM)NULL);
|
|
}
|
|
|
|
End:
|
|
return;
|
|
}
|
|
|
|
|
|
INT
|
|
ShowMainEntries(
|
|
IN HWND hdlg
|
|
)
|
|
/*++
|
|
|
|
ShowMainEntries
|
|
|
|
Desc: Loads the PDBENTRY elements for the system database
|
|
|
|
Params:
|
|
IN HWND hdlg: The main app window
|
|
|
|
Return:
|
|
-1: Some other thread has called this function and is not done as yet
|
|
0: There was some error
|
|
1: Either we loaded it successfully or we already had the main entries loaded
|
|
|
|
Warn: No two threads should call this function at the same time
|
|
|
|
Note: We can call ShowMainEntries() from a thread in query database modeless
|
|
dialog box. When we are doing the query we disable the main dialog box. We still
|
|
have protection using critical sections here.
|
|
|
|
When we call ShowMainEntries() from the main window, then we will not be able
|
|
to invoke the search or query dialog because the main thread will be busy and no
|
|
messages will be processed
|
|
--*/
|
|
{
|
|
INT iOk = 1;
|
|
BOOL bReturn = FALSE;
|
|
PDATABASE pOldPresentDatabase = NULL;
|
|
|
|
EnterCriticalSection(&s_csExpanding);
|
|
|
|
if (g_bExpanding) {
|
|
//
|
|
// Some other thread is already trying to load the system database
|
|
//
|
|
LeaveCriticalSection(&s_csExpanding);
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
g_bExpanding = TRUE;
|
|
}
|
|
|
|
LeaveCriticalSection(&s_csExpanding);
|
|
|
|
EnterCriticalSection(&g_critsectShowMain);
|
|
|
|
if (g_bMainAppExpanded) {
|
|
goto End;
|
|
}
|
|
|
|
pOldPresentDatabase = g_pPresentDataBase;
|
|
|
|
bReturn = GetDatabaseEntries(NULL, &GlobalDataBase);
|
|
|
|
//
|
|
// GetDatabaseEntries will change the g_pPresentDataBase, so revert back
|
|
//
|
|
g_pPresentDataBase = pOldPresentDatabase;
|
|
|
|
if (bReturn == FALSE) {
|
|
|
|
MessageBox(hdlg, CSTRING(IDS_ERROROPEN), g_szAppName, MB_ICONERROR);
|
|
iOk = 0;
|
|
goto End;
|
|
}
|
|
|
|
//
|
|
// Delete the dummy node
|
|
//
|
|
HTREEITEM hItemTemp = TreeView_GetChild(DBTree.m_hLibraryTree,
|
|
GlobalDataBase.hItemAllApps);
|
|
|
|
if (hItemTemp) {
|
|
TreeView_DeleteItem(DBTree.m_hLibraryTree, hItemTemp);
|
|
}
|
|
|
|
//
|
|
// Load only the apps as the lib has already been loaded when the system started
|
|
//
|
|
DBTree.PopulateLibraryTree(GlobalDataBase.hItemAllApps, &GlobalDataBase, FALSE, TRUE);
|
|
|
|
g_bMainAppExpanded = TRUE;
|
|
SetCaption();
|
|
|
|
End:
|
|
LeaveCriticalSection(&g_critsectShowMain);
|
|
|
|
EnterCriticalSection(&s_csExpanding);
|
|
g_bExpanding = FALSE;
|
|
LeaveCriticalSection(&s_csExpanding);
|
|
|
|
SetStatus(g_hwndStatus, CSTRING(IDS_MAINLOADED));
|
|
|
|
if (g_hdlgSearchDB) {
|
|
//
|
|
// If somebody clicked on the search modeless window while we were loading the
|
|
// system db then the search window will show in its status message that we are loading the system db, we need
|
|
// to change the status message when we are done
|
|
//
|
|
SetStatus(GetDlgItem(g_hdlgSearchDB, IDC_STATUSBAR), GetString(IDS_SEARCHCOMPLETE));
|
|
}
|
|
|
|
return iOk;
|
|
}
|
|
|
|
void
|
|
DrawSplitter(
|
|
IN HWND hdlg
|
|
)
|
|
/*++
|
|
DrawSplitter
|
|
|
|
Desc: Draws the vertical. split bar.
|
|
We call this function when we are moving the mouse with the LBUTTON pressed
|
|
and the mouse button was initially pressed on top of the split bar
|
|
|
|
We also call this function when we need to position the
|
|
vert. split bar when we start up and have to position the split bar
|
|
as it was during the last session
|
|
|
|
Params:
|
|
IN HWND hdlg: The main dialog box.
|
|
--*/
|
|
{
|
|
RECT rectDBTree, rectEntryTree;
|
|
PAINTSTRUCT ps;
|
|
HDC hDC;
|
|
|
|
GetWindowRect(GetDlgItem(hdlg, IDC_LIBRARY), &rectDBTree);
|
|
MapWindowPoints(NULL, hdlg, (LPPOINT)&rectDBTree, 2);
|
|
|
|
GetWindowRect(GetDlgItem(hdlg, IDC_ENTRY), &rectEntryTree);
|
|
MapWindowPoints(NULL, hdlg, (LPPOINT)&rectEntryTree, 2);
|
|
|
|
hDC = BeginPaint(hdlg, &ps);
|
|
|
|
RECT rectDraw, rectBar;
|
|
|
|
if (!g_bDrag) {
|
|
//
|
|
// The mouse is not being dragged. We also call this function when we need to
|
|
// position the vert. split bar when we start up and have to position the split bar
|
|
// as it was during the last session
|
|
//
|
|
rectBar.left = rectDBTree.right + 1;
|
|
rectBar.top = rectDBTree.top;
|
|
rectBar.right = rectEntryTree.left - 1;
|
|
rectBar.bottom = rectDBTree.bottom;
|
|
|
|
} else {
|
|
//
|
|
// The mouse is being dragged
|
|
//
|
|
rectBar = g_rectBar;
|
|
}
|
|
|
|
//
|
|
// Fill the entire with gray. This is required because otherwise we had
|
|
// to draw a rectangle, and change the default pen before this.
|
|
// This would have painted the inside with the default brush.
|
|
//
|
|
SetRect(&rectDraw, rectBar.left - 1, rectBar.top, rectBar.right + 1, rectBar.bottom);
|
|
FillRect(hDC, &rectDraw, (HBRUSH)GetStockObject(GRAY_BRUSH));
|
|
|
|
SetRect(&rectDraw, rectBar.left - 1, rectBar.top + 1, rectBar.left, rectBar.bottom - 1);
|
|
FillRect(hDC, &rectDraw, (HBRUSH)(HBRUSH)GetStockObject(WHITE_BRUSH));
|
|
|
|
SetRect(&rectDraw, rectBar.left, rectBar.top + 1, rectBar.right, rectBar.bottom - 1);
|
|
FillRect(hDC, &rectDraw, (HBRUSH)(HBRUSH)GetStockObject(LTGRAY_BRUSH));
|
|
|
|
EndPaint(hdlg, &ps);
|
|
}
|
|
|
|
BOOL
|
|
CloseAllDatabases(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
CloseAllDatabases
|
|
|
|
Desc: Closes all the working databases
|
|
|
|
Return:
|
|
TRUE: All the databases were not closed
|
|
FALSE: All the databases were not closed, either because of some error
|
|
Or the user selected cancel on the prompt for some unsaved database
|
|
--*/
|
|
{
|
|
PDATABASE pDatabase = DataBaseList.pDataBaseHead;
|
|
|
|
while (pDatabase) {
|
|
|
|
HTREEITEM hItemDB = pDatabase->hItemDB;
|
|
|
|
if (pDatabase->bChanged) {
|
|
TreeView_SelectItem(DBTree.m_hLibraryTree, hItemDB);
|
|
}
|
|
|
|
if (!CloseDataBase(pDatabase)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The DataBaseList.pDataBaseHead now points to the next database
|
|
//
|
|
pDatabase = DataBaseList.pDataBaseHead;
|
|
}
|
|
|
|
if (pDatabase != NULL) {
|
|
//
|
|
// Was Cancel pressed in between.
|
|
//
|
|
g_pPresentDataBase = pDatabase;
|
|
TreeView_SelectItem(DBTree.m_hLibraryTree, pDatabase->hItemDB);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
INT_PTR
|
|
CALLBACK
|
|
HandleConflictEntry(
|
|
IN HWND hdlg,
|
|
IN UINT uMsg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
HandleConflictEntry
|
|
|
|
Desc: The handler for the entry conflict dialog box
|
|
|
|
Params: Standard dialog handler parameters
|
|
|
|
IN HWND hDlg
|
|
IN UINT uMsg
|
|
IN WPARAM wParam
|
|
IN LPARAM lParam
|
|
|
|
Return: Standard dialog handler return
|
|
--*/
|
|
{
|
|
switch (uMsg) {
|
|
case WM_INITDIALOG:
|
|
|
|
CenterWindow(g_hDlg, hdlg);
|
|
SetDlgItemText(hdlg, IDC_CONFLICT_MSG, (TCHAR*)lParam);
|
|
break;
|
|
|
|
|
|
case WM_COMMAND:
|
|
|
|
switch (LOWORD (wParam)) {
|
|
|
|
case IDNO:
|
|
case IDCANCEL:
|
|
case IDYES:
|
|
|
|
EndDialog(hdlg, LOWORD (wParam));
|
|
break;
|
|
|
|
default: return FALSE;
|
|
}
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
TYPE
|
|
ConvertLparam2Type(
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
|
|
ConvertLparam2Type
|
|
|
|
Desc: Converts the lParam to a TYPE
|
|
|
|
Params:
|
|
IN LPARAM lParam: The lParam that has to be converted
|
|
|
|
Return: Converts the lParam to a TYPE
|
|
|
|
Warn: Do not use this routine for lParams of the entry tree.
|
|
The entry tree has attribute items whose lParams are the index in a bit array.
|
|
So for them we see if the parent has type TYPE_MATCHIING_FILE and handle them
|
|
differently.
|
|
|
|
For trees, GetItemType() instead.
|
|
--*/
|
|
{
|
|
if (lParam > TYPE_NULL) {
|
|
|
|
PDS_TYPE pds = (PDS_TYPE)lParam;
|
|
|
|
if (pds) {
|
|
return (pds->type);
|
|
} else {
|
|
assert(FALSE);
|
|
}
|
|
}
|
|
|
|
return (TYPE)lParam;
|
|
}
|
|
|
|
TYPE
|
|
ConvertLpVoid2Type(
|
|
IN LPVOID lpVoid
|
|
)
|
|
/*++
|
|
|
|
ConvertLpVoid2Type
|
|
|
|
Desc: Converts the lpVoid to a TYPE
|
|
|
|
Params:
|
|
IN LPVOID lpVoid: The lpVoid that has to be converted
|
|
|
|
Return: Converts the lpVoid to a TYPE
|
|
|
|
Warn: Do not use this routine for lParams of the entry tree.
|
|
The entry tree has attribute items whose lParams are the index in a bit array.
|
|
So for them we see if the parent has type TYPE_MATCHIING_FILE and handle them
|
|
differently.
|
|
|
|
For trees, use GetItemType() instead.
|
|
--*/
|
|
{
|
|
if ((LPARAM)lpVoid > TYPE_NULL) {
|
|
|
|
PDS_TYPE pds = (PDS_TYPE)lpVoid;
|
|
|
|
if (pds) {
|
|
return (pds->type);
|
|
} else {
|
|
assert(FALSE);
|
|
}
|
|
}
|
|
|
|
return (TYPE)(LPARAM)lpVoid;
|
|
}
|
|
|
|
|
|
TYPE
|
|
GetItemType(
|
|
IN HWND hwndTree,
|
|
IN HTREEITEM hItem
|
|
)
|
|
/*++
|
|
Desc: For the HTREEITEM hItem in tree view hwndTree, finds the data type of the item
|
|
For the list of possible data types see CompatAdmin.h
|
|
|
|
Params:
|
|
IN HWND hwndTree: Handle to the tree view, should be one of
|
|
g_hwndTree or DBTree.m_hLibraryTree
|
|
|
|
IN HTREEITEM hItem: The HTREITEM whose type we need to find
|
|
|
|
Notes: The type can be either a GUI type or a data structure type or attribute type
|
|
(If it is a child of a matching file, applicable only to entry tree[RHS])
|
|
|
|
This routine should be the preferred method for getting the type of tree items,
|
|
over Convert*2Type routines, which should be used for the list view items
|
|
--*/
|
|
{
|
|
TVITEM Item;
|
|
|
|
Item.mask = TVIF_PARAM;
|
|
Item.hItem = hItem;
|
|
|
|
if (!TreeView_GetItem(hwndTree, &Item)) {
|
|
return TYPE_UNKNOWN;
|
|
}
|
|
|
|
HTREEITEM hItemParent = TreeView_GetParent(hwndTree, hItem);
|
|
|
|
if (hItemParent != NULL) {
|
|
|
|
if (GetItemType(hwndTree, hItemParent) == TYPE_MATCHING_FILE) {
|
|
return TYPE_MATCHING_ATTRIBUTE;
|
|
}
|
|
}
|
|
|
|
if (Item.lParam > TYPE_NULL) {
|
|
|
|
PDS_TYPE pds = (PDS_TYPE)Item.lParam;
|
|
|
|
if (pds) {
|
|
return (pds->type);
|
|
} else {
|
|
assert(FALSE);
|
|
}
|
|
}
|
|
|
|
return (TYPE)Item.lParam;
|
|
}
|
|
|
|
void
|
|
DoTheCutEntryTree(
|
|
IN PDATABASE pDataBase,
|
|
IN TYPE type,
|
|
IN SOURCE_TYPE SourceType,
|
|
IN LPVOID lpData,
|
|
IN HTREEITEM hItem,
|
|
IN BOOL bDelete
|
|
)
|
|
/*++
|
|
|
|
DoTheCutEntryTree
|
|
|
|
Desc: This function does the cut part for the entry tree (LHS).
|
|
This routine is also called when we want to delete.
|
|
|
|
Params:
|
|
IN PDATABASE pDataBase: The database where we are doing a cut/delete
|
|
IN TYPE type: The type of the element that has to be deleted
|
|
IN SOURCE_TYPE SourceType: Where the cut/delete was performed. Always: ENTRY_TREE
|
|
IN LPVOID lpData: The pointer to the element that has to be deleted
|
|
IN HTREEITEM hItem: The tree item, if there is some
|
|
IN BOOL bDelete: Is this because of a delete or a cut. True if delete
|
|
|
|
Note: If we have done a cut and attempt to do the paste on the same database,
|
|
the ID_EDIT_PASTE handler returns before doing any paste.
|
|
Also then this function WILL NOT get called.
|
|
|
|
*********************
|
|
Important
|
|
*********************
|
|
As of now only Entries can be cut from the entry tree,
|
|
individual shims, layers, apphelp etc.
|
|
of entries cannot be cut. So if the type is anything other than TYPE_ENTRY
|
|
then we should have the focus on the entry tree and the operation should be
|
|
a delete and NOT a cut
|
|
|
|
We use g_pSelEntry here when we remove the shims, layers, matching files etc.
|
|
This is based on the assumption that for non-entries we have the focus on a a
|
|
item under g_pSelEntry
|
|
--*/
|
|
{
|
|
HWND hwndFocus = GetFocus();
|
|
LPARAM LPARAMFirstFix = NULL;
|
|
|
|
if (bDelete == FALSE && type != TYPE_ENTRY) {
|
|
//
|
|
// For entry tree only entries can be cut, others can be deleted but not cut.
|
|
//
|
|
assert(FALSE);
|
|
Dbg(dlError, "[DoTheCutEntryTree]: Trying to cut a non-entry item in the entry tree");
|
|
return;
|
|
}
|
|
|
|
switch(type) {
|
|
case TYPE_ENTRY:
|
|
{
|
|
PDBENTRY pApp = NULL;
|
|
//
|
|
// Get the app for this entry.
|
|
//
|
|
pApp = GetAppForEntry(pDataBase, (PDBENTRY)lpData);
|
|
|
|
if (pApp) {
|
|
RemoveEntry(pDataBase, (PDBENTRY)lpData, pApp, bDelete);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case TYPE_APPHELP_ENTRY:
|
|
{
|
|
if (g_pSelEntry == NULL) {
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If the entry has only apphelp and we are deleting the
|
|
// apphelp the entry has to be deleted
|
|
//
|
|
if (g_pSelEntry->pFirstFlag
|
|
|| g_pSelEntry->pFirstLayer
|
|
|| g_pSelEntry->pFirstPatch
|
|
|| g_pSelEntry->pFirstShim) {
|
|
//
|
|
// The entry has some other stuff besides apphelp
|
|
//
|
|
TreeView_DeleteItem(g_hwndEntryTree, hItem);
|
|
|
|
if (DeleteAppHelp(pDataBase, g_pSelEntry->appHelp.HTMLHELPID)) {
|
|
g_pSelEntry->appHelp.bPresent = FALSE;
|
|
g_pSelEntry->appHelp.bBlock = FALSE;
|
|
g_pSelEntry->appHelp.severity = APPTYPE_NONE;
|
|
} else {
|
|
assert(FALSE);
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Prompt the user, want to delete the entry ?
|
|
//
|
|
int nResult = MessageBox(g_hDlg,
|
|
GetString(IDS_DELETE_VERIFY),
|
|
g_szAppName,
|
|
MB_YESNO | MB_ICONQUESTION);
|
|
|
|
if (nResult == IDYES) {
|
|
|
|
DoTheCutEntryTree(pDataBase,
|
|
TYPE_ENTRY,
|
|
SourceType,
|
|
g_pSelEntry,
|
|
g_pSelEntry->hItemExe,
|
|
bDelete);
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case TYPE_MATCHING_FILE:
|
|
{
|
|
if (!g_pSelEntry) {
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
PMATCHINGFILE pMatch = (PMATCHINGFILE)lpData;
|
|
|
|
if (!pMatch) {
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
if (pMatch->strMatchName == TEXT("*")) {
|
|
//
|
|
// Main file must not be deleted
|
|
//
|
|
MessageBox(g_hDlg,
|
|
GetString(IDS_REQUIREDFORMATCHING),
|
|
g_szAppName,
|
|
MB_ICONWARNING);
|
|
|
|
} else {
|
|
|
|
DeleteMatchingFile(&g_pSelEntry->pFirstMatchingFile,
|
|
pMatch,
|
|
g_hwndEntryTree,
|
|
hItem);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case TYPE_MATCHING_ATTRIBUTE:
|
|
{
|
|
HTREEITEM hItemParent = TreeView_GetParent(g_hwndEntryTree, hItem);
|
|
LPARAM lParam;
|
|
|
|
if (hItemParent == NULL) {
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
if (!DBTree.GetLParam(hItemParent, &lParam)) {
|
|
break;
|
|
}
|
|
|
|
PMATCHINGFILE pMatch = PMATCHINGFILE(lParam);
|
|
|
|
if (!pMatch) {
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Set off the bit for this attribute in the mask, so we know that
|
|
// this is no longer in use
|
|
// The lParam of the attributes items which are sub-items of the matching file tree
|
|
// item contain the type of the attribute
|
|
// The mask for a matching file specifies which attributes of this matching file
|
|
// are in use
|
|
// the lParam of the attributes is : TYPE_NULL + 1 + (1 << (dwPos + 1));
|
|
// where dwPos is the index of the attribute in the attribute array: g_Attributes
|
|
// which is defined in dbsupport.cpp
|
|
//
|
|
pMatch->dwMask &= ~ ((ULONG_PTR)lpData - (TYPE_NULL + 1));
|
|
|
|
TreeView_DeleteItem(g_hwndEntryTree, hItem);
|
|
}
|
|
|
|
break;
|
|
|
|
case TYPE_GUI_MATCHING_FILES:
|
|
|
|
MessageBeep(MB_ICONEXCLAMATION);
|
|
break;
|
|
|
|
case FIX_LAYER:
|
|
{
|
|
if (!g_pSelEntry) {
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
PLAYER_FIX plfTobeRemoved = (PLAYER_FIX)lpData;
|
|
|
|
if (g_pSelEntry->pFirstLayer == NULL || plfTobeRemoved == NULL) {
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
if (g_pSelEntry->pFirstLayer->pNext) {
|
|
//
|
|
// We have more than one layer
|
|
//
|
|
PLAYER_FIX_LIST plflTemp = g_pSelEntry->pFirstLayer, plflPrev = NULL;
|
|
|
|
while (plflTemp) {
|
|
|
|
if (plflTemp->pLayerFix == plfTobeRemoved) {
|
|
break;
|
|
}
|
|
|
|
plflPrev = plflTemp;
|
|
plflTemp = plflTemp->pNext;
|
|
}
|
|
|
|
if (plflTemp) {
|
|
|
|
TreeView_DeleteItem(g_hwndEntryTree, hItem);
|
|
|
|
if (plflPrev == NULL) {
|
|
g_pSelEntry->pFirstLayer = plflTemp->pNext;
|
|
} else {
|
|
plflPrev->pNext = plflTemp->pNext;
|
|
}
|
|
|
|
delete plflTemp;
|
|
plflTemp = NULL;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Same as if we are trying to remove the root of the layers
|
|
//
|
|
DoTheCutEntryTree(pDataBase,
|
|
TYPE_GUI_LAYERS,
|
|
SourceType,
|
|
NULL,
|
|
TreeView_GetParent(g_hwndEntryTree, hItem),
|
|
bDelete);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case FIX_SHIM:
|
|
{
|
|
if (!g_pSelEntry) {
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
PSHIM_FIX psfTobeRemoved = (PSHIM_FIX)lpData;
|
|
|
|
if (g_pSelEntry->pFirstShim == NULL || psfTobeRemoved == NULL) {
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
if (g_pSelEntry->pFirstShim->pNext || g_pSelEntry->pFirstFlag) {
|
|
//
|
|
// We have more than one item under the "Compatibility Fixes" tree item
|
|
//
|
|
PSHIM_FIX_LIST psflTemp = g_pSelEntry->pFirstShim, psflPrev = NULL;
|
|
|
|
while (psflTemp) {
|
|
|
|
if (psflTemp->pShimFix == psfTobeRemoved) {
|
|
break;
|
|
}
|
|
|
|
psflPrev = psflTemp;
|
|
psflTemp = psflTemp->pNext;
|
|
}
|
|
|
|
if (psflTemp) {
|
|
|
|
TreeView_DeleteItem(g_hwndEntryTree, hItem);
|
|
|
|
if (psflPrev == NULL) {
|
|
g_pSelEntry->pFirstShim = psflTemp->pNext;
|
|
} else {
|
|
psflPrev->pNext = psflTemp->pNext;
|
|
}
|
|
|
|
delete psflTemp;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Same as we are trying to remove the root of the shims
|
|
//
|
|
DoTheCutEntryTree(pDataBase,
|
|
TYPE_GUI_SHIMS,
|
|
SourceType,
|
|
NULL,
|
|
TreeView_GetParent(g_hwndEntryTree, hItem),
|
|
bDelete);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case FIX_FLAG:
|
|
{
|
|
if (!g_pSelEntry) {
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
PFLAG_FIX pffTobeRemoved = (PFLAG_FIX)lpData;
|
|
|
|
if (g_pSelEntry->pFirstFlag == NULL || pffTobeRemoved == NULL) {
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
if (g_pSelEntry->pFirstFlag->pNext || g_pSelEntry->pFirstShim) {
|
|
//
|
|
// We have more than one item under the "Compatibility Fixes" tree item
|
|
//
|
|
PFLAG_FIX_LIST pfflTemp = g_pSelEntry->pFirstFlag, pfflPrev = NULL;
|
|
|
|
while (pfflTemp) {
|
|
|
|
if (pfflTemp->pFlagFix == pffTobeRemoved) {
|
|
break;
|
|
}
|
|
|
|
pfflPrev = pfflTemp;
|
|
pfflTemp = pfflTemp->pNext;
|
|
}
|
|
|
|
if (pfflTemp) {
|
|
|
|
TreeView_DeleteItem(g_hwndEntryTree, hItem);
|
|
|
|
if (pfflPrev == NULL) {
|
|
g_pSelEntry->pFirstFlag = pfflTemp->pNext;
|
|
} else {
|
|
pfflPrev->pNext = pfflTemp->pNext;
|
|
}
|
|
|
|
delete pfflTemp;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Same as we are trying to remove the root of the flags
|
|
//
|
|
DoTheCutEntryTree(pDataBase,
|
|
TYPE_GUI_SHIMS,
|
|
SourceType,
|
|
NULL,
|
|
TreeView_GetParent(g_hwndEntryTree, hItem),
|
|
bDelete);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case FIX_PATCH:
|
|
{
|
|
if (!g_pSelEntry) {
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
PPATCH_FIX pPatchTobeRemoved = (PPATCH_FIX)lpData;
|
|
|
|
if (g_pSelEntry->pFirstPatch == NULL || pPatchTobeRemoved == NULL) {
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
if (g_pSelEntry->pFirstPatch->pNext || g_pSelEntry->pFirstShim) {
|
|
|
|
PPATCH_FIX_LIST pPatchTemp = g_pSelEntry->pFirstPatch, pPatchflPrev = NULL;
|
|
|
|
while (pPatchTemp) {
|
|
|
|
pPatchflPrev = pPatchTemp;
|
|
|
|
if (pPatchTemp->pPatchFix == pPatchTobeRemoved) {
|
|
break;
|
|
}
|
|
|
|
pPatchTemp = pPatchTemp->pNext;
|
|
}
|
|
|
|
if (pPatchTemp) {
|
|
|
|
TreeView_DeleteItem(g_hwndEntryTree, hItem);
|
|
|
|
if (pPatchflPrev == NULL) {
|
|
g_pSelEntry->pFirstPatch = pPatchTemp->pNext;
|
|
} else {
|
|
pPatchflPrev->pNext = pPatchTemp->pNext;
|
|
}
|
|
|
|
delete pPatchTemp;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Same as we are trying to remove the root of the patches
|
|
//
|
|
DoTheCutEntryTree(pDataBase,
|
|
TYPE_GUI_PATCHES,
|
|
SourceType,
|
|
NULL,
|
|
TreeView_GetParent(g_hwndEntryTree, hItem),
|
|
bDelete);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case TYPE_GUI_LAYERS:
|
|
{
|
|
if (!g_pSelEntry) {
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If the entry has only layers and we are deleting it the
|
|
// entry has to be deleted
|
|
//
|
|
if (g_pSelEntry->pFirstFlag
|
|
|| g_pSelEntry->pFirstPatch
|
|
|| g_pSelEntry->appHelp.bPresent
|
|
|| g_pSelEntry->pFirstShim) {
|
|
|
|
TreeView_DeleteItem(g_hwndEntryTree, hItem);
|
|
|
|
DeleteLayerFixList(g_pSelEntry->pFirstLayer);
|
|
g_pSelEntry->pFirstLayer = NULL;
|
|
|
|
} else {
|
|
//
|
|
// Prompt the user, want to delete the entry ?
|
|
//
|
|
int nResult = MessageBox(g_hDlg,
|
|
GetString(IDS_DELETE_VERIFY),
|
|
g_szAppName,
|
|
MB_YESNO | MB_ICONQUESTION);
|
|
|
|
if (nResult == IDYES) {
|
|
|
|
DoTheCutEntryTree(pDataBase,
|
|
TYPE_ENTRY,
|
|
SourceType,
|
|
g_pSelEntry,
|
|
g_pSelEntry->hItemExe,
|
|
bDelete);
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case TYPE_GUI_PATCHES:
|
|
{
|
|
if (!g_pSelEntry) {
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If the entry has only patches and we are deleting it the entry has
|
|
// to be deleted
|
|
//
|
|
if (g_pSelEntry->pFirstFlag
|
|
|| g_pSelEntry->appHelp.bPresent
|
|
|| g_pSelEntry->pFirstLayer
|
|
|| g_pSelEntry->pFirstShim) {
|
|
|
|
TreeView_DeleteItem(g_hwndEntryTree, hItem);
|
|
|
|
DeletePatchFixList(g_pSelEntry->pFirstPatch);
|
|
g_pSelEntry->pFirstPatch = NULL;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Prompt the user, want to delete the entry ?
|
|
//
|
|
int nResult = MessageBox(g_hDlg,
|
|
GetString(IDS_DELETE_VERIFY),
|
|
g_szAppName,
|
|
MB_YESNO | MB_ICONQUESTION);
|
|
|
|
if (nResult == IDYES) {
|
|
|
|
DoTheCutEntryTree(pDataBase,
|
|
TYPE_ENTRY,
|
|
SourceType,
|
|
g_pSelEntry,
|
|
g_pSelEntry->hItemExe,
|
|
bDelete);
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case TYPE_GUI_SHIMS:
|
|
{
|
|
|
|
if (!g_pSelEntry) {
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If the entry has only shims & flags and we are deleting it the entry
|
|
// has to be deleted
|
|
//
|
|
if (g_pSelEntry->pFirstPatch
|
|
|| g_pSelEntry->appHelp.bPresent
|
|
|| g_pSelEntry->pFirstLayer) {
|
|
|
|
TreeView_DeleteItem(g_hwndEntryTree, hItem);
|
|
|
|
DeleteShimFixList(g_pSelEntry->pFirstShim);
|
|
g_pSelEntry->pFirstShim = NULL;
|
|
|
|
DeleteFlagFixList(g_pSelEntry->pFirstFlag);
|
|
g_pSelEntry->pFirstFlag = NULL;
|
|
|
|
} else {
|
|
//
|
|
// Prompt the user, want to delete the entry ?
|
|
//
|
|
int nResult = MessageBox(g_hDlg,
|
|
GetString(IDS_DELETE_VERIFY),
|
|
g_szAppName,
|
|
MB_YESNO | MB_ICONQUESTION);
|
|
|
|
if (nResult == IDYES) {
|
|
|
|
DoTheCutEntryTree(pDataBase,
|
|
TYPE_ENTRY,
|
|
SourceType,
|
|
g_pSelEntry,
|
|
g_pSelEntry->hItemExe,
|
|
bDelete);
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
DoTheCut(
|
|
IN PDATABASE pDataBase,
|
|
IN TYPE type,
|
|
IN SOURCE_TYPE SourceType,
|
|
IN LPVOID lpData, // To be removed
|
|
IN HTREEITEM hItem,
|
|
IN BOOL bDelete
|
|
)
|
|
/*++
|
|
|
|
DoTheCut
|
|
|
|
Desc: This function does the cut part. This routine is also called when we want
|
|
to delete.
|
|
|
|
Params:
|
|
IN PDATABASE pDataBase : The database where we are doing a cut/delete
|
|
IN TYPE type : The type of the element that has to be deleted
|
|
IN SOURCE_TYPE SourceType : Where the cut/delete was performed. One of:
|
|
a) ENTRY_TREE
|
|
b) ENTRY_LIST
|
|
c) LIB_TREE
|
|
|
|
IN LPVOID lpData: The pointer to the element that has to be deleted
|
|
IN HTREEITEM hItem: The tree item, if there is some
|
|
IN BOOL bDelete: If this is true that means that this routine has been
|
|
called because of a delete operation. Deletion is a bit different than cut because
|
|
when we delete then we might need to repaint the UI. When doing a cut, the actual
|
|
cut is done only after paste has been successful. In that case the newly pasted
|
|
item is shown in the UI and we should not try to update the UI.
|
|
|
|
Note: If we have done a cut and attempt to do the paste on the same database,
|
|
the ID_EDIT_PASTE handler returns before doing any paste.
|
|
Also then this function WILL NOT get called.
|
|
--*/
|
|
{
|
|
INT iIndex = -1;
|
|
HWND hwndFocus;
|
|
|
|
hwndFocus = GetFocus();
|
|
|
|
if (SourceType == ENTRY_TREE) {
|
|
|
|
DoTheCutEntryTree(pDataBase,
|
|
type,
|
|
SourceType,
|
|
lpData,
|
|
hItem,
|
|
bDelete);
|
|
return;
|
|
}
|
|
|
|
switch (type) {
|
|
case TYPE_ENTRY:
|
|
|
|
if (SourceType == LIB_TREE || SourceType == ENTRY_LIST) {
|
|
//
|
|
// Must delete the tree item before actually removing the
|
|
// entry using RemoveApp/RemoveEntry
|
|
//
|
|
if (bDelete) {
|
|
TreeDeleteAll(g_hwndEntryTree);
|
|
g_pEntrySelApp = g_pSelEntry = NULL;
|
|
}
|
|
|
|
if (SourceType == ENTRY_LIST && bDelete) {
|
|
iIndex = GetContentsListIndex(g_hwndContentsList, (LPARAM)lpData);
|
|
|
|
if (iIndex > -1) {
|
|
ListView_DeleteItem(g_hwndContentsList, iIndex);
|
|
}
|
|
}
|
|
|
|
DBTree.DeleteAppLayer(pDataBase, TRUE, hItem, bDelete);
|
|
|
|
RemoveApp(pDataBase, (PDBENTRY)lpData);
|
|
|
|
if (bDelete && pDataBase->hItemAllApps) {
|
|
//
|
|
// Show that we now have one less entry/app
|
|
//
|
|
SetStatusStringDBTree(pDataBase->hItemAllApps);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case TYPE_GUI_APPS:
|
|
|
|
TreeView_DeleteItem(DBTree.m_hLibraryTree, pDataBase->hItemAllApps);
|
|
RemoveAllApps(pDataBase);
|
|
pDataBase->hItemAllApps = NULL;
|
|
|
|
break;
|
|
|
|
case FIX_LAYER:
|
|
|
|
if (RemoveLayer(pDataBase, (PLAYER_FIX)lpData, NULL)) {
|
|
//
|
|
// This function will return FALSE, if the layer in in use
|
|
//
|
|
if (SourceType == ENTRY_LIST && bDelete && type == FIX_LAYER) {
|
|
iIndex = GetContentsListIndex(g_hwndContentsList, (LPARAM)lpData);
|
|
|
|
if (iIndex > -1) {
|
|
ListView_DeleteItem(g_hwndContentsList, iIndex);
|
|
}
|
|
}
|
|
|
|
DBTree.DeleteAppLayer(pDataBase, FALSE, hItem, bDelete);
|
|
|
|
//
|
|
// Show that we now have one less layer
|
|
//
|
|
if (bDelete && pDataBase->hItemAllLayers) {
|
|
SetStatusStringDBTree(pDataBase->hItemAllLayers);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case TYPE_GUI_LAYERS:
|
|
{
|
|
BOOL fAllDeleted = TRUE;
|
|
|
|
while (pDataBase->pLayerFixes && fAllDeleted) {
|
|
|
|
fAllDeleted = fAllDeleted && RemoveLayer(pDataBase,
|
|
pDataBase->pLayerFixes,
|
|
NULL);
|
|
}
|
|
|
|
if (fAllDeleted) {
|
|
TreeView_DeleteItem(DBTree.m_hLibraryTree, pDataBase->hItemAllLayers);
|
|
pDataBase->hItemAllLayers = NULL;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
LRESULT
|
|
CALLBACK
|
|
ListViewProc(
|
|
IN HWND hWnd,
|
|
IN UINT uMsg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
ListViewProc
|
|
|
|
Desc: Subclasses the message proc for the contents list view (RHS)
|
|
|
|
Params: Standard dialog handler parameters
|
|
|
|
IN HWND hDlg
|
|
IN UINT uMsg
|
|
IN WPARAM wParam
|
|
IN LPARAM lParam
|
|
|
|
Return: Standard handler return
|
|
|
|
--*/
|
|
{
|
|
switch (uMsg) {
|
|
case WM_SETFOCUS:
|
|
{
|
|
LVITEM lvi;
|
|
|
|
ZeroMemory(&lvi, sizeof(lvi));
|
|
|
|
lvi.mask = LVIF_PARAM;
|
|
lvi.iItem = g_iContentsListSelectIndex;
|
|
lvi.iSubItem = 0;
|
|
|
|
if (ListView_GetItem(hWnd, &lvi)) {
|
|
|
|
HTREEITEM hItemInDBTree = NULL;
|
|
|
|
hItemInDBTree = DBTree.FindChild(TreeView_GetSelection(DBTree.m_hLibraryTree),
|
|
lvi.lParam);
|
|
|
|
SetStatusStringDBTree(hItemInDBTree);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return CallWindowProc(g_PrevListProc, hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
LRESULT
|
|
CALLBACK
|
|
TreeViewProc(
|
|
HWND hWnd,
|
|
UINT uMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
|
|
/*++
|
|
TreeViewProc
|
|
|
|
Desc: Subclasses the message proc for both the tree views
|
|
|
|
Params: Standard dialog handler parameters
|
|
|
|
IN HWND hDlg
|
|
IN UINT uMsg
|
|
IN WPARAM wParam
|
|
IN LPARAM lParam
|
|
|
|
Return: Standard handler return
|
|
|
|
--*/
|
|
|
|
{
|
|
switch (uMsg)
|
|
case WM_SETFOCUS:
|
|
{
|
|
HTREEITEM hItem= TreeView_GetSelection(hWnd);
|
|
|
|
if (hItem) {
|
|
|
|
if (hWnd && hWnd == DBTree.m_hLibraryTree) {
|
|
SetStatusStringDBTree(hItem);
|
|
} else if (hWnd && hWnd == g_hwndEntryTree) {
|
|
SetStatusStringEntryTree(hItem);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return CallWindowProc(g_PrevTreeProc, hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
void
|
|
PopulateContentsList(
|
|
IN HTREEITEM hItem
|
|
)
|
|
/*++
|
|
|
|
PopulateContentsList
|
|
|
|
Desc: Populates the contents list with the children of hItem in the DB Tree
|
|
At the moment we show the items in the contents list only if the type of
|
|
hItem is of:
|
|
|
|
TYPE_GUI_APPS
|
|
TYPE_GUI_LAYERS
|
|
TYPE_GUI_SHIMS
|
|
FIX_LAYER
|
|
|
|
Params:
|
|
IN HTREEITEM hItem: A tree item in the db tree.
|
|
|
|
Return:
|
|
void
|
|
|
|
Notes: This routine will not/should not be called when the user has selected a
|
|
an app in the Lib Tree.
|
|
--*/
|
|
{
|
|
TCHAR szBuffer[512];
|
|
TYPE type = (TYPE)GetItemType(DBTree.m_hLibraryTree, hItem);
|
|
TVITEM Item;
|
|
UINT uIndex = 0;
|
|
LVITEM lvi;
|
|
|
|
if (type == TYPE_GUI_APPS
|
|
|| type == TYPE_GUI_LAYERS
|
|
|| type == TYPE_GUI_SHIMS
|
|
|| type == FIX_LAYER) {
|
|
|
|
ShowWindow(g_hwndEntryTree, SW_HIDE);
|
|
|
|
TreeDeleteAll(g_hwndEntryTree);
|
|
|
|
g_bIsContentListVisible = TRUE;
|
|
|
|
ListView_DeleteAllItems(g_hwndContentsList);
|
|
|
|
//
|
|
// ASSUMPTION: We are assuming (this is correct at the moment), that we
|
|
// have only one column in the list view
|
|
//
|
|
ListView_DeleteColumn(g_hwndContentsList, 0);
|
|
|
|
ShowWindow(g_hwndContentsList, SW_SHOW);
|
|
SendMessage(g_hwndContentsList, WM_SETREDRAW, TRUE, 0);
|
|
|
|
*szBuffer = 0;
|
|
|
|
Item.mask = TVIF_TEXT;
|
|
Item.hItem = hItem;
|
|
Item.pszText = szBuffer;
|
|
Item.cchTextMax = ARRAYSIZE(szBuffer);
|
|
|
|
if (!TreeView_GetItem(DBTree.m_hLibraryTree, &Item)) {
|
|
assert(FALSE);
|
|
goto End;
|
|
}
|
|
|
|
//
|
|
// Set the column text as the text of the item in the tree view
|
|
//
|
|
InsertColumnIntoListView(g_hwndContentsList, szBuffer, 0, 100);
|
|
|
|
//
|
|
// Add all the children of the selected item in the tree view
|
|
//
|
|
hItem = TreeView_GetChild(DBTree.m_hLibraryTree, hItem);
|
|
|
|
Item.mask = TVIF_PARAM | TVIF_IMAGE | TVIF_TEXT;
|
|
Item.pszText = szBuffer;
|
|
Item.cchTextMax = ARRAYSIZE(szBuffer);
|
|
|
|
lvi.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
|
|
|
|
while (hItem) {
|
|
|
|
*szBuffer = 0;
|
|
Item.hItem = hItem;
|
|
|
|
if (!TreeView_GetItem(DBTree.m_hLibraryTree, &Item)) {
|
|
assert(FALSE);
|
|
return;
|
|
}
|
|
|
|
lvi.pszText = Item.pszText;
|
|
lvi.iSubItem = 0;
|
|
lvi.lParam = Item.lParam;
|
|
lvi.iImage = Item.iImage;
|
|
lvi.iItem = uIndex;
|
|
|
|
ListView_InsertItem(g_hwndContentsList, &lvi);
|
|
|
|
UpdateWindow(g_hwndContentsList);
|
|
|
|
uIndex++;
|
|
hItem = TreeView_GetNextSibling(DBTree.m_hLibraryTree, hItem);
|
|
}
|
|
|
|
//
|
|
// Set the selection mark for the first element.
|
|
//
|
|
ListView_SetSelectionMark(g_hwndContentsList, 0);
|
|
ListView_SetItemState(g_hwndContentsList,
|
|
0,
|
|
LVIS_FOCUSED | LVIS_SELECTED ,
|
|
LVIS_FOCUSED | LVIS_SELECTED);
|
|
|
|
//
|
|
// Set the column width of the list view appropriately to cover the width of the
|
|
// list view
|
|
// Assumption: The list view has only one column
|
|
//
|
|
ListView_SetColumnWidth(g_hwndContentsList, 0, LVSCW_AUTOSIZE_USEHEADER);
|
|
|
|
} else {
|
|
//
|
|
// Clear the contents pane. This is the only way.
|
|
//
|
|
TreeDeleteAll(g_hwndEntryTree);
|
|
g_pSelEntry = g_pEntrySelApp = NULL;
|
|
|
|
ShowWindow(g_hwndContentsList, SW_HIDE);
|
|
|
|
g_bIsContentListVisible = FALSE;
|
|
|
|
ShowWindow(g_hwndEntryTree, SW_SHOW);
|
|
}
|
|
|
|
End:
|
|
return;
|
|
}
|
|
|
|
void
|
|
LoadPerUserSettings(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
LoadPerUserSettings
|
|
|
|
Desc: Loads the list of per-user settings
|
|
|
|
Params:
|
|
void
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
WCHAR szwName[1024];
|
|
TCHAR szUserName[256], szDomainName[256];
|
|
TCHAR szValueName[MAX_PATH];
|
|
HKEY hKey = NULL;
|
|
PSID pSid = NULL;
|
|
DWORD dwIndex = 0;
|
|
LPTSTR pszData = NULL;
|
|
DWORD dwIndexValue = 0;
|
|
INT iLength = 0;
|
|
SID_NAME_USE sid_type;
|
|
TVINSERTSTRUCT is;
|
|
|
|
|
|
*szwName = 0;
|
|
|
|
SendMessage(DBTree.m_hLibraryTree, WM_SETREDRAW, FALSE, 0);
|
|
|
|
//
|
|
// Remove the tree item for per-user settings if it exists. We
|
|
// repopulate the entire list.
|
|
//
|
|
if (DBTree.m_hPerUserHead) {
|
|
TreeView_DeleteItem(DBTree.m_hLibraryTree, DBTree.m_hPerUserHead);
|
|
DBTree.m_hPerUserHead = NULL;
|
|
}
|
|
|
|
|
|
DWORD dwchSizeSubKeyName = sizeof(szwName)/sizeof(WCHAR);
|
|
|
|
//
|
|
// Enumerate the sub-keys under HKEY_USERS.
|
|
//
|
|
while (ERROR_SUCCESS == RegEnumKey(HKEY_USERS,
|
|
dwIndex,
|
|
szwName,
|
|
dwchSizeSubKeyName)) {
|
|
dwIndex++;
|
|
|
|
pSid = NULL;
|
|
|
|
if (!ConvertStringSidToSid(szwName, &pSid)) {
|
|
|
|
if (pSid) {
|
|
LocalFree(pSid);
|
|
pSid = NULL;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
DWORD dwchSizeofUserName = ARRAYSIZE(szUserName);
|
|
DWORD dwchSizeDomainName = ARRAYSIZE(szDomainName);
|
|
|
|
*szUserName = *szDomainName = 0;
|
|
|
|
if (!LookupAccountSid(NULL,
|
|
pSid,
|
|
szUserName,
|
|
&dwchSizeofUserName,
|
|
szDomainName,
|
|
&dwchSizeDomainName,
|
|
&sid_type)) {
|
|
|
|
if (pSid) {
|
|
LocalFree(pSid);
|
|
pSid = NULL;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (sid_type != SidTypeUser) {
|
|
|
|
if (pSid) {
|
|
LocalFree(pSid);
|
|
pSid = NULL;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (pSid) {
|
|
LocalFree(pSid);
|
|
pSid = NULL;
|
|
}
|
|
|
|
iLength = lstrlen(szwName);
|
|
|
|
SafeCpyN(szwName + iLength, APPCOMPAT_PERM_LAYER_PATH, ARRAYSIZE(szwName) - iLength);
|
|
|
|
if (RegOpenKeyEx(HKEY_USERS, szwName, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
|
|
//
|
|
// enumerate the values
|
|
//
|
|
*szValueName = 0;
|
|
|
|
DWORD dwchSizeofValueName;
|
|
DWORD dwSizeofData;
|
|
DWORD dwType = REG_SZ;
|
|
LONG lReturn;
|
|
HTREEITEM hItemSingleUser = NULL, hItemApp = NULL;
|
|
|
|
while (TRUE) {
|
|
|
|
dwchSizeofValueName = ARRAYSIZE(szValueName);
|
|
dwSizeofData = 512;
|
|
dwType = REG_SZ;
|
|
|
|
pszData = new TCHAR[dwSizeofData / sizeof(TCHAR)];
|
|
|
|
if (pszData == NULL) {
|
|
MEM_ERR;
|
|
break;
|
|
}
|
|
|
|
lReturn = RegEnumValue(hKey,
|
|
dwIndexValue,
|
|
szValueName,
|
|
&dwchSizeofValueName,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)pszData,
|
|
&dwSizeofData);
|
|
|
|
if (lReturn == ERROR_NO_MORE_ITEMS) {
|
|
break;
|
|
}
|
|
|
|
if (lReturn != ERROR_SUCCESS || dwType != REG_SZ) {
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
if (DBTree.m_hPerUserHead == NULL) {
|
|
//
|
|
// Make the first node.
|
|
//
|
|
is.hParent = TVI_ROOT;
|
|
is.hInsertAfter = (DBTree.m_hItemAllInstalled) ? DBTree.m_hItemAllInstalled : DBTree.m_hItemGlobal;
|
|
|
|
is.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE ;
|
|
is.item.stateMask = TVIS_EXPANDED;
|
|
is.item.pszText = GetString(IDS_PERUSER);
|
|
is.item.iImage = IMAGE_ALLUSERS;
|
|
is.item.iSelectedImage = IMAGE_ALLUSERS;
|
|
|
|
DBTree.m_hPerUserHead = TreeView_InsertItem(DBTree.m_hLibraryTree, &is);
|
|
}
|
|
|
|
if (dwIndexValue == 0) {
|
|
//
|
|
// First app, we have to create the user icon as well
|
|
//
|
|
is.hParent = DBTree.m_hPerUserHead;
|
|
is.hInsertAfter = TVI_SORT;
|
|
is.item.pszText = szUserName;
|
|
is.item.iImage = IMAGE_SINGLEUSER;
|
|
is.item.iSelectedImage = IMAGE_SINGLEUSER;
|
|
|
|
hItemSingleUser = TreeView_InsertItem(DBTree.m_hLibraryTree, &is);
|
|
}
|
|
|
|
//
|
|
// Now add the app for the user.
|
|
//
|
|
is.hInsertAfter = TVI_SORT;
|
|
is.hParent = hItemSingleUser;
|
|
is.item.pszText = szValueName;
|
|
is.item.iImage = IMAGE_SINGLEAPP;
|
|
is.item.iSelectedImage = IMAGE_SINGLEAPP;
|
|
hItemApp = TreeView_InsertItem(DBTree.m_hLibraryTree, &is);
|
|
|
|
//
|
|
// Now we have to add all the layers for this app.
|
|
//
|
|
is.hParent = hItemApp;
|
|
is.item.iImage = IMAGE_LAYERS;
|
|
is.item.iSelectedImage = IMAGE_LAYERS;
|
|
|
|
LPCTSTR pszLayerName = NULL;
|
|
|
|
//
|
|
// Get the individual mode names that have been applied to the app (BO)
|
|
//
|
|
pszLayerName = _tcstok(pszData, TEXT(" "));
|
|
|
|
while (pszLayerName) {
|
|
|
|
PLAYER_FIX plf = (PLAYER_FIX)FindFix(pszLayerName,
|
|
FIX_LAYER,
|
|
&GlobalDataBase);
|
|
|
|
if (plf) {
|
|
|
|
is.item.pszText = plf->strName.pszString;
|
|
TreeView_InsertItem(DBTree.m_hLibraryTree, &is);
|
|
}
|
|
|
|
pszLayerName = _tcstok(NULL, TEXT(" "));
|
|
}
|
|
|
|
++dwIndexValue;
|
|
|
|
if (pszData) {
|
|
delete[] pszData;
|
|
pszData = NULL;
|
|
}
|
|
}
|
|
|
|
REGCLOSEKEY(hKey);
|
|
|
|
//
|
|
// We might come here if we had some error and we break off the while loop
|
|
//
|
|
if (pszData) {
|
|
delete[] pszData;
|
|
pszData = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
SendMessage(DBTree.m_hLibraryTree, WM_SETREDRAW, TRUE, 0);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
INT_PTR
|
|
CALLBACK
|
|
MsgBoxDlgProc(
|
|
IN HWND hdlg,
|
|
IN UINT uMsg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
MsgBoxDlgProc
|
|
|
|
Desc: Displays a message box dialog so we can use the hyperlink.
|
|
|
|
Params: Standard dialog handler parameters
|
|
|
|
IN HWND hDlg
|
|
IN UINT uMsg
|
|
IN WPARAM wParam
|
|
IN LPARAM lParam
|
|
|
|
Return: Standard dialog handler return
|
|
--*/
|
|
{
|
|
int wCode = LOWORD(wParam);
|
|
int wNotifyCode = HIWORD(wParam);
|
|
|
|
switch (uMsg) {
|
|
case WM_INITDIALOG:
|
|
{
|
|
TCHAR szLink[MAX_PATH];
|
|
UINT uNoSDB;
|
|
|
|
uNoSDB = (UINT)lParam;
|
|
//
|
|
// Use the parameter to determine what text to display.
|
|
//
|
|
if (uNoSDB) {
|
|
|
|
LoadString(g_hInstance, IDS_W2K_NO_SDB, szLink, ARRAYSIZE(szLink));
|
|
SetDlgItemText(hdlg, IDC_MESSAGE, szLink);
|
|
|
|
} else {
|
|
LoadString(g_hInstance, IDS_SP2_SDB, szLink, ARRAYSIZE(szLink));
|
|
SetDlgItemText(hdlg, IDC_MESSAGE, szLink);
|
|
}
|
|
|
|
LoadString(g_hInstance, IDS_MSG_LINK, szLink, ARRAYSIZE(szLink));
|
|
SetDlgItemText(hdlg, IDC_MSG_LINK, szLink);
|
|
|
|
ShowWindow(hdlg, SW_SHOWNORMAL);
|
|
break;
|
|
}
|
|
|
|
case WM_NOTIFY:
|
|
{
|
|
NMHDR* pHdr = (NMHDR*)lParam;
|
|
|
|
if (pHdr->idFrom == IDC_MSG_LINK) {
|
|
|
|
if (pHdr->code == NM_CLICK || pHdr->code == NM_RETURN) {
|
|
|
|
SHELLEXECUTEINFO sei = { 0 };
|
|
|
|
sei.cbSize = sizeof(SHELLEXECUTEINFO);
|
|
sei.fMask = SEE_MASK_DOENVSUBST;
|
|
sei.hwnd = hdlg;
|
|
sei.nShow = SW_SHOWNORMAL;
|
|
sei.lpFile = g_szW2KUrl;
|
|
|
|
ShellExecuteEx(&sei);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
|
|
switch (wCode) {
|
|
case IDCANCEL:
|
|
|
|
EndDialog(hdlg, FALSE);
|
|
break;
|
|
|
|
case ID_UPDATE:
|
|
{
|
|
SHELLEXECUTEINFO sei = { 0 };
|
|
|
|
sei.cbSize = sizeof(SHELLEXECUTEINFO);
|
|
sei.fMask = SEE_MASK_DOENVSUBST;
|
|
sei.hwnd = NULL;
|
|
sei.nShow = SW_SHOWNORMAL;
|
|
sei.lpFile = g_szW2KUrl;
|
|
|
|
ShellExecuteEx(&sei);
|
|
|
|
EndDialog(hdlg, TRUE);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
CheckProperSP(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
CheckProperSP
|
|
|
|
Returns:
|
|
TRUE: If the service pack is more than two
|
|
FALSE: Otherwise
|
|
--*/
|
|
{
|
|
if (g_fSPGreaterThan2) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
CopyToClipBoard(
|
|
IN WPARAM wCode
|
|
)
|
|
/*++
|
|
|
|
CopyToClipBoard
|
|
|
|
Desc: Copies data into our Clipboard data structure. (Not the windows clipboard)
|
|
|
|
Params:
|
|
IN WPARAMS wCode:
|
|
One of:
|
|
ID_EDIT_CUT: This is a cut
|
|
ID_EDIT_COPY: This is a copy
|
|
--*/
|
|
{
|
|
TCHAR szBuffer[256];
|
|
TYPE type;
|
|
HGLOBAL hGlobal = NULL;
|
|
HWND hwndFocus = GetFocus();
|
|
LPARAM lParam = NULL;
|
|
TCHAR* pszGlobal = NULL;
|
|
CopyStruct* pCopyTemp = NULL;
|
|
HTREEITEM hItem;
|
|
TVITEM Item;
|
|
SIZE_T chBuffersize;
|
|
|
|
*szBuffer = 0;
|
|
|
|
gClipBoard.RemoveAll();
|
|
|
|
g_bIsCut = (wCode == ID_EDIT_CUT);
|
|
gClipBoard.pDataBase = g_pPresentDataBase;
|
|
|
|
if (hwndFocus == g_hwndEntryTree || hwndFocus == DBTree.m_hLibraryTree) {
|
|
|
|
//
|
|
// Copy/Cut is on some tree.
|
|
//
|
|
pCopyTemp= new CopyStruct;
|
|
|
|
if (pCopyTemp == NULL) {
|
|
MEM_ERR;
|
|
goto End;
|
|
}
|
|
|
|
hItem = TreeView_GetSelection(hwndFocus);
|
|
|
|
Item.mask = TVIF_PARAM | TVIF_TEXT;
|
|
Item.pszText = szBuffer;
|
|
Item.cchTextMax = ARRAYSIZE(szBuffer);
|
|
Item.hItem = hItem;
|
|
|
|
if (!TreeView_GetItem(hwndFocus, &Item) || Item.lParam == NULL) {
|
|
assert(FALSE);
|
|
|
|
if (pCopyTemp) {
|
|
delete pCopyTemp;
|
|
pCopyTemp = NULL;
|
|
}
|
|
|
|
goto End;
|
|
}
|
|
|
|
lParam = Item.lParam;
|
|
|
|
//
|
|
// Copy text to Windows clipboard
|
|
//
|
|
chBuffersize = ARRAYSIZE(szBuffer);
|
|
|
|
hGlobal = GlobalAlloc(GHND | GMEM_SHARE, chBuffersize * sizeof(TCHAR));
|
|
|
|
if (hGlobal) {
|
|
pszGlobal = (TCHAR*)GlobalLock(hGlobal);
|
|
SafeCpyN(pszGlobal, szBuffer, chBuffersize);
|
|
}
|
|
|
|
GlobalUnlock(hGlobal);
|
|
|
|
if (OpenClipboard(g_hDlg)) {
|
|
|
|
EmptyClipboard();
|
|
SetClipboardData(CF_UNICODETEXT, hGlobal);
|
|
CloseClipboard();
|
|
}
|
|
|
|
//
|
|
// Now copy the data to our own clipboard, which is nothing but a linked list
|
|
//
|
|
type = GetItemType(hwndFocus, hItem);
|
|
|
|
gClipBoard.type = type;
|
|
|
|
//
|
|
// Set the source type. This will indicate on which control out copy-cut operation
|
|
// took place
|
|
//
|
|
if (hwndFocus == g_hwndEntryTree) {
|
|
gClipBoard.SourceType = ENTRY_TREE;
|
|
} else if(hwndFocus == DBTree.m_hLibraryTree) {
|
|
gClipBoard.SourceType = LIB_TREE;
|
|
}
|
|
|
|
pCopyTemp->hItem = hItem;
|
|
pCopyTemp->lpData = (LPVOID)lParam;
|
|
|
|
gClipBoard.Add(pCopyTemp);
|
|
|
|
} else if (hwndFocus == g_hwndContentsList) {
|
|
//
|
|
// We can have multiple selects here.
|
|
//
|
|
gClipBoard.SourceType = ENTRY_LIST;
|
|
|
|
//
|
|
// Get the type of the items for the content list at the moment.
|
|
//
|
|
LVITEM lvItem;
|
|
|
|
lvItem.mask = LVIF_PARAM;
|
|
lvItem.iItem = 0;
|
|
|
|
if (!ListView_GetItem(g_hwndContentsList, &lvItem)) {
|
|
assert(FALSE);
|
|
goto End;
|
|
}
|
|
|
|
assert(lvItem.lParam);
|
|
|
|
if (lvItem.lParam > TYPE_NULL) {
|
|
PDS_TYPE pdsType = (PDS_TYPE)lvItem.lParam;
|
|
type = pdsType->type;
|
|
} else {
|
|
type = (TYPE)lvItem.lParam;
|
|
}
|
|
|
|
gClipBoard.type = type;
|
|
|
|
UINT uSelectedCount = ListView_GetSelectedCount(g_hwndContentsList);
|
|
INT iTotalCount = ListView_GetItemCount(g_hwndContentsList);
|
|
INT iIndex = 0;
|
|
UINT uState = 0;
|
|
LONG lSizeofClipboard = 0;
|
|
HTREEITEM hParent; // This will be either the AllApps item or the AllLayers Item
|
|
CSTRINGLIST strlListContents;
|
|
|
|
lvItem.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
|
|
lvItem.stateMask = LVIS_SELECTED;
|
|
|
|
for (UINT uFoundSelected = 1;
|
|
iIndex < iTotalCount && uFoundSelected <= uSelectedCount;
|
|
iIndex++) {
|
|
|
|
*szBuffer = 0;
|
|
|
|
lvItem.iItem = iIndex;
|
|
lvItem.iSubItem = 0;
|
|
lvItem.pszText = szBuffer;
|
|
lvItem.cchTextMax = ARRAYSIZE(szBuffer) - ARRAYSIZE(STR_NEW_LINE_CHARS);
|
|
|
|
if(!ListView_GetItem(g_hwndContentsList, &lvItem)) {
|
|
assert(FALSE);
|
|
continue;
|
|
}
|
|
|
|
if (lvItem.state & LVIS_SELECTED) {
|
|
|
|
pCopyTemp = new CopyStruct;
|
|
|
|
if (pCopyTemp == NULL) {
|
|
MEM_ERR;
|
|
goto End;
|
|
}
|
|
|
|
INT iItemLength = lstrlen(szBuffer);
|
|
|
|
//
|
|
// See below: + 2 for the TEXT("\r\n") characters. Note that we are
|
|
// adding 2 because lSizeofClipboard represents the number of
|
|
// characters and not the size.
|
|
// Do NOT change this to sizeof(TCHAR)
|
|
//
|
|
lSizeofClipboard += iItemLength + 2;
|
|
|
|
if (((ARRAYSIZE(szBuffer) - 1) - iItemLength) >= (ARRAYSIZE(STR_NEW_LINE_CHARS) - 1)) {
|
|
//
|
|
// Make sure we have sufficient space
|
|
//
|
|
SafeCpyN(szBuffer + iItemLength, STR_NEW_LINE_CHARS, ARRAYSIZE(szBuffer) - iItemLength);
|
|
} else {
|
|
assert(FALSE);
|
|
}
|
|
|
|
strlListContents.AddString(szBuffer);
|
|
|
|
if (type == TYPE_ENTRY) {
|
|
hParent = g_pPresentDataBase->hItemAllApps;
|
|
|
|
} else if (type == FIX_LAYER) {
|
|
hParent = g_pPresentDataBase->hItemAllLayers;
|
|
|
|
} else if (type == FIX_SHIM || type == FIX_FLAG) {
|
|
hParent = DBTree.FindChild(g_pPresentDataBase->hItemDB,
|
|
TYPE_GUI_SHIMS);
|
|
} else {
|
|
assert(FALSE);
|
|
}
|
|
|
|
pCopyTemp->hItem = DBTree.FindChild(hParent, lvItem.lParam);
|
|
pCopyTemp->lpData = (LPVOID)lvItem.lParam;
|
|
|
|
gClipBoard.Add(pCopyTemp);
|
|
uFoundSelected++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy text to the Windows clipboard
|
|
//
|
|
if (lSizeofClipboard) {
|
|
|
|
chBuffersize = lSizeofClipboard + 1; // The last + 1 for the last NULL character
|
|
|
|
hGlobal = GlobalAlloc(GHND | GMEM_SHARE, chBuffersize * sizeof(TCHAR));
|
|
|
|
if (hGlobal) {
|
|
|
|
pszGlobal = (TCHAR*)GlobalLock(hGlobal);
|
|
|
|
if (pszGlobal == NULL) {
|
|
assert(FALSE);
|
|
goto End;
|
|
}
|
|
|
|
PSTRLIST pHead = strlListContents.m_pHead;
|
|
|
|
*pszGlobal = 0;
|
|
|
|
while (pHead) {
|
|
StringCchCat(pszGlobal, chBuffersize, pHead->szStr.pszString );
|
|
pHead = pHead->pNext;
|
|
}
|
|
|
|
GlobalUnlock(hGlobal);
|
|
|
|
if (OpenClipboard(g_hDlg)) {
|
|
|
|
EmptyClipboard();
|
|
SetClipboardData(CF_UNICODETEXT, hGlobal);
|
|
CloseClipboard();
|
|
}
|
|
|
|
} else {
|
|
assert(FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
End:;
|
|
}
|
|
|
|
void
|
|
PasteFromClipBoard(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
PasteFromClipBoard
|
|
|
|
Desc: Pastes from our own CLIPBOARD data structure. This routine will also do the cut part. We will
|
|
cut an entry if the entry was cut instead of being copied. Once we have successfully pasted an entry
|
|
at the destination then we proceed with our cut.
|
|
|
|
Note: 1. If the source and the dest. databases are same, and the operation involves a cut
|
|
for an application or a layer and the source is the DB tree do nothing
|
|
|
|
2. If the source is the entry tree then we do allow cut for the same database.
|
|
|
|
Assumption: Only entries can be copied/cut from the entry tree. If you allow
|
|
non-entries to be copied as well, but do not allow cut for them, it
|
|
will be a mess
|
|
|
|
Copy proceeds normally but for cut we have to do something extra. If this
|
|
is a cut operation then when we check for conflicts we will definitely find
|
|
the entry being cut as a conflict (we do not cut the
|
|
entry until after it has been pasted successfully), so we must leave that
|
|
entry when we check for conflicts and we must delete that entry at
|
|
the end of paste.
|
|
|
|
Note that cut is not allowed for shims / flags in any case
|
|
--*/
|
|
{
|
|
//
|
|
// If we are doing a cut and the source and the destination databases are the same and
|
|
// the source of the cut is either the db tree or the entry list then there is no need to do a paste
|
|
// But the situation is different if we are doing a cut in the entry tree. Because in this case we
|
|
// should allow users to cut an entry and paste it in a different application. If the user tries to
|
|
// paste in the same application then we do not do anything
|
|
//
|
|
if (g_bIsCut && gClipBoard.pDataBase == g_pPresentDataBase &&
|
|
(gClipBoard.SourceType == LIB_TREE || gClipBoard.SourceType == ENTRY_LIST) &&
|
|
(gClipBoard.type == TYPE_ENTRY
|
|
|| gClipBoard.type == TYPE_GUI_APPS
|
|
|| gClipBoard.type == TYPE_GUI_LAYERS
|
|
|| gClipBoard.type == FIX_LAYER)) {
|
|
|
|
return;
|
|
}
|
|
|
|
HWND hwndFocus = GetFocus();
|
|
TYPE typeTemp = TYPE_UNKNOWN;
|
|
|
|
switch (gClipBoard.type) {
|
|
case TYPE_ENTRY:
|
|
{
|
|
HTREEITEM hItem = NULL;
|
|
|
|
if (hwndFocus != g_hwndContentsList) {
|
|
hItem = TreeView_GetSelection(DBTree.m_hLibraryTree);
|
|
} else {
|
|
|
|
LVITEM lvItem;
|
|
|
|
lvItem.mask = LVIF_PARAM | LVIF_STATE ;
|
|
lvItem.stateMask = LVIS_SELECTED;
|
|
lvItem.iItem = ListView_GetSelectionMark(g_hwndContentsList);
|
|
lvItem.iSubItem = 0;
|
|
|
|
if (g_pPresentDataBase == NULL) {
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
if (ListView_GetItem(g_hwndContentsList, &lvItem) &&
|
|
(lvItem.state & LVIS_SELECTED)) {
|
|
|
|
//
|
|
// Check that the selected item is an App
|
|
//
|
|
typeTemp = ConvertLparam2Type(lvItem.lParam);
|
|
|
|
if (typeTemp != TYPE_ENTRY) {
|
|
//
|
|
// We needed a entry to be selected in the contents list
|
|
//
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
hItem = DBTree.FindChild(g_pPresentDataBase->hItemAllApps, lvItem.lParam);
|
|
|
|
} else {
|
|
hItem = g_pPresentDataBase->hItemAllApps;
|
|
}
|
|
|
|
if (hItem == NULL) {
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We selected an entry from the tree on the right and we have also selected an app on the left.
|
|
// That means we wish to put the selected entry into the selected app on the left.
|
|
// Note that if we have selected some app from the contents list, we find the corresponding
|
|
// tree item in the db tree for that app and proceed as if we had tried to do paste
|
|
// on that tree item
|
|
//
|
|
if (gClipBoard.SourceType == ENTRY_TREE &&
|
|
GetItemType(DBTree.m_hLibraryTree, hItem) == TYPE_ENTRY) {
|
|
|
|
LPARAM lParam;
|
|
|
|
if(!DBTree.GetLParam(hItem, &lParam)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If we are doing a cut and the the destination app is the same as the app of the
|
|
// entry being cut, do nothing
|
|
//
|
|
if (g_bIsCut && (PDBENTRY)lParam == GetAppForEntry(gClipBoard.pDataBase,
|
|
(PDBENTRY)gClipBoard.pClipBoardHead->lpData)) {
|
|
|
|
goto End;
|
|
}
|
|
|
|
PasteSingleApp((PDBENTRY)gClipBoard.pClipBoardHead->lpData,
|
|
g_pPresentDataBase,
|
|
gClipBoard.pDataBase,
|
|
FALSE,
|
|
((PDBENTRY)lParam)->strAppName.pszString);
|
|
|
|
SendMessage(g_hwndEntryTree, WM_SETREDRAW, TRUE, 0);
|
|
|
|
} else if (gClipBoard.SourceType == ENTRY_TREE) {
|
|
//
|
|
// Paste this in the database itself, we do not have any specific app
|
|
// in which we can paste this
|
|
//
|
|
PasteSingleApp((PDBENTRY)gClipBoard.pClipBoardHead->lpData,
|
|
g_pPresentDataBase,
|
|
gClipBoard.pDataBase,
|
|
FALSE);
|
|
|
|
SendMessage(g_hwndEntryTree, WM_SETREDRAW, TRUE, 0);
|
|
|
|
} else {
|
|
|
|
PasteMultipleApps(g_pPresentDataBase);
|
|
|
|
SendMessage(g_hwndEntryTree, WM_SETREDRAW, TRUE, 0);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case TYPE_GUI_APPS:
|
|
|
|
PasteAllApps(g_pPresentDataBase);
|
|
break;
|
|
|
|
case FIX_LAYER:
|
|
|
|
PasteMultipleLayers(g_pPresentDataBase);
|
|
break;
|
|
|
|
case TYPE_GUI_LAYERS:
|
|
|
|
PasteAllLayers(g_pPresentDataBase);
|
|
break;
|
|
|
|
case FIX_FLAG:
|
|
case FIX_SHIM:
|
|
|
|
PasteShimsFlags(g_pPresentDataBase);
|
|
break;
|
|
|
|
default: assert(FALSE);
|
|
}
|
|
|
|
if (g_bIsCut) {
|
|
//
|
|
// If this routine was called because of a cut, then we do the actual cut here
|
|
//
|
|
CopyStruct* pCopyTemp = gClipBoard.pClipBoardHead;
|
|
CopyStruct* pCopyTempNext = NULL;
|
|
|
|
while (pCopyTemp) {
|
|
|
|
pCopyTempNext = pCopyTemp->pNext;
|
|
|
|
DoTheCut(gClipBoard.pDataBase,
|
|
gClipBoard.type,
|
|
gClipBoard.SourceType,
|
|
pCopyTemp->lpData,
|
|
pCopyTemp->hItem,
|
|
FALSE);
|
|
|
|
//
|
|
// NOTE: The gClipBoard.pClipBoardHead might have got changed in ValidateClipboard()
|
|
//
|
|
if (gClipBoard.pClipBoardHead == pCopyTemp) {
|
|
//
|
|
// We were not able to remove this entry, that is cut must have failed.
|
|
// An example situation is when we are trying to cut a layer that is in use by some
|
|
// entry
|
|
//
|
|
break;
|
|
} else {
|
|
pCopyTemp = gClipBoard.pClipBoardHead;
|
|
}
|
|
}
|
|
|
|
gClipBoard.pDataBase->bChanged = TRUE;
|
|
|
|
//
|
|
// Set the caption only for the tree item
|
|
//
|
|
SetCaption(TRUE, gClipBoard.pDataBase, TRUE);
|
|
gClipBoard.RemoveAll();
|
|
}
|
|
|
|
|
|
if (!g_pPresentDataBase->bChanged) {
|
|
g_pPresentDataBase->bChanged = TRUE;
|
|
SetCaption();
|
|
}
|
|
|
|
if (g_pEntrySelApp && gClipBoard.SourceType == ENTRY_TREE) {
|
|
//
|
|
// We will now try to set focus to the last entry pasted. The focus will
|
|
// be set in the entry tree.
|
|
//
|
|
TreeView_SelectItem(g_hwndEntryTree, g_pEntrySelApp->hItemExe);
|
|
|
|
} else if (gClipBoard.SourceType == LIB_TREE) {
|
|
//
|
|
// Select the first entry.
|
|
//
|
|
TreeView_SelectItem(g_hwndEntryTree, TreeView_GetRoot(g_hwndEntryTree));
|
|
}
|
|
|
|
//
|
|
// The refresh of the content list is handled in ID_EDIT_PASTE
|
|
//
|
|
End:;
|
|
|
|
}
|
|
|
|
void
|
|
ListViewSelectAll(
|
|
IN HWND hwndList
|
|
)
|
|
/*++
|
|
ListViewInvertSelection
|
|
|
|
Desc: Selects all the items for the list view
|
|
|
|
Params:
|
|
IN HWND hwndList: The handle to the list view
|
|
|
|
Return:
|
|
void
|
|
|
|
--*/
|
|
{
|
|
INT iLastindex = ListView_GetItemCount(hwndList) - 1;
|
|
|
|
if (iLastindex > -1) {
|
|
|
|
for (iLastindex; iLastindex >= 0; iLastindex--) {
|
|
|
|
ListView_SetItemState(g_hwndContentsList,
|
|
iLastindex,
|
|
LVIS_SELECTED,
|
|
LVIS_SELECTED);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ListViewInvertSelection(
|
|
IN HWND hwndList
|
|
)
|
|
/*++
|
|
ListViewInvertSelection
|
|
|
|
Desc: Inverts the selection for the list view
|
|
|
|
Params:
|
|
IN HWND hwndList: The handle to the list view
|
|
|
|
Return:
|
|
void
|
|
|
|
--*/
|
|
{
|
|
INT iLastindex = ListView_GetItemCount(hwndList) - 1;
|
|
|
|
if (iLastindex > -1) {
|
|
|
|
for (iLastindex; iLastindex >= 0; iLastindex--) {
|
|
|
|
UINT uState = ListView_GetItemState(g_hwndContentsList, iLastindex, LVIS_SELECTED);
|
|
|
|
(uState == LVIS_SELECTED) ? (uState = 0) : (uState = LVIS_SELECTED);
|
|
|
|
ListView_SetItemState(g_hwndContentsList, iLastindex, uState, LVIS_SELECTED);
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
DeleteMatchingFile(
|
|
IN OUT PMATCHINGFILE* ppMatchFirst,
|
|
IN PMATCHINGFILE pMatchToDelete,
|
|
IN HWND hwndTree,
|
|
IN HTREEITEM hItem
|
|
)
|
|
/*++
|
|
|
|
DeleteMatchingFile
|
|
|
|
Desc: Deletes a matching file tree item from the entry tree.
|
|
|
|
Params:
|
|
IN OUT PMATCHINGFILE* ppMatchFirst: The address of g_pSelEntry->pFirstMatchingFile
|
|
IN PMATCHINGFILE pMatchToDelete: The matching file that has to be deleted
|
|
IN HWND hwndTree: The handle to the entry tree
|
|
IN HTREEITEM hItem: The matching tree item that has to be deleted
|
|
--*/
|
|
{
|
|
|
|
if (ppMatchFirst == NULL || pMatchToDelete == NULL) {
|
|
assert(FALSE);
|
|
Dbg(dlError, "[DeleteMatchingFile] Invalid parameters");
|
|
return FALSE;
|
|
}
|
|
|
|
PMATCHINGFILE pMatchTemp = *ppMatchFirst, pMatchPrev = NULL;
|
|
|
|
while (NULL != pMatchTemp) {
|
|
|
|
if (pMatchTemp == pMatchToDelete) {
|
|
break;
|
|
}
|
|
|
|
pMatchPrev = pMatchTemp;
|
|
pMatchTemp = pMatchTemp->pNext;
|
|
}
|
|
|
|
if (pMatchTemp == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (pMatchPrev == NULL) {
|
|
//
|
|
//Delete first matching file
|
|
//
|
|
*ppMatchFirst = pMatchTemp->pNext;
|
|
} else {
|
|
pMatchPrev->pNext = pMatchTemp->pNext;
|
|
}
|
|
|
|
TreeView_DeleteItem(hwndTree, hItem);
|
|
|
|
delete pMatchTemp;
|
|
pMatchTemp = NULL;
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
CheckInstalled(
|
|
IN PCTSTR pszPath,
|
|
IN PCTSTR pszGUID
|
|
)
|
|
/*++
|
|
|
|
CheckInstalled
|
|
|
|
Desc: Checks if the database with path szPath and guid szGuid is an installed database
|
|
That is to say that it checks if the file exists in the %windir%AppPatch\Custom
|
|
Directory and has the same file-name as the guid. (Plus a .sdb)
|
|
|
|
Params:
|
|
IN PCTSTR szPath: The path of the database that has to be checked
|
|
IN PCTSTR szGUID: The guid of the database
|
|
--*/
|
|
{
|
|
|
|
Dbg(dlInfo, "File Name = %S", pszPath);
|
|
|
|
TCHAR szDrive[MAX_PATH],
|
|
szDir[MAX_PATH],
|
|
szFile[MAX_PATH];
|
|
CSTRING strAppPatchCustom;
|
|
CSTRING strPath;
|
|
|
|
*szDir = *szDrive = *szFile = 0;
|
|
|
|
_tsplitpath(pszPath, szDrive, szDir, szFile, NULL);
|
|
|
|
strPath = szDrive;
|
|
strPath += szDir;
|
|
|
|
if (!strAppPatchCustom.GetSystemWindowsDirectory()) {
|
|
assert(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
strAppPatchCustom += TEXT("AppPatch\\Custom\\");
|
|
|
|
if (strAppPatchCustom == strPath && lstrcmpi(pszGUID, szFile) == 0) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
DWORD WINAPI
|
|
ThreadEventKeyNotify(
|
|
LPVOID pVoid
|
|
)
|
|
/*++
|
|
|
|
ThreadEventKeyNotify
|
|
|
|
Desc: Thread routine that is responsible for automatic updating of the
|
|
Installed databases list and the per user settings list
|
|
|
|
Params:
|
|
LPVOID pVoid: Not used
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
|
|
{
|
|
DWORD dwInd;
|
|
|
|
while (TRUE) {
|
|
|
|
#ifdef HELP_BOUND_CHECK
|
|
|
|
if (s_bProcessExiting) {
|
|
|
|
if (g_hKeyNotify[IND_PERUSER]) {
|
|
REGCLOSEKEY(g_hKeyNotify[IND_PERUSER]);
|
|
}
|
|
|
|
if (g_hKeyNotify[IND_ALLUSERS]) {
|
|
REGCLOSEKEY(g_hKeyNotify[IND_ALLUSERS]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
dwInd = WaitForMultipleObjects(2, g_arrhEventNotify, FALSE, INFINITE);
|
|
|
|
switch (dwInd) {
|
|
case WAIT_OBJECT_0:
|
|
//
|
|
// We use PostMessage, so that if we get the two events in quick succession
|
|
// we do not mess up our data structures
|
|
//
|
|
PostMessage(g_hDlg, WM_USER_UPDATEPERUSER, 0, 0);
|
|
|
|
RegNotifyChangeKeyValue(g_hKeyNotify[IND_PERUSER],
|
|
TRUE,
|
|
REG_NOTIFY_CHANGE_NAME
|
|
| REG_NOTIFY_CHANGE_ATTRIBUTES
|
|
| REG_NOTIFY_CHANGE_LAST_SET,
|
|
g_arrhEventNotify[IND_PERUSER],
|
|
TRUE);
|
|
break;
|
|
|
|
case WAIT_OBJECT_0 + 1:
|
|
//
|
|
// We use PostMessage, so that if we get the two events in quick succession
|
|
// we do not mess up our data structures
|
|
//
|
|
PostMessage(g_hDlg, WM_USER_UPDATEINSTALLED, 0, 0);
|
|
|
|
RegNotifyChangeKeyValue(g_hKeyNotify[IND_ALLUSERS],
|
|
TRUE,
|
|
REG_NOTIFY_CHANGE_NAME
|
|
| REG_NOTIFY_CHANGE_ATTRIBUTES
|
|
| REG_NOTIFY_CHANGE_LAST_SET,
|
|
g_arrhEventNotify[IND_ALLUSERS],
|
|
TRUE);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
SetStatus(
|
|
IN INT iCode
|
|
)
|
|
/*++
|
|
SetStatus
|
|
|
|
Desc: Sets the text for the status control in the main window
|
|
|
|
Params:
|
|
IN INT iCode: This is the resource id of the string in the string table that
|
|
has to be displayed in the status control
|
|
|
|
Return:
|
|
void
|
|
|
|
--*/
|
|
|
|
{
|
|
SetWindowText(GetDlgItem(g_hDlg, IDC_STATUSBAR), GetString(iCode));
|
|
}
|
|
|
|
void
|
|
SetStatus(
|
|
IN PCTSTR pszMessage
|
|
)
|
|
/*++
|
|
|
|
SetStatus
|
|
|
|
Desc: Sets the text for the status control in the main window
|
|
|
|
Params:
|
|
IN PCTSTR pszMessage: The text that has to be displayed in the status control
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
SetWindowText(GetDlgItem(g_hDlg, IDC_STATUSBAR), pszMessage);
|
|
}
|
|
|
|
void
|
|
SetStatus(
|
|
IN HWND hwndStatus,
|
|
IN PCTSTR pszMessage
|
|
)
|
|
/*++
|
|
|
|
SetStatus
|
|
|
|
Desc: Sets the text for a status control
|
|
|
|
Params:
|
|
IN HWND hwndStatus: The handle to the status window
|
|
IN PCTSTR pszMessage: The text that has to be displayed in the status control
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
SetWindowText(hwndStatus, pszMessage);
|
|
}
|
|
|
|
void
|
|
SetStatus(
|
|
IN HWND hwndStatus,
|
|
IN INT iCode
|
|
)
|
|
/*++
|
|
|
|
SetStatus
|
|
|
|
Desc: Sets the text for a status control
|
|
|
|
Params:
|
|
IN INT iCode: This is the resource id of the string in the string table that
|
|
has to be displayed in the status control
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
|
|
{
|
|
SetWindowText(hwndStatus, GetString(iCode));
|
|
}
|
|
|
|
|
|
void
|
|
SetStatusDBItems(
|
|
IN HTREEITEM hItem
|
|
)
|
|
/*++
|
|
|
|
SetStausDBItems
|
|
|
|
Desc: Sets the main window status control, when the user selects some item
|
|
in the db tree(LHS)
|
|
|
|
Params:
|
|
IN HTREEITEM hItem: The tree item that the user selected
|
|
|
|
Return:
|
|
void
|
|
|
|
--*/
|
|
{
|
|
TCHAR szStatus[512];
|
|
TYPE type = GetItemType(DBTree.m_hLibraryTree, hItem);
|
|
UINT uCount = ARRAYSIZE(szStatus);
|
|
|
|
|
|
switch (type) {
|
|
case TYPE_ENTRY:
|
|
{
|
|
LPARAM lParam;
|
|
|
|
if (DBTree.GetLParam(hItem, &lParam)) {
|
|
|
|
PDBENTRY pApp = (PDBENTRY)lParam;
|
|
UINT uEntryCount = GetAppEntryCount(pApp);
|
|
|
|
*szStatus = 0;
|
|
|
|
StringCchPrintf(szStatus,
|
|
uCount,
|
|
GetString(IDS_STA_ENTRYCOUNT),
|
|
pApp->strAppName.pszString,
|
|
uEntryCount);
|
|
|
|
SetStatus(szStatus);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case FIX_SHIM:
|
|
|
|
SetStatus(IDS_STA_SHIM);
|
|
break;
|
|
|
|
case TYPE_GUI_COMMANDLINE:
|
|
|
|
SetStatus(IDS_STA_COMMANDLINE);
|
|
break;
|
|
|
|
case TYPE_GUI_EXCLUDE:
|
|
|
|
ShowExcludeStatusMessage(DBTree.m_hLibraryTree, hItem);
|
|
break;
|
|
|
|
case TYPE_GUI_INCLUDE:
|
|
|
|
ShowIncludeStatusMessage(DBTree.m_hLibraryTree, hItem);
|
|
break;
|
|
|
|
case TYPE_GUI_APPS:
|
|
|
|
if (g_pPresentDataBase) {
|
|
|
|
StringCchPrintf(szStatus,
|
|
uCount,
|
|
GetString(IDS_STA_POPCONTENTS_GUI_APPS),
|
|
g_pPresentDataBase->uAppCount);
|
|
|
|
SetStatus(szStatus);
|
|
}
|
|
|
|
break;
|
|
|
|
case TYPE_GUI_LAYERS:
|
|
|
|
if (g_pPresentDataBase) {
|
|
|
|
StringCchPrintf(szStatus,
|
|
uCount,
|
|
GetString(IDS_STA_POPCONTENTS_GUI_LAYERS),
|
|
GetLayerCount((LPARAM)g_pPresentDataBase, g_pPresentDataBase->type));
|
|
|
|
SetStatus(szStatus);
|
|
}
|
|
|
|
break;
|
|
|
|
case FIX_LAYER:
|
|
{
|
|
LPARAM lParam;
|
|
PLAYER_FIX plf;
|
|
|
|
DBTree.GetLParam(hItem, &lParam);
|
|
|
|
plf = (PLAYER_FIX)lParam;
|
|
|
|
if (plf) {
|
|
|
|
StringCchPrintf(szStatus,
|
|
uCount,
|
|
GetString(IDS_STA_POPCONTENTS_GUI_SHIMS),
|
|
GetShimFlagCount((LPARAM)plf, FIX_LAYER));
|
|
|
|
SetStatus(szStatus);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case TYPE_GUI_SHIMS:
|
|
|
|
if (g_pPresentDataBase) {
|
|
|
|
StringCchPrintf(szStatus,
|
|
uCount,
|
|
GetString(IDS_STA_POPCONTENTS_GUI_SHIMS),
|
|
g_pPresentDataBase->uShimCount);
|
|
|
|
SetStatus(szStatus);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
SetStatusStringDBTree(
|
|
IN HTREEITEM hItem
|
|
)
|
|
/*++
|
|
|
|
SetStatusStringDBTree
|
|
|
|
Desc: Given a hItem from the db tree, determines the status string to be displayed
|
|
in the status control.
|
|
Params:
|
|
IN HTREEITEM hItem: The tree item whose status string we want to display
|
|
|
|
--*/
|
|
{
|
|
HWND hwndFocus = GetFocus();
|
|
|
|
INT iCode = 0;
|
|
|
|
if (hItem == DBTree.m_hItemAllInstalled) {
|
|
iCode = IDS_STA_INSTALLED;
|
|
|
|
} else if (hItem == DBTree.m_hItemAllWorking) {
|
|
iCode = IDS_STA_WORKING;
|
|
|
|
} else if (hItem == DBTree.m_hPerUserHead) {
|
|
iCode = IDS_STA_PERUSER;
|
|
|
|
} else {
|
|
|
|
if (g_pPresentDataBase && hItem == g_pPresentDataBase->hItemDB) {
|
|
|
|
if (g_pPresentDataBase->type == DATABASE_TYPE_INSTALLED) {
|
|
iCode = IDS_DESC_INSTALLED;
|
|
|
|
} else if (g_pPresentDataBase->type == DATABASE_TYPE_WORKING) {
|
|
iCode = IDS_STA_WORKINGDB;
|
|
|
|
} else if (g_pPresentDataBase->type == DATABASE_TYPE_GLOBAL) {
|
|
iCode = IDS_SYSDB;
|
|
}
|
|
|
|
} else {
|
|
SetStatusDBItems(hItem);
|
|
}
|
|
}
|
|
|
|
if (iCode) {
|
|
SetStatus(iCode);
|
|
}
|
|
}
|
|
|
|
void
|
|
SetStatusStringEntryTree(
|
|
IN HTREEITEM hItem
|
|
)
|
|
/*++
|
|
|
|
SetStatusStringEntryTree
|
|
|
|
Desc: Given a hItem from the db tree, determines the status string to be displayed
|
|
in the status control.
|
|
Params:
|
|
IN HTREEITEM hItem: The tree item whose status string we want to display
|
|
|
|
--*/
|
|
{
|
|
HWND hwndFocus = GetFocus();
|
|
|
|
if (hwndFocus != g_hwndEntryTree) {
|
|
//
|
|
// We can come here if we we selected some item programmatically.
|
|
// But we want to show the status message in the context of the control that
|
|
// is presently selected. So do not put an assert() here.
|
|
//
|
|
return;
|
|
}
|
|
|
|
TYPE type = GetItemType(g_hwndEntryTree, hItem);
|
|
TCHAR szStatus[256];
|
|
|
|
*szStatus = 0;
|
|
|
|
if (g_pSelEntry == NULL) {
|
|
assert(FALSE);
|
|
return;
|
|
}
|
|
|
|
switch (type) {
|
|
case TYPE_ENTRY:
|
|
|
|
StringCchPrintf(szStatus,
|
|
ARRAYSIZE(szStatus),
|
|
GetString(IDS_STA_TYPE_ENTRY),
|
|
g_pSelEntry->strExeName.pszString,
|
|
g_pSelEntry->strAppName.pszString,
|
|
g_pSelEntry->strVendor.pszString);
|
|
|
|
SetStatus(szStatus);
|
|
break;
|
|
|
|
case TYPE_APPHELP_ENTRY:
|
|
|
|
SetStatus (IDS_STA_TYPE_APPHELP);
|
|
break;
|
|
|
|
case FIX_LAYER:
|
|
|
|
SetStatus (IDS_STA_FIX_LAYER);
|
|
break;
|
|
|
|
case FIX_FLAG:
|
|
case FIX_SHIM:
|
|
|
|
SetStatus (IDS_STA_FIX_SHIM);
|
|
break;
|
|
|
|
case FIX_PATCH:
|
|
|
|
SetStatus (IDS_STA_FIX_PATCH);
|
|
break;
|
|
|
|
case TYPE_GUI_PATCHES:
|
|
|
|
StringCchPrintf(szStatus,
|
|
ARRAYSIZE(szStatus),
|
|
GetString(IDS_STA_FIX_PATCHES),
|
|
g_pSelEntry->strExeName);
|
|
|
|
SetStatus(szStatus);
|
|
|
|
break;
|
|
|
|
case TYPE_GUI_LAYERS:
|
|
|
|
StringCchPrintf(szStatus,
|
|
ARRAYSIZE(szStatus),
|
|
GetString(IDS_STA_FIX_LAYERS),
|
|
g_pSelEntry->strExeName);
|
|
|
|
SetStatus(szStatus);
|
|
break;
|
|
|
|
case TYPE_GUI_SHIMS:
|
|
|
|
StringCchPrintf(szStatus,
|
|
ARRAYSIZE(szStatus),
|
|
GetString(IDS_STA_FIX_SHIMS),
|
|
g_pSelEntry->strExeName);
|
|
|
|
SetStatus(szStatus);
|
|
break;
|
|
|
|
case TYPE_GUI_MATCHING_FILES:
|
|
|
|
StringCchPrintf(szStatus,
|
|
ARRAYSIZE(szStatus),
|
|
GetString(IDS_STA_MATCHINGFILES),
|
|
g_pSelEntry->strExeName);
|
|
|
|
SetStatus(szStatus);
|
|
break;
|
|
|
|
case TYPE_MATCHING_FILE:
|
|
|
|
SetStatus (IDS_STA_MATCHINGFILE);
|
|
break;
|
|
|
|
case TYPE_MATCHING_ATTRIBUTE:
|
|
|
|
SetStatus(IDS_STA_MATCHINGATTRIBUTE);
|
|
break;
|
|
|
|
case TYPE_GUI_EXCLUDE:
|
|
|
|
ShowExcludeStatusMessage(g_hwndEntryTree, hItem);
|
|
break;
|
|
|
|
case TYPE_GUI_INCLUDE:
|
|
|
|
ShowIncludeStatusMessage(g_hwndEntryTree, hItem);
|
|
break;
|
|
|
|
case TYPE_GUI_COMMANDLINE:
|
|
|
|
SetStatus(IDS_STA_COMMANDLINE);
|
|
break;
|
|
|
|
default: SetStatus(TEXT(""));
|
|
}
|
|
}
|
|
|
|
void
|
|
OnMoveSplitter(
|
|
IN HWND hdlg,
|
|
IN LPARAM lParam,
|
|
IN BOOL bDoTheDrag,
|
|
IN INT iDiff
|
|
)
|
|
/*++
|
|
|
|
OnMoveSplitter
|
|
|
|
Desc: May move the vertical split bar (if bDoTheDrag is true),
|
|
iDiff pixels +ve units to the right. Changes the mouse cursor to
|
|
horiz. arrow if it is over the split bar
|
|
|
|
Params:
|
|
IN HWND hdlg: The main app window
|
|
IN LPARAM lParam: The mouse position
|
|
IN BOOL bDoTheDrag: Should we move the split bar
|
|
IN INT iDiff: The distance in pixels that the split bar has to
|
|
to be moved. +ve right, -ve left. Relevant only if bDoTheDrag is
|
|
TRUE
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
|
|
RECT rectDlg, rectEntryTree, rectDBTree;
|
|
HWND hwndDBTree, hwndEntryTree;
|
|
|
|
hwndDBTree = GetDlgItem(hdlg, IDC_LIBRARY);
|
|
GetWindowRect(hwndDBTree, &rectDBTree);
|
|
MapWindowPoints(NULL, hdlg, (LPPOINT)&rectDBTree, 2);
|
|
|
|
hwndEntryTree = GetDlgItem(hdlg, IDC_ENTRY);
|
|
GetWindowRect(hwndEntryTree, &rectEntryTree);
|
|
MapWindowPoints(NULL, hdlg, (LPPOINT)&rectEntryTree, 2);
|
|
|
|
GetWindowRect(hdlg, &rectDlg);
|
|
|
|
int iMX = (int)LOWORD(lParam);
|
|
int iMY = (int)HIWORD(lParam);
|
|
|
|
if (iMX > rectDBTree.right && iMX < rectEntryTree.left && iMY > rectDBTree.top && iMY < rectDBTree.bottom) {
|
|
SetCursor(LoadCursor(NULL, IDC_SIZEWE));
|
|
}
|
|
|
|
if (bDoTheDrag) {
|
|
|
|
int iDlgWidth = rectDlg.right - rectDlg.left;
|
|
|
|
//
|
|
// Enforce left and right limit
|
|
//
|
|
if ((rectDBTree.right - rectDBTree.left < 0.20 * iDlgWidth && (iDiff <= 0)) || // Not too much left
|
|
(rectDBTree.right - rectDBTree.left > 0.80 * iDlgWidth && (iDiff >= 0))) { // Not too much right
|
|
|
|
return;
|
|
|
|
} else if (iMX < iDlgWidth) {
|
|
//
|
|
// Note: We get +ve values when the mouse goes out of the window. -1 becomes 65535
|
|
//
|
|
g_iMousePressedX = iMX;
|
|
|
|
RECT rectRedraw;
|
|
SetRect(&rectRedraw, rectDBTree.left, rectDBTree.top, rectEntryTree.right, rectDBTree.bottom);
|
|
|
|
InvalidateRect(hdlg, &rectRedraw, TRUE);
|
|
|
|
SetRect(&g_rectBar,
|
|
rectDBTree.right + iDiff + 1,
|
|
rectDBTree.top,
|
|
rectEntryTree.left + iDiff - 1,
|
|
rectDBTree.bottom);
|
|
|
|
//
|
|
// Move the db tree
|
|
//
|
|
HDWP hdwp = BeginDeferWindowPos(MAIN_WINDOW_CONTROL_COUNT);
|
|
|
|
DeferWindowPos(hdwp,
|
|
hwndDBTree,
|
|
NULL,
|
|
rectDBTree.left,
|
|
rectDBTree.top,
|
|
rectDBTree.right - rectDBTree.left + iDiff ,
|
|
rectDBTree.bottom - rectDBTree.top,
|
|
REDRAW_TYPE);
|
|
|
|
//
|
|
// Move the exe tree
|
|
//
|
|
DeferWindowPos(hdwp,
|
|
hwndEntryTree,
|
|
NULL,
|
|
rectEntryTree.left + iDiff,
|
|
rectEntryTree.top,
|
|
rectEntryTree.right - rectEntryTree.left - iDiff ,
|
|
rectEntryTree.bottom - rectEntryTree.top,
|
|
REDRAW_TYPE);
|
|
//
|
|
// Move the contents list.
|
|
//
|
|
DeferWindowPos(hdwp,
|
|
GetDlgItem(hdlg, IDC_CONTENTSLIST),
|
|
NULL,
|
|
rectEntryTree.left + iDiff,
|
|
rectEntryTree.top,
|
|
rectEntryTree.right - rectEntryTree.left - iDiff ,
|
|
rectEntryTree.bottom - rectEntryTree.top,
|
|
REDRAW_TYPE);
|
|
|
|
//
|
|
// Move the description window.
|
|
//
|
|
if (g_bDescWndOn) {
|
|
|
|
HWND hwndDesc;
|
|
RECT rectDesc;
|
|
|
|
hwndDesc = GetDlgItem(hdlg, IDC_DESCRIPTION);
|
|
GetWindowRect(hwndDesc, &rectDesc);
|
|
MapWindowPoints(NULL, hdlg, (LPPOINT)&rectDesc, 2);
|
|
|
|
DeferWindowPos(hdwp,
|
|
GetDlgItem(hdlg, IDC_DESCRIPTION),
|
|
NULL,
|
|
rectDesc.left + iDiff,
|
|
rectDesc.top,
|
|
rectDesc.right - rectDesc.left - iDiff ,
|
|
100,
|
|
REDRAW_TYPE);
|
|
|
|
InvalidateRect(hwndDesc, NULL, TRUE);
|
|
UpdateWindow(hwndDesc);
|
|
}
|
|
|
|
EndDeferWindowPos(hdwp);
|
|
}
|
|
}
|
|
}
|
|
|
|
UINT
|
|
GetAppEntryCount(
|
|
IN PDBENTRY pEntry
|
|
)
|
|
/*++
|
|
Desc: Gets the number of entries in an app
|
|
|
|
Params:
|
|
IN PDBENTRY pEntry: The pointer to the first entry in the app
|
|
|
|
Return: Number of entries which are in same app as pEntry.
|
|
pEntry should point to the first entry in the app.
|
|
--*/
|
|
{
|
|
UINT uCount = 0;
|
|
|
|
while (pEntry) {
|
|
++uCount;
|
|
pEntry = pEntry->pSameAppExe;
|
|
}
|
|
|
|
return uCount;
|
|
}
|
|
|
|
void
|
|
AddToMRU(
|
|
IN CSTRING& strPath
|
|
)
|
|
/*++
|
|
|
|
AddToMRU
|
|
|
|
Desc: Adds the file name to the MRU (Most recently used files).
|
|
|
|
1. First of all tries to remove the file from the MRU.
|
|
|
|
2. Then checks if the count in the MRU is equal or greater than the
|
|
MAX_MRU_COUNT.
|
|
a) If yes, then it removes the last from the MRU
|
|
|
|
3. Adds the new file name to the MRU.
|
|
|
|
Params:
|
|
IN CSTRING& strPath: The full path of the program that has to be added
|
|
--*/
|
|
{
|
|
assert(g_strlMRU.m_uCount <= MAX_MRU_COUNT);
|
|
|
|
g_strlMRU.Remove(strPath);
|
|
|
|
if (g_strlMRU.m_uCount >= MAX_MRU_COUNT) {
|
|
g_strlMRU.RemoveLast();
|
|
}
|
|
|
|
g_strlMRU.AddStringAtBeg(strPath);
|
|
}
|
|
|
|
void
|
|
AddMRUItemsToMenu(
|
|
IN HMENU hMenu,
|
|
IN int iPos
|
|
)
|
|
/*++
|
|
|
|
AddMRUItemsToMenu
|
|
|
|
Desc: Adds the MRU menus items for the File menu
|
|
|
|
Params:
|
|
IN HMENU hMenu: The File top-level menu
|
|
IN int iPos: Position of the menu item before which to insert
|
|
the new item
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
TCHAR szRetPath[MAX_PATH];
|
|
TCHAR* pszMenuString;
|
|
MENUITEMINFO menuItem = {0};
|
|
INT iId = ID_FILE_MRU1, iIndex = 0;
|
|
PSTRLIST pHead = g_strlMRU.m_pHead;
|
|
|
|
menuItem.cbSize = sizeof (MENUITEMINFO);
|
|
menuItem.fMask = MIIM_TYPE | MIIM_ID;
|
|
menuItem.fType = MFT_STRING;
|
|
|
|
while (pHead) {
|
|
|
|
//
|
|
// Now add this to the menuItem.
|
|
//
|
|
*szRetPath = 0;
|
|
pszMenuString = FormatMRUString(pHead->szStr.pszString,
|
|
iIndex + 1,
|
|
szRetPath,
|
|
ARRAYSIZE(szRetPath));
|
|
|
|
menuItem.dwTypeData = pszMenuString;
|
|
menuItem.cch = lstrlen(pszMenuString);
|
|
menuItem.wID = iId;
|
|
|
|
InsertMenuItem(hMenu,
|
|
iPos,
|
|
TRUE,
|
|
&menuItem);
|
|
|
|
++iId;
|
|
++iPos;
|
|
++iIndex;
|
|
|
|
pHead = pHead->pNext;
|
|
}
|
|
}
|
|
|
|
void
|
|
AddMRUToFileMenu(
|
|
IN HMENU hmenuFile
|
|
)
|
|
/*++
|
|
|
|
AddMRUToFileMenu
|
|
|
|
Desc: Populates the MRU
|
|
|
|
Params:
|
|
IN HMENU hmenuFile: The File top level menu
|
|
--*/
|
|
{
|
|
HKEY hKey = NULL;
|
|
DWORD dwType = REG_SZ;
|
|
MENUITEMINFO minfo = {0};
|
|
INT iPos = 0;
|
|
LONG lResult = 0;
|
|
BOOL bValid = FALSE;
|
|
TCHAR szData[MAX_PATH + 1], szValueName[MAX_PATH + 1];
|
|
DWORD dwchSizeofValueName, dwIndexValue;
|
|
DWORD dwSizeofData;
|
|
|
|
g_strlMRU.DeleteAll();
|
|
|
|
if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_CURRENT_USER,
|
|
MRU_KEY,
|
|
0,
|
|
KEY_READ,
|
|
&hKey)) {
|
|
|
|
Dbg(dlInfo, "[AddMRUToFileMenu]: No MRU items exist, could not open - Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\CompatAdmin\\MRU");
|
|
return;
|
|
}
|
|
|
|
dwIndexValue = 0;
|
|
|
|
while (TRUE && iPos < MAX_MRU_COUNT) {
|
|
//
|
|
// Note that the values are not ordered in any particular way !
|
|
//
|
|
dwchSizeofValueName = ARRAYSIZE(szValueName);
|
|
dwSizeofData = sizeof(szData);
|
|
*szData = 0;
|
|
*szValueName = 0;
|
|
|
|
lResult = RegEnumValue(hKey,
|
|
dwIndexValue,
|
|
szValueName,
|
|
&dwchSizeofValueName,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)szData,
|
|
&dwSizeofData);
|
|
|
|
if (lResult == ERROR_NO_MORE_ITEMS) {
|
|
break;
|
|
}
|
|
|
|
if (ERROR_SUCCESS != lResult || dwType != REG_SZ) {
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
iPos = Atoi(szValueName, &bValid);
|
|
|
|
if (iPos >= 0 && bValid) {
|
|
g_strlMRU.AddStringInOrder(szData, iPos);
|
|
} else {
|
|
assert(FALSE);
|
|
}
|
|
|
|
++dwIndexValue;
|
|
}
|
|
|
|
//
|
|
// The MRU has been populated, now add these to the "File" menu Item
|
|
//
|
|
if (g_strlMRU.IsEmpty() == FALSE) {
|
|
|
|
//
|
|
// Add the separator
|
|
//
|
|
minfo.cbSize = sizeof(MENUITEMINFO);
|
|
minfo.fMask = MIIM_TYPE;
|
|
minfo.fType = MFT_SEPARATOR;
|
|
|
|
INT iPosSeparator = GetMenuItemCount(hmenuFile) - 2; // -1 for the exit menu and -1 for the separator above it
|
|
|
|
InsertMenuItem(hmenuFile,
|
|
iPosSeparator,
|
|
TRUE,
|
|
&minfo);
|
|
|
|
AddMRUItemsToMenu(hmenuFile, iPosSeparator + 1);
|
|
}
|
|
|
|
if (hKey) {
|
|
REGCLOSEKEY(hKey);
|
|
}
|
|
}
|
|
|
|
TCHAR*
|
|
FormatMRUString(
|
|
IN PCTSTR pszPath,
|
|
IN INT iIndex,
|
|
OUT PTSTR pszRetPath,
|
|
IN UINT cchRetPath
|
|
)
|
|
/*++
|
|
Desc: Formats szPath so that we can show it as a menu item in Files
|
|
Max. length of the returned string is MAX_LENGTH_MRU_MENUITEM
|
|
|
|
Params: IN PCTSTR pszPath: The complete path of the .sdb file
|
|
IN INT iIndex: The index of this MRU item. This will also serve
|
|
as the short-cut key. First MRU item will have index as 1 and number of
|
|
mru items is limited by MAX_MRU_COUNT
|
|
|
|
OUT PTSTR pzRetPath: This will have the formatted string
|
|
IN UINT cchRetPath: Number of chars that can be stored in cchRetpath.
|
|
This will include the NULL char as well.
|
|
To be safe it should be greater than 128
|
|
|
|
Return: Fills up pszPath with the formatted file name and returns a pointer to it
|
|
--*/
|
|
{
|
|
assert(cchRetPath > 128);
|
|
|
|
if (pszRetPath) {
|
|
*pszRetPath = 0;
|
|
} else {
|
|
assert(FALSE);
|
|
return TEXT("");
|
|
}
|
|
|
|
if (iIndex < 1 || iIndex > MAX_MRU_COUNT) {
|
|
assert(FALSE);
|
|
return TEXT("");
|
|
}
|
|
|
|
TCHAR szResult[MAX_PATH * 2],
|
|
szDir[MAX_PATH],
|
|
szFileName[MAX_PATH],
|
|
szExt[MAX_PATH];
|
|
|
|
szResult[0] = TEXT('&');
|
|
|
|
//
|
|
// We have already checked that iIndex is a valid +ve integer and is within proper bounds
|
|
//
|
|
_itot(iIndex, szResult + 1, 10);
|
|
|
|
StringCchCat(szResult, ARRAYSIZE(szResult), TEXT(" "));
|
|
|
|
if (lstrlen(pszPath) <= MAX_LENGTH_MRU_MENUITEM) {
|
|
StringCchCat(szResult, ARRAYSIZE(szResult), pszPath);
|
|
goto End;
|
|
}
|
|
|
|
_tsplitpath(pszPath,
|
|
szResult + lstrlen(szResult),
|
|
szDir,
|
|
szFileName,
|
|
szExt);
|
|
|
|
//
|
|
// Now for the directory. Start from the front and add MAX_DIR_SHOW chars to szResult.
|
|
//
|
|
_tcsncat(szResult, szDir, MAX_DIR_SHOW);
|
|
|
|
if (lstrlen(szDir) > MAX_DIR_SHOW) {
|
|
StringCchCat(szResult, ARRAYSIZE(szResult), TEXT("...\\"));
|
|
}
|
|
|
|
//
|
|
// For the file-name get the first MAX_FILE_SHOW chars and then append ... to the file name, after that put the .SDB
|
|
//
|
|
_tcsncat(szResult, szFileName, MAX_FILE_SHOW);
|
|
|
|
if (lstrlen(szFileName) <= MAX_FILE_SHOW) {
|
|
StringCchCat(szResult, ARRAYSIZE(szResult), szExt);
|
|
} else {
|
|
StringCchCat(szResult, ARRAYSIZE(szResult), TEXT("..."));
|
|
}
|
|
|
|
End:
|
|
SafeCpyN(pszRetPath, szResult, cchRetPath);
|
|
|
|
return pszRetPath;
|
|
}
|
|
|
|
void
|
|
RefreshMRUMenu(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
RefreshMRUMenu
|
|
|
|
Desc: Refreshes the "File" menu contents (the MRU), this is called when we
|
|
open a new database or save, save as an existing one.
|
|
--*/
|
|
{
|
|
|
|
HMENU hMenu = GetMenu(g_hDlg);
|
|
MENUITEMINFO minfo = {0};
|
|
//
|
|
// Get the file menu
|
|
//
|
|
hMenu = GetSubMenu(hMenu, 0);
|
|
|
|
//
|
|
// Delete all the MRU items from the menu
|
|
//
|
|
for (UINT uCount = 0; uCount < g_strlMRU.m_uCount; ++uCount) {
|
|
DeleteMenu(hMenu, ID_FILE_MRU1 + uCount, MF_BYCOMMAND);
|
|
}
|
|
|
|
INT iItemCount = GetMenuItemCount(hMenu);
|
|
|
|
//
|
|
// Check if the separator for the top of the MRU list exists or not, if not add it
|
|
//
|
|
minfo.cbSize = sizeof(minfo);
|
|
minfo.fMask = MIIM_TYPE;
|
|
|
|
if (GetMenuItemID(hMenu, iItemCount - 3) == ID_FILE_PROPERTIES) {
|
|
//
|
|
// There was no MRU file in the MRU menu, so we have to add the separator
|
|
//
|
|
minfo.fType = MFT_SEPARATOR;
|
|
|
|
InsertMenuItem(hMenu,
|
|
iItemCount - 2, // Before the sep. of the Exit menu
|
|
TRUE,
|
|
&minfo);
|
|
|
|
++iItemCount;
|
|
}
|
|
|
|
AddMRUItemsToMenu(hMenu, iItemCount - 2); // -1 for the EXIT, -1 for the separator above that
|
|
|
|
DrawMenuBar(g_hDlg);
|
|
}
|
|
|
|
BOOL
|
|
LoadDataBase(
|
|
IN TCHAR* szPath
|
|
)
|
|
/*++
|
|
LoadDataBase
|
|
|
|
Desc: Load the database file with szPath as its path
|
|
|
|
Params:
|
|
IN TCHAR* szPath: Path of the database to be loaded
|
|
|
|
Return:
|
|
FALSE: If the database could not be loadd
|
|
TRUE: False otherwise
|
|
--*/
|
|
{
|
|
PDATABASE pOldPresentDatabase = NULL;
|
|
PDATABASE pDataBase = new DATABASE(DATABASE_TYPE_WORKING);
|
|
|
|
if (pDataBase == NULL) {
|
|
MEM_ERR;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// NOTE: If GetDatabaseEntries() returns succeeds then it set the g_pPresentDataBase to pDataBase,
|
|
// so after it returns successfully, the g_pPresentDataBase is changed.
|
|
//
|
|
pOldPresentDatabase = g_pPresentDataBase;
|
|
|
|
BOOL bReturn = GetDatabaseEntries(szPath, pDataBase);
|
|
|
|
g_pPresentDataBase = pOldPresentDatabase;
|
|
|
|
if (!bReturn) {
|
|
delete pDataBase;
|
|
return FALSE;
|
|
}
|
|
|
|
if (!DBTree.AddWorking(pDataBase)) {
|
|
|
|
delete pDataBase;
|
|
return FALSE;
|
|
}
|
|
|
|
pDataBase->bChanged = FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AddRegistryKeys(
|
|
void
|
|
)
|
|
/*++
|
|
AddRegistryKeys
|
|
|
|
Desc: Adds the necessary registry keys so that we can listen on them.
|
|
If they are not there, we cannot listen on them and update the
|
|
list of all installed databases and the per-user settings
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
|
|
HKEY hKey = NULL, hKeySub = NULL;
|
|
DWORD dwDisposition;
|
|
|
|
if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_CURRENT_USER,
|
|
KEY_BASE,
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hKey)) {
|
|
|
|
assert(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
if (ERROR_SUCCESS != RegCreateKeyEx(hKey,
|
|
TEXT("AppCompatFlags"),
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
NULL,
|
|
&hKeySub,
|
|
&dwDisposition)) {
|
|
|
|
REGCLOSEKEY(hKey);
|
|
return FALSE;
|
|
}
|
|
|
|
REGCLOSEKEY(hKey);
|
|
hKey = hKeySub;
|
|
|
|
if (ERROR_SUCCESS != RegCreateKeyEx(hKey,
|
|
TEXT("InstalledSDB"),
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
NULL,
|
|
&hKeySub,
|
|
&dwDisposition)) {
|
|
|
|
REGCLOSEKEY(hKey);
|
|
return FALSE;
|
|
}
|
|
|
|
REGCLOSEKEY(hKey);
|
|
REGCLOSEKEY(hKeySub);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
SetTBButtonStatus(
|
|
IN HWND hwndTB,
|
|
IN HWND hwndControl
|
|
)
|
|
{
|
|
/*++
|
|
|
|
SetTBButtonStatus
|
|
|
|
Desc: This routine is called when the selection changes for DB Tree or the
|
|
Entry Tree. This routine enables/disables some of the tool bar buttons as
|
|
deemed necessary.
|
|
Params:
|
|
IN HWND hwndTB: The handle to the tool bar
|
|
IN HWND hwndControl: The control on which the sel change has taken place
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
|
|
TYPE typeDB = TYPE_UNKNOWN;
|
|
BOOL bEnable;
|
|
|
|
if (hwndControl == DBTree.m_hLibraryTree) {
|
|
|
|
if (g_pPresentDataBase) {
|
|
typeDB = g_pPresentDataBase->type;
|
|
}
|
|
|
|
bEnable = g_pPresentDataBase && typeDB == DATABASE_TYPE_WORKING;
|
|
|
|
//
|
|
// Set the options as for working databases
|
|
//
|
|
EnableToolBarButton(hwndTB, ID_FILE_SAVE, bEnable);
|
|
EnableToolBarButton(hwndTB, ID_DATABASE_CLOSE, bEnable);
|
|
EnableToolBarButton(hwndTB, ID_FIX_CREATEANAPPLICATIONFIX, bEnable);
|
|
|
|
//
|
|
// AppHelp mechanism is not supported in win2k
|
|
//
|
|
EnableToolBarButton(hwndTB,
|
|
ID_FIX_CREATEANEWAPPHELPMESSAGE,
|
|
(g_bWin2K) ? FALSE : bEnable);
|
|
|
|
EnableToolBarButton(hwndTB, ID_FIX_CREATENEWLAYER, bEnable);
|
|
|
|
bEnable = (g_pSelEntry != NULL);
|
|
EnableToolBarButton(hwndTB, ID_FIX_EXECUTEAPPLICATION, bEnable);
|
|
|
|
} else if (hwndControl == g_hwndEntryTree) {
|
|
//
|
|
// Run Program button
|
|
//
|
|
bEnable = (g_pSelEntry != NULL);
|
|
EnableToolBarButton(hwndTB, ID_FIX_EXECUTEAPPLICATION, bEnable);
|
|
}
|
|
}
|
|
|
|
void
|
|
ShowToolBarToolTips(
|
|
IN HWND hdlg,
|
|
IN LPARAM lParam
|
|
)
|
|
|
|
/*++
|
|
|
|
ShowToolBarToolTips
|
|
|
|
Desc: Gets the text for the tool bar tool tips
|
|
|
|
Params:
|
|
IN HWND hdlg: The main app window
|
|
IN LPARAM lParam: The lParam for WM_NOTIFY
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
LPTOOLTIPTEXT lpttt;
|
|
INT idStringResource = 0;
|
|
|
|
lpttt = (LPTOOLTIPTEXT)lParam;
|
|
|
|
if (lpttt == NULL) {
|
|
assert(FALSE);
|
|
return;
|
|
}
|
|
|
|
lpttt->hinst = g_hInstance;
|
|
|
|
//
|
|
// Specify the resource identifier of the descriptive
|
|
// text for the given button.
|
|
//
|
|
switch (lpttt->hdr.idFrom) {
|
|
case ID_FILE_NEW:
|
|
|
|
idStringResource = IDS_TB_TT_NEW;
|
|
break;
|
|
|
|
case ID_FILE_OPEN:
|
|
|
|
idStringResource = IDS_TB_TT_OPEN;
|
|
break;
|
|
|
|
case ID_FILE_SAVE:
|
|
|
|
idStringResource = IDS_TB_TT_SAVE;
|
|
break;
|
|
|
|
case ID_FIX_CREATEANAPPLICATIONFIX:
|
|
|
|
idStringResource = IDS_TB_TT_CREATEFIX;
|
|
break;
|
|
|
|
case ID_FIX_CREATEANEWAPPHELPMESSAGE:
|
|
|
|
idStringResource = IDS_TB_TT_CREATEAPPHELP;
|
|
break;
|
|
|
|
case ID_FIX_CREATENEWLAYER:
|
|
|
|
idStringResource = IDS_TB_TT_CREATEMODE;
|
|
break;
|
|
|
|
case ID_FIX_EXECUTEAPPLICATION:
|
|
|
|
idStringResource = IDS_TB_TT_RUN;
|
|
break;
|
|
|
|
case ID_TOOLS_SEARCHFORFIXES:
|
|
|
|
idStringResource = IDS_TB_TT_SEARCH;
|
|
break;
|
|
|
|
case ID_SEARCH_QUERYDATABASE:
|
|
|
|
idStringResource = IDS_TB_TT_QUERY;
|
|
break;
|
|
}
|
|
|
|
lpttt->lpszText = MAKEINTRESOURCE(idStringResource);
|
|
}
|
|
|
|
PSHIM_FIX_LIST
|
|
IsLUARedirectFSPresent(
|
|
IN PDBENTRY pEntry
|
|
)
|
|
/*++
|
|
IsLUARedirectFSPresent
|
|
|
|
Desc: Checks if the entry pEntry has LUARedirectFS shim applied to it
|
|
|
|
Params:
|
|
IN PDBENTRY pEntry: The entry for which we want to make the check
|
|
|
|
Return:
|
|
PSHIM_FIX_LIST for LUARedirectFS: if the entry has LUARedirectFS applied
|
|
NULL: otherwise
|
|
|
|
Notes: Because we always add the shims in the LUA layer individually,
|
|
we only check in the shim fix list of this entry.
|
|
--*/
|
|
{
|
|
if (pEntry == NULL) {
|
|
assert(FALSE);
|
|
return NULL;
|
|
}
|
|
|
|
PSHIM_FIX_LIST psfl = pEntry->pFirstShim;
|
|
|
|
while (psfl) {
|
|
|
|
if (psfl->pShimFix->strName == TEXT("LUARedirectFS")) {
|
|
return psfl;
|
|
}
|
|
|
|
psfl = psfl->pNext;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
CreateNewAppHelp(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
CreateNewAppHelp
|
|
|
|
Desc: Creates a new AppHelp fix. This routine starts up the wizard to do the job
|
|
and if an entry has been created (the user pressed finish button) adds the entry
|
|
into the database
|
|
--*/
|
|
{
|
|
CAppHelpWizard wizAppHelp;
|
|
PDATABASE pCurrentSelectedDB = GetCurrentDB();
|
|
BOOL bReturn = FALSE;
|
|
|
|
if (pCurrentSelectedDB == NULL) {
|
|
assert(FALSE);
|
|
return;
|
|
}
|
|
|
|
bReturn = wizAppHelp.BeginWizard(g_hDlg, NULL, pCurrentSelectedDB);
|
|
|
|
if (bReturn == FALSE) {
|
|
return;
|
|
}
|
|
|
|
PDBENTRY pEntry = new DBENTRY;
|
|
|
|
if (pEntry == NULL) {
|
|
MEM_ERR;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// This will point to the entry that conflicts.
|
|
//
|
|
PDBENTRY pEntryConflict = NULL;
|
|
|
|
if (CheckIfConflictingEntry(pCurrentSelectedDB,
|
|
&wizAppHelp.m_Entry,
|
|
NULL,
|
|
&pEntryConflict)) {
|
|
|
|
StringCchPrintf(g_szData,
|
|
ARRAYSIZE(g_szData),
|
|
GetString(IDS_CONFLICT_CREATE_EDIT),
|
|
pEntryConflict->strExeName.pszString,
|
|
pEntryConflict->strAppName.pszString);
|
|
|
|
if (IDNO == MessageBox(g_hDlg,
|
|
g_szData,
|
|
g_szAppName,
|
|
MB_ICONQUESTION | MB_YESNO)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// NOTE: "=" is overloaded. Does not modify the pNext member.
|
|
//
|
|
*pEntry = wizAppHelp.m_Entry;
|
|
|
|
PDBENTRY pApp;
|
|
BOOL bNew;
|
|
|
|
pApp = AddExeInApp(pEntry, &bNew, pCurrentSelectedDB);
|
|
|
|
if (bNew == TRUE) {
|
|
//
|
|
// This means that this is going to be a new application. There does not
|
|
// exist anyt app with this name in the database
|
|
//
|
|
pApp = NULL;
|
|
}
|
|
|
|
DBTree.AddNewExe(pCurrentSelectedDB, pEntry, pApp);
|
|
|
|
if (!pCurrentSelectedDB->bChanged) {
|
|
pCurrentSelectedDB->bChanged = TRUE;
|
|
SetCaption();
|
|
}
|
|
}
|
|
|
|
void
|
|
ModifyAppHelp(
|
|
void
|
|
)
|
|
/*++
|
|
ModifyAppHelp
|
|
|
|
Desc: Modifies or adds a new apphelp entry for the presently selected
|
|
application fix
|
|
--*/
|
|
{
|
|
PDBENTRY pEntry = g_pSelEntry;
|
|
CAppHelpWizard Wiz;
|
|
PDBENTRY pEntryConflict = NULL;
|
|
PDATABASE pCurrentSelectedDB = GetCurrentDB();
|
|
BOOL bRet = FALSE;
|
|
|
|
if (pEntry == NULL || pCurrentSelectedDB == NULL) {
|
|
assert(FALSE);
|
|
return;
|
|
}
|
|
|
|
bRet = Wiz.BeginWizard(g_hDlg, pEntry, pCurrentSelectedDB);
|
|
|
|
if (bRet) {
|
|
|
|
if (CheckIfConflictingEntry(pCurrentSelectedDB,
|
|
&Wiz.m_Entry,
|
|
pEntry,
|
|
&pEntryConflict)) {
|
|
|
|
*g_szData = 0;
|
|
|
|
StringCchPrintf(g_szData,
|
|
ARRAYSIZE(g_szData),
|
|
GetString(IDS_ENTRYCONFLICT),
|
|
pEntryConflict->strExeName.pszString,
|
|
pEntryConflict->strAppName.pszString);
|
|
|
|
if (IDNO == MessageBox(g_hDlg,
|
|
g_szData,
|
|
g_szAppName,
|
|
MB_ICONQUESTION | MB_YESNO)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// NOTE: "=" is overloaded. Does not modify the pNext member.
|
|
//
|
|
*pEntry = Wiz.m_Entry;
|
|
|
|
SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
UpdateEntryTreeView(g_pEntrySelApp, g_hwndEntryTree);
|
|
SetCursor(LoadCursor(NULL, IDC_ARROW));
|
|
|
|
if (!pCurrentSelectedDB->bChanged) {
|
|
pCurrentSelectedDB->bChanged = TRUE;
|
|
SetCaption();
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
CreateNewAppFix(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
CreateNewAppFix
|
|
|
|
Desc: Creates a new application fix.
|
|
|
|
--*/
|
|
{
|
|
CShimWizard Wiz;
|
|
BOOL bShouldStartLUAWizard;
|
|
PDATABASE pCurrentSelectedDB = GetCurrentDB();
|
|
BOOL bReturn = FALSE;
|
|
PDBENTRY pEntryConflict = NULL;
|
|
|
|
if (pCurrentSelectedDB == NULL) {
|
|
assert(FALSE);
|
|
return;
|
|
}
|
|
|
|
bReturn = Wiz.BeginWizard(g_hDlg, NULL, pCurrentSelectedDB, &bShouldStartLUAWizard);
|
|
|
|
if (bReturn == FALSE) {
|
|
return;
|
|
}
|
|
|
|
if (CheckIfConflictingEntry(pCurrentSelectedDB,
|
|
&Wiz.m_Entry,
|
|
NULL,
|
|
&pEntryConflict)) {
|
|
|
|
*g_szData = 0;
|
|
|
|
StringCchPrintf(g_szData,
|
|
ARRAYSIZE(g_szData),
|
|
GetString(IDS_CONFLICT_CREATE_EDIT),
|
|
pEntryConflict->strExeName.pszString,
|
|
pEntryConflict->strAppName.pszString);
|
|
|
|
if (IDNO == MessageBox(g_hDlg,
|
|
g_szData,
|
|
g_szAppName,
|
|
MB_ICONQUESTION | MB_YESNO)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
PDBENTRY pEntry = new DBENTRY;
|
|
|
|
if (pEntry == NULL) {
|
|
MEM_ERR;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// "=" is overloaded. Does not modify the pNext member.
|
|
//
|
|
*pEntry = Wiz.m_Entry;
|
|
|
|
BOOL bNew;
|
|
PDBENTRY pApp = AddExeInApp(pEntry, &bNew, pCurrentSelectedDB);
|
|
|
|
if (bNew == TRUE) {
|
|
pApp = NULL;
|
|
}
|
|
|
|
DBTree.AddNewExe(pCurrentSelectedDB, pEntry, pApp);
|
|
|
|
if (!pCurrentSelectedDB->bChanged) {
|
|
pCurrentSelectedDB->bChanged = TRUE;
|
|
SetCaption();
|
|
}
|
|
|
|
if (bShouldStartLUAWizard) {
|
|
LuaBeginWizard(g_hDlg, pEntry, pCurrentSelectedDB);
|
|
}
|
|
}
|
|
|
|
void
|
|
ChangeEnableStatus(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
ChangeEnableStatus
|
|
|
|
Desc: Toggles the status of the presently selected entry.
|
|
If the entry is disabled, the fixes will no longer be applied to it.
|
|
|
|
Notes: No point in calling this function if the user is not an admin
|
|
|
|
--*/
|
|
{
|
|
if (g_pSelEntry == NULL) {
|
|
ASSERT(FALSE);
|
|
return;
|
|
}
|
|
|
|
BOOL bFlags = !g_pSelEntry->bDisablePerMachine;
|
|
|
|
if (SetDisabledStatus(HKEY_LOCAL_MACHINE, g_pSelEntry->szGUID, bFlags)) {
|
|
|
|
if (bFlags == FALSE) {
|
|
//
|
|
// We have enabled the fix, we need too flush the cache
|
|
//
|
|
FlushCache();
|
|
}
|
|
|
|
g_pSelEntry->bDisablePerMachine = bFlags;
|
|
|
|
//
|
|
// Just update the icon
|
|
//
|
|
TVITEM Item;
|
|
|
|
Item.mask = TVIF_SELECTEDIMAGE | TVIF_IMAGE;
|
|
Item.hItem = g_pSelEntry->hItemExe;
|
|
|
|
if (bFlags) {
|
|
|
|
//
|
|
// This is disabled
|
|
//
|
|
Item.iImage = IMAGE_WARNING;
|
|
Item.iSelectedImage = IMAGE_WARNING;
|
|
SetStatus(GetString(IDS_STA_DISABLED));
|
|
|
|
} else {
|
|
|
|
Item.iImage = LookupFileImage(g_hImageList,
|
|
g_pSelEntry->strExeName,
|
|
IMAGE_APPLICATION,
|
|
g_uImageRedirector,
|
|
ARRAYSIZE(g_uImageRedirector));
|
|
|
|
Item.iSelectedImage = Item.iImage;
|
|
|
|
SetStatus(GetString(IDS_STA_ENABLED));
|
|
}
|
|
|
|
TreeView_SetItem(g_hwndEntryTree, &Item);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
ModifyAppFix(
|
|
void
|
|
)
|
|
/*++
|
|
ModifyAppFix
|
|
|
|
Desc: Modifies the selected entry in the Entry Tree. This routine will either modify the
|
|
app fix or might create a new one if the selected entry had only AppHelp.
|
|
|
|
This routine calls the CShimWizard::BeginWizard to do the job
|
|
--*/
|
|
{
|
|
|
|
CShimWizard Wiz;
|
|
BOOL bShouldStartLUAWizard;
|
|
PDBENTRY pEntryConflict = NULL;
|
|
PDBENTRY pEntry = g_pSelEntry;
|
|
PDATABASE pCurrentSelectedDB = GetCurrentDB();
|
|
BOOL bRet = FALSE;
|
|
|
|
if (g_pSelEntry == NULL || pCurrentSelectedDB == NULL) {
|
|
assert(FALSE);
|
|
return;
|
|
}
|
|
|
|
bRet = Wiz.BeginWizard(g_hDlg, pEntry, pCurrentSelectedDB, &bShouldStartLUAWizard);
|
|
|
|
if (bRet) {
|
|
|
|
if (CheckIfConflictingEntry(pCurrentSelectedDB,
|
|
&Wiz.m_Entry,
|
|
pEntry,
|
|
&pEntryConflict)) {
|
|
|
|
*g_szData = 0;
|
|
|
|
StringCchPrintf(g_szData,
|
|
ARRAYSIZE(g_szData),
|
|
GetString(IDS_CONFLICT_CREATE_EDIT),
|
|
pEntryConflict->strExeName.pszString,
|
|
pEntryConflict->strAppName.pszString);
|
|
|
|
if (IDNO == MessageBox(g_hDlg,
|
|
g_szData,
|
|
g_szAppName,
|
|
MB_ICONQUESTION | MB_YESNO)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
|
|
*pEntry = Wiz.m_Entry;
|
|
|
|
UpdateEntryTreeView(g_pEntrySelApp, g_hwndEntryTree);
|
|
|
|
SetCursor(LoadCursor(NULL, IDC_ARROW));
|
|
|
|
if (!pCurrentSelectedDB->bChanged) {
|
|
pCurrentSelectedDB->bChanged = TRUE;
|
|
SetCaption();
|
|
}
|
|
|
|
if (bShouldStartLUAWizard) {
|
|
LuaBeginWizard(g_hDlg, pEntry, pCurrentSelectedDB);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
CreateNewLayer(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
CreateNewLayer
|
|
|
|
Desc: Calls CCustomLayer::AddCustomLayer to create a new layer (compatibility mode)
|
|
Calls DBTree.AddNewLayer() to add the new layer to the tree
|
|
--*/
|
|
{
|
|
CCustomLayer CustomLayer;
|
|
HWND hWnd = GetFocus();
|
|
PDATABASE pCurrentSelectedDB = GetCurrentDB();
|
|
PLAYER_FIX plfNew = NULL;
|
|
|
|
if (pCurrentSelectedDB == NULL) {
|
|
assert(FALSE);
|
|
return;
|
|
}
|
|
|
|
plfNew = new LAYER_FIX(TRUE);
|
|
|
|
if (plfNew == NULL) {
|
|
MEM_ERR;
|
|
return;
|
|
}
|
|
|
|
if (CustomLayer.AddCustomLayer(plfNew, pCurrentSelectedDB)) {
|
|
//
|
|
// Add this new layer in the datbase.
|
|
//
|
|
plfNew->pNext = pCurrentSelectedDB->pLayerFixes;
|
|
pCurrentSelectedDB->pLayerFixes = plfNew;
|
|
|
|
if (!pCurrentSelectedDB->bChanged) {
|
|
pCurrentSelectedDB->bChanged = TRUE;
|
|
SetCaption();
|
|
}
|
|
|
|
DBTree.AddNewLayer(pCurrentSelectedDB, plfNew, TRUE);
|
|
|
|
} else {
|
|
delete plfNew;
|
|
}
|
|
|
|
SetFocus(hWnd);
|
|
}
|
|
|
|
void
|
|
OnDelete(
|
|
void
|
|
)
|
|
/*++
|
|
OnDelete
|
|
|
|
Desc: Handles the ID_EDIT_DELETE message to delete an entry.
|
|
The entry can be either in the entry-tree, db tree or the contents list.
|
|
--*/
|
|
{
|
|
HWND hwndFocus = GetFocus();
|
|
SOURCE_TYPE srcType;
|
|
HTREEITEM hItem = NULL;
|
|
PDATABASE pCurrentSelectedDB = GetCurrentDB();
|
|
|
|
if (pCurrentSelectedDB == NULL) {
|
|
assert(FALSE);
|
|
return;
|
|
}
|
|
|
|
if (hwndFocus == DBTree.m_hLibraryTree || hwndFocus == g_hwndEntryTree) {
|
|
|
|
hItem = TreeView_GetSelection(hwndFocus);
|
|
|
|
TYPE type = (TYPE)GetItemType(hwndFocus, hItem);
|
|
|
|
if (hwndFocus == g_hwndEntryTree) {
|
|
srcType = ENTRY_TREE;
|
|
} else {
|
|
srcType = LIB_TREE;
|
|
}
|
|
|
|
LPARAM lParam;
|
|
|
|
CTree::GetLParam(hwndFocus, hItem, &lParam);
|
|
DoTheCut(pCurrentSelectedDB, type, srcType, (LPVOID)lParam, hItem, TRUE);
|
|
|
|
} else {
|
|
//
|
|
// Handle delete for the contents list.
|
|
//
|
|
HTREEITEM hParent = NULL;
|
|
LVITEM lvItem;
|
|
TYPE type;
|
|
|
|
lvItem.mask = LVIF_PARAM;
|
|
lvItem.iItem = 0;
|
|
|
|
if (!ListView_GetItem(g_hwndContentsList, &lvItem)) {
|
|
assert(FALSE);
|
|
return;
|
|
}
|
|
|
|
if (lvItem.lParam > TYPE_NULL) {
|
|
PDS_TYPE pdsType = (PDS_TYPE)lvItem.lParam;
|
|
type = pdsType->type;
|
|
} else {
|
|
type = (TYPE)lvItem.lParam;
|
|
}
|
|
|
|
if (type == TYPE_ENTRY) {
|
|
hParent = pCurrentSelectedDB->hItemAllApps;
|
|
|
|
} else if (type == FIX_LAYER) {
|
|
hParent = pCurrentSelectedDB->hItemAllLayers;
|
|
|
|
} else {
|
|
assert(FALSE);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Get the selected items and then delete them
|
|
//
|
|
UINT uSelectedCount = ListView_GetSelectedCount(g_hwndContentsList);
|
|
INT iLastIndex = ListView_GetItemCount(g_hwndContentsList) - 1;
|
|
|
|
lvItem.mask = LVIF_PARAM | LVIF_STATE;
|
|
lvItem.stateMask = LVIS_SELECTED;
|
|
|
|
for (UINT uFoundSelected = 1;
|
|
iLastIndex >= 0 && uFoundSelected <= uSelectedCount;
|
|
--iLastIndex) {
|
|
|
|
lvItem.iItem = iLastIndex;
|
|
lvItem.iSubItem = 0;
|
|
|
|
if (!ListView_GetItem(g_hwndContentsList, &lvItem)) {
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
if (lvItem.state & LVIS_SELECTED) {
|
|
|
|
hItem = DBTree.FindChild(hParent, lvItem.lParam);
|
|
assert(hItem);
|
|
|
|
DoTheCut(pCurrentSelectedDB, type, ENTRY_LIST, (LPVOID)lvItem.lParam, hItem, TRUE);
|
|
|
|
uFoundSelected++;
|
|
}
|
|
}
|
|
}
|
|
|
|
pCurrentSelectedDB->bChanged = TRUE;
|
|
|
|
SetCaption(TRUE, pCurrentSelectedDB);
|
|
}
|
|
|
|
void
|
|
CreateNewDatabase(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
CreateNewDatabase
|
|
|
|
Desc: Creates a new database and adds it to the db tree
|
|
calling DBTree.AddWorking()
|
|
--*/
|
|
{
|
|
PDATABASE pDatabaseNew = new DATABASE(DATABASE_TYPE_WORKING);
|
|
|
|
if (pDatabaseNew == NULL) {
|
|
MEM_ERR;
|
|
return;
|
|
}
|
|
|
|
pDatabaseNew->bChanged = FALSE;
|
|
|
|
DataBaseList.Add(pDatabaseNew);
|
|
|
|
g_pEntrySelApp = g_pSelEntry = NULL;
|
|
SetCaption();
|
|
|
|
++g_uNextDataBaseIndex;
|
|
|
|
//
|
|
// Now update the screen
|
|
//
|
|
DBTree.AddWorking(pDatabaseNew);
|
|
|
|
TreeDeleteAll(g_hwndEntryTree);
|
|
|
|
SetFocus(DBTree.m_hLibraryTree);
|
|
}
|
|
|
|
void
|
|
OnDatabaseClose(
|
|
void
|
|
)
|
|
/*++
|
|
OnDatabaseClose
|
|
|
|
Desc: Calls CloseDataBase to close a database.
|
|
This is called on ID_DATABASE_CLOSE message
|
|
|
|
--*/
|
|
{
|
|
PDATABASE pCurrentSelectedDB = GetCurrentDB();
|
|
|
|
if (pCurrentSelectedDB == NULL) {
|
|
|
|
MessageBox(g_hDlg,
|
|
GetString(IDS_CANNOTBECLOSED),
|
|
g_szAppName,
|
|
MB_ICONWARNING);
|
|
return;
|
|
}
|
|
|
|
TYPE typeDB = pCurrentSelectedDB->type;
|
|
|
|
if (typeDB == DATABASE_TYPE_WORKING) {
|
|
CloseDataBase(pCurrentSelectedDB);
|
|
}
|
|
}
|
|
|
|
void
|
|
DatabaseSaveAll(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
DatabaseSaveAll
|
|
|
|
Desc: Saves all the working databases
|
|
|
|
--*/
|
|
{
|
|
PDATABASE g_pOldPresentDataBase = g_pPresentDataBase;
|
|
|
|
g_pPresentDataBase = DataBaseList.pDataBaseHead;
|
|
|
|
while (g_pPresentDataBase) {
|
|
|
|
if (g_pPresentDataBase->bChanged
|
|
|| NotCompletePath(g_pPresentDataBase->strPath)) {
|
|
|
|
BOOL bReturn = TRUE;
|
|
|
|
if (NotCompletePath(g_pPresentDataBase->strPath)) {
|
|
bReturn = SaveDataBaseAs(g_pPresentDataBase);
|
|
} else {
|
|
bReturn = SaveDataBase(g_pPresentDataBase,
|
|
g_pPresentDataBase->strPath);
|
|
}
|
|
|
|
if (bReturn == FALSE) {
|
|
|
|
CSTRING strMessage;
|
|
strMessage.Sprintf(GetString(IDS_NOTSAVED), g_pPresentDataBase->strName);
|
|
|
|
if (g_hDlg && strMessage.pszString) {
|
|
MessageBox(g_hDlg,
|
|
strMessage.pszString,
|
|
g_szAppName,
|
|
MB_ICONWARNING);
|
|
}
|
|
}
|
|
}
|
|
|
|
g_pPresentDataBase = g_pPresentDataBase->pNext;
|
|
}
|
|
|
|
g_pPresentDataBase = g_pOldPresentDataBase;
|
|
|
|
if (g_pPresentDataBase) {
|
|
TreeView_SelectItem(DBTree.m_hLibraryTree, g_pPresentDataBase->hItemDB);
|
|
} else {
|
|
TreeView_SelectItem(DBTree.m_hLibraryTree, DBTree.m_hLibraryTree);
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
ModifyLayer(
|
|
void
|
|
)
|
|
/*++
|
|
ModifyLayer
|
|
|
|
Desc: Modifies a layer. Calls CustomLayer.EditCustomLayer to do the actual job
|
|
--*/
|
|
{
|
|
CCustomLayer clayer;
|
|
BOOL bOk = FALSE;
|
|
HWND hwndGetFocus = GetFocus();
|
|
PDATABASE pCurrentSelectedDB = GetCurrentDB();
|
|
|
|
if (hwndGetFocus == DBTree.m_hLibraryTree) {
|
|
|
|
HTREEITEM hSelectedItem = TreeView_GetSelection(hwndGetFocus);
|
|
|
|
if (hSelectedItem && GetItemType(DBTree.m_hLibraryTree, hSelectedItem) == FIX_LAYER) {
|
|
|
|
LPARAM lParamMode;
|
|
|
|
if (DBTree.GetLParam(hSelectedItem, &lParamMode)) {
|
|
bOk = clayer.EditCustomLayer((PLAYER_FIX)lParamMode, pCurrentSelectedDB);
|
|
}
|
|
|
|
if (bOk) {
|
|
//
|
|
// We have to refresh all the layers. We have to refresh all the layers
|
|
// Because the UI provides the user the flexibility to edit more than one layer :(
|
|
//
|
|
if (!pCurrentSelectedDB->bChanged) {
|
|
pCurrentSelectedDB->bChanged = TRUE;
|
|
SetCaption();
|
|
}
|
|
|
|
DBTree.RefreshAllLayers(pCurrentSelectedDB);
|
|
|
|
hSelectedItem = DBTree.FindChild(pCurrentSelectedDB->hItemAllLayers,
|
|
lParamMode);
|
|
|
|
TreeView_SelectItem(DBTree.m_hLibraryTree, hSelectedItem);
|
|
SetStatusStringDBTree(hSelectedItem);
|
|
}
|
|
}
|
|
}
|
|
|
|
SetFocus(hwndGetFocus);
|
|
return bOk;
|
|
}
|
|
|
|
void
|
|
OnRename(
|
|
void
|
|
)
|
|
/*++
|
|
OnRename
|
|
|
|
Desc: Processes the ID_EDIT_RENAME message to handle renaming of databases,
|
|
compatibility modes and applications.
|
|
--*/
|
|
{
|
|
HWND hwndFocus = GetFocus();
|
|
INT_PTR iStyle;
|
|
TYPE type;
|
|
|
|
if (hwndFocus == DBTree.m_hLibraryTree) {
|
|
|
|
HTREEITEM hItemSelected = TreeView_GetSelection(hwndFocus);
|
|
|
|
if (hItemSelected == NULL) {
|
|
return;
|
|
}
|
|
|
|
iStyle = GetWindowLongPtr(hwndFocus, GWL_STYLE);
|
|
iStyle |= TVS_EDITLABELS;
|
|
|
|
SetWindowLongPtr(hwndFocus, GWL_STYLE, iStyle);
|
|
|
|
HWND hwndText = NULL;
|
|
type = (TYPE)GetItemType(hwndFocus, hItemSelected);
|
|
|
|
switch(type) {
|
|
case TYPE_ENTRY:
|
|
case FIX_LAYER:
|
|
case DATABASE_TYPE_WORKING:
|
|
|
|
hwndText = TreeView_EditLabel(hwndFocus, hItemSelected);
|
|
break;
|
|
}
|
|
|
|
} else if (hwndFocus == g_hwndContentsList) {
|
|
|
|
INT iSelected = ListView_GetSelectionMark(g_hwndContentsList);
|
|
|
|
if (iSelected == -1) {
|
|
return;
|
|
}
|
|
|
|
iStyle = GetWindowLongPtr(hwndFocus, GWL_STYLE);
|
|
iStyle |= LVS_EDITLABELS;
|
|
|
|
SetWindowLongPtr(hwndFocus, GWL_STYLE, iStyle);
|
|
|
|
LVITEM lvItem;
|
|
|
|
lvItem.mask = LVIF_PARAM;
|
|
lvItem.iItem = iSelected;
|
|
lvItem.iSubItem = 0;
|
|
|
|
if(!ListView_GetItem(g_hwndContentsList, &lvItem)) {
|
|
return;
|
|
}
|
|
|
|
assert(lvItem.lParam);
|
|
|
|
type = (TYPE)ConvertLparam2Type(lvItem.lParam);
|
|
|
|
if (type == TYPE_ENTRY || type == FIX_LAYER) {
|
|
ListView_EditLabel(g_hwndContentsList, iSelected);
|
|
}
|
|
}
|
|
}
|
|
|
|
INT_PTR
|
|
ShowDBPropertiesDlgProcOnInitDialog(
|
|
IN HWND hdlg,
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
|
|
ShowDBPropertiesDlgProcOnInitDialog
|
|
|
|
Desc: Handles the WM_INITDIALOG for the database properties dialog box
|
|
|
|
Params:
|
|
IN HWND hdlg: The database properties dialog box
|
|
IN LPARAM lParam: The database pointer whose properties we want to see
|
|
|
|
Return:
|
|
TRUE
|
|
|
|
--*/
|
|
{
|
|
PDATABASE pDatabase = (PDATABASE)lParam;
|
|
FILETIME localtime;
|
|
SYSTEMTIME systime;
|
|
WIN32_FILE_ATTRIBUTE_DATA attr;
|
|
TCHAR szBuffer[MAX_PATH];
|
|
PDBENTRY pApp;
|
|
PDBENTRY pEntry;
|
|
INT iAppCount = 0;
|
|
INT iEntryCount = 0;
|
|
|
|
*szBuffer = 0;
|
|
|
|
if (pDatabase == NULL) {
|
|
assert(FALSE);
|
|
goto End;
|
|
}
|
|
|
|
//
|
|
// If we are trying to show the properties of the system database, the apps must be
|
|
// loaded first
|
|
//
|
|
if (pDatabase->type == DATABASE_TYPE_GLOBAL && !g_bMainAppExpanded) {
|
|
|
|
SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
INT iResult = ShowMainEntries(hdlg);
|
|
|
|
if (iResult == -1) {
|
|
|
|
SetWindowLongPtr(hdlg, DWLP_MSGRESULT, TRUE);
|
|
SetStatus(g_hwndStatus, CSTRING(IDS_LOADINGMAIN));
|
|
SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
|
|
} else {
|
|
SetCursor(LoadCursor(NULL, IDC_ARROW));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Show the friendly name
|
|
//
|
|
SetDlgItemText(hdlg, IDC_NAME, pDatabase->strName);
|
|
|
|
//
|
|
// Show the path
|
|
//
|
|
SetDlgItemText(hdlg, IDC_PATH, pDatabase->strPath);
|
|
|
|
//
|
|
// Show the GUID
|
|
//
|
|
SetDlgItemText(hdlg, IDC_GUID, pDatabase->szGUID);
|
|
|
|
//
|
|
// Show the various dates: creation, modification and access dates
|
|
//
|
|
if (GetFileAttributesEx(pDatabase->strPath, GetFileExInfoStandard, &attr)) {
|
|
|
|
//
|
|
// Creation date-time
|
|
//
|
|
FileTimeToLocalFileTime(&attr.ftCreationTime, &localtime);
|
|
FileTimeToSystemTime(&localtime, &systime);
|
|
FormatDate(&systime, szBuffer, ARRAYSIZE(szBuffer));
|
|
SetDlgItemText(hdlg, IDC_DATE_CREATED, szBuffer);
|
|
|
|
//
|
|
// Modification date-time
|
|
//
|
|
FileTimeToLocalFileTime(&attr.ftLastWriteTime, &localtime);
|
|
FileTimeToSystemTime(&localtime, &systime);
|
|
FormatDate(&systime, szBuffer, ARRAYSIZE(szBuffer));
|
|
SetDlgItemText(hdlg, IDC_DATE_MODIFIED, szBuffer);
|
|
|
|
//
|
|
// Access date-time
|
|
//
|
|
FileTimeToLocalFileTime(&attr.ftLastAccessTime, &localtime);
|
|
FileTimeToSystemTime(&localtime, &systime);
|
|
FormatDate(&systime, szBuffer, ARRAYSIZE(szBuffer));
|
|
SetDlgItemText(hdlg, IDC_DATE_ACCESSED, szBuffer);
|
|
|
|
} else {
|
|
|
|
//
|
|
// New database: does not exist on disk.
|
|
//
|
|
SetDlgItemText(hdlg, IDC_DATE_CREATED, GetString(IDS_NOTCREATED));
|
|
SetDlgItemText(hdlg, IDC_DATE_MODIFIED, TEXT(""));
|
|
SetDlgItemText(hdlg, IDC_DATE_ACCESSED, TEXT(""));
|
|
}
|
|
|
|
//
|
|
// Get Application count and entry count
|
|
//
|
|
pApp = pDatabase->pEntries;
|
|
|
|
while (pApp) {
|
|
|
|
++iAppCount;
|
|
pEntry = pApp;
|
|
|
|
while (pEntry) {
|
|
iEntryCount++;
|
|
pEntry = pEntry->pSameAppExe;
|
|
}
|
|
|
|
pApp = pApp->pNext;
|
|
}
|
|
|
|
//
|
|
// App-Count
|
|
//
|
|
*szBuffer = 0;
|
|
SetDlgItemText(hdlg, IDC_APP_COUNT, _itot(iAppCount, szBuffer, 10));
|
|
|
|
//
|
|
// Entry-Count
|
|
//
|
|
*szBuffer = 0;
|
|
SetDlgItemText(hdlg, IDC_ENTRY_COUNT, _itot(iEntryCount, szBuffer, 10));
|
|
|
|
//
|
|
// Get the number of custom compatibility modes
|
|
//
|
|
INT iModeCount = 0;
|
|
PLAYER_FIX plf = pDatabase->pLayerFixes;
|
|
|
|
while (plf) {
|
|
++iModeCount;
|
|
plf = plf->pNext;
|
|
}
|
|
|
|
//
|
|
// Layer count
|
|
//
|
|
|
|
*szBuffer = 0;
|
|
SetDlgItemText(hdlg, IDC_MODE_COUNT, _itot(iModeCount, szBuffer, 10));
|
|
|
|
|
|
//
|
|
// We need to have protected access because, the installed list data structure
|
|
// might get modified if somebody does a (un)install when we are iterating
|
|
// the list in CheckIfInstalledDB()
|
|
//
|
|
// ********** Warning *****************************************************
|
|
//
|
|
// Do not do EnterCriticalSection(g_csInstalledList) in CheckIfInstalledDB()
|
|
// because CheckIfInstalledDB() is called by Qyery db as well when it tries
|
|
// to evaluate expressions and it might already have done a
|
|
// EnterCriticalSection(g_csInstalledList)
|
|
// and then we will get a deadlock
|
|
//
|
|
// *************************************************************************
|
|
//
|
|
EnterCriticalSection(&g_csInstalledList);
|
|
//
|
|
// Installed
|
|
//
|
|
SetDlgItemText(hdlg,
|
|
IDC_INSTALLED,
|
|
CheckIfInstalledDB(pDatabase->szGUID) ? GetString(IDS_YES):GetString(IDS_NO));
|
|
|
|
LeaveCriticalSection(&g_csInstalledList);
|
|
|
|
End:
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
INT_PTR
|
|
CALLBACK
|
|
ShowDBPropertiesDlgProc(
|
|
IN HWND hdlg,
|
|
IN UINT uMsg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
|
|
ShowDBPropertiesDlgProc
|
|
|
|
Desc: Shows the properties of the selected database
|
|
|
|
Params: Standard dialog handler parameters
|
|
|
|
IN HWND hDlg
|
|
IN UINT uMsg
|
|
IN WPARAM wParam
|
|
IN LPARAM lParam: contains the pointer to the selected database
|
|
|
|
Return: Standard dialog handler return
|
|
|
|
--*/
|
|
{
|
|
int wCode = LOWORD(wParam);
|
|
int wNotifyCode = HIWORD(wParam);
|
|
|
|
switch (uMsg) {
|
|
case WM_INITDIALOG:
|
|
|
|
ShowDBPropertiesDlgProcOnInitDialog(hdlg, lParam);
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
{
|
|
switch (wCode) {
|
|
case IDOK:
|
|
case IDCANCEL:
|
|
|
|
EndDialog(hdlg, TRUE);
|
|
break;
|
|
|
|
default: return FALSE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default: return FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
INT_PTR
|
|
CALLBACK
|
|
EventDlgProc(
|
|
IN HWND hdlg,
|
|
IN UINT uMsg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
Desc: Dialog Proc for the events dialog.
|
|
|
|
Params: Standard dialog handler parameters
|
|
|
|
IN HWND hDlg
|
|
IN UINT uMsg
|
|
IN WPARAM wParam
|
|
IN LPARAM lParam
|
|
|
|
Return: Standard dialog handler return
|
|
--*/
|
|
{
|
|
int wCode = LOWORD(wParam);
|
|
int wNotifyCode = HIWORD(wParam);
|
|
|
|
switch (uMsg) {
|
|
case WM_INITDIALOG:
|
|
{
|
|
g_hwndEventsWnd = hdlg;
|
|
|
|
HWND hwndEventsList = GetDlgItem(g_hwndEventsWnd, IDC_LIST);
|
|
g_iEventCount = 0;
|
|
|
|
ListView_SetImageList(hwndEventsList, g_hImageList, LVSIL_SMALL);
|
|
|
|
ListView_SetExtendedListViewStyleEx(hwndEventsList,
|
|
0,
|
|
LVS_EX_LABELTIP | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
|
|
|
|
InsertColumnIntoListView(hwndEventsList,
|
|
GetString(IDS_EVENT_COL_TIME),
|
|
EVENTS_COLUMN_TIME,
|
|
30);
|
|
|
|
InsertColumnIntoListView(hwndEventsList,
|
|
GetString(IDS_EVENT_COL_MSG),
|
|
EVENTS_COLUMN_MSG,
|
|
70);
|
|
|
|
ListView_SetColumnWidth(hwndEventsList, 1, LVSCW_AUTOSIZE_USEHEADER);
|
|
|
|
RECT r;
|
|
|
|
GetWindowRect(hdlg, &r);
|
|
s_cEventWidth = r.right - r.left;
|
|
s_cEventHeight = r.bottom - r.top;
|
|
|
|
//
|
|
// Disable the min/maximize menu in the system window. This is needed because otherwise
|
|
// the user can minimize the events window and if he maximizes and restores the
|
|
// main window, our events window will pop-up.
|
|
//
|
|
// The events window gets popped up if it has been created when we do a restore
|
|
// for the main window
|
|
//
|
|
HMENU hSysmenu = GetSystemMenu(hdlg, FALSE);
|
|
|
|
EnableMenuItem(hSysmenu, SC_MINIMIZE, MF_GRAYED);
|
|
EnableMenuItem(hSysmenu, SC_MAXIMIZE, MF_GRAYED);
|
|
|
|
SetFocus(hwndEventsList);
|
|
break;
|
|
}
|
|
|
|
case WM_SIZE:
|
|
|
|
EventsWindowSize(hdlg);
|
|
break;
|
|
|
|
case WM_GETMINMAXINFO:
|
|
{
|
|
MINMAXINFO* pmmi = (MINMAXINFO*)lParam;
|
|
|
|
pmmi->ptMinTrackSize.x = 300;
|
|
pmmi->ptMinTrackSize.y = 100;
|
|
|
|
return 0;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
|
|
switch (wCode) {
|
|
case IDCANCEL:
|
|
|
|
g_hwndEventsWnd = NULL;
|
|
DestroyWindow(hdlg);
|
|
break;
|
|
|
|
default: return FALSE;
|
|
}
|
|
|
|
break;
|
|
|
|
default: return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
AppendEvent(
|
|
IN INT iType,
|
|
IN TCHAR* pszTimestamp,
|
|
IN TCHAR* pszMsg,
|
|
IN BOOL bAddToFile // DEF = FALSE
|
|
)
|
|
/*++
|
|
|
|
AppendEvent
|
|
|
|
Desc: Adds a new description to the events window, if it is visible also opens
|
|
events log file and appends it to that
|
|
|
|
Params:
|
|
IN INT iType: The type of the event.
|
|
|
|
One of: EVENT_LAYER_COPYOK
|
|
EVENT_ENTRY_COPYOK
|
|
EVENT_SYSTEM_RENAME
|
|
EVENT_CONFLICT_ENTRY
|
|
|
|
IN TCHAR* pszTimestamp: Timestamp when the event occurred
|
|
IN TCHAR* pszMsg: The message to be displayed
|
|
IN BOOL bAddToFile(FALSE): Whether we want to append the event to the log file
|
|
As of now always FALSE
|
|
--*/
|
|
{
|
|
TCHAR szTime[256];
|
|
LVITEM lvi;
|
|
INT iIndex = -1;
|
|
|
|
ZeroMemory(&lvi, sizeof(lvi));
|
|
lvi.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
|
|
|
|
if (pszTimestamp == NULL) {
|
|
//
|
|
// Get the time
|
|
//
|
|
SYSTEMTIME st;
|
|
GetLocalTime(&st);
|
|
|
|
*szTime = 0;
|
|
FormatDate(&st, szTime, ARRAYSIZE(szTime));
|
|
pszTimestamp = szTime;
|
|
}
|
|
|
|
if (g_hwndEventsWnd) {
|
|
|
|
HWND hwndEventsList = GetDlgItem(g_hwndEventsWnd, IDC_LIST);
|
|
|
|
switch (iType) {
|
|
|
|
case EVENT_LAYER_COPYOK:
|
|
case EVENT_ENTRY_COPYOK:
|
|
|
|
lvi.iImage = IMAGE_EVENT_INFO;
|
|
break;
|
|
|
|
case EVENT_SYSTEM_RENAME:
|
|
|
|
lvi.iImage = IMAGE_EVENT_WARNING;
|
|
break;
|
|
|
|
case EVENT_CONFLICT_ENTRY:
|
|
|
|
lvi.iImage = IMAGE_EVENT_ERROR;
|
|
break;
|
|
}
|
|
|
|
lvi.pszText = pszTimestamp;
|
|
lvi.iSubItem = EVENTS_COLUMN_TIME;
|
|
lvi.lParam = iType;
|
|
lvi.iItem = 0;
|
|
|
|
iIndex = ListView_InsertItem(hwndEventsList, &lvi);
|
|
ListView_SetItemText(hwndEventsList, iIndex, EVENTS_COLUMN_MSG, pszMsg);
|
|
}
|
|
|
|
if (bAddToFile) {
|
|
//
|
|
// So append this to the file
|
|
//
|
|
FILE* fp = _tfopen(TEXT("events.log"), TEXT("a+"));
|
|
|
|
if (fp == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
fwprintf(fp, TEXT("%d %s; %s;"), iType, pszTimestamp, pszMsg);
|
|
fclose(fp);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
EventsWindowSize(
|
|
IN HWND hDlg
|
|
)
|
|
/*++
|
|
Desc: Handles the WM_SIZE for the event dialog
|
|
|
|
Params:
|
|
IN HWND hDlg: The events dialog
|
|
--*/
|
|
{
|
|
|
|
RECT rDlg;
|
|
|
|
if (s_cEventHeight == 0 || s_cEventWidth == 0) {
|
|
return;
|
|
}
|
|
|
|
GetWindowRect(hDlg, &rDlg);
|
|
|
|
int nWidth = rDlg.right - rDlg.left;
|
|
int nHeight = rDlg.bottom - rDlg.top;
|
|
|
|
int deltaW = nWidth - s_cEventWidth;
|
|
int deltaH = nHeight - s_cEventHeight;
|
|
|
|
HWND hwnd;
|
|
RECT r;
|
|
|
|
//
|
|
// List
|
|
//
|
|
hwnd = GetDlgItem(hDlg, IDC_LIST);
|
|
|
|
GetWindowRect(hwnd, &r);
|
|
MapWindowPoints(NULL, hDlg, (LPPOINT)&r, 2);
|
|
|
|
MoveWindow(hwnd,
|
|
r.left,
|
|
r.top,
|
|
r.right - r.left + deltaW,
|
|
r.bottom - r.top + deltaH,
|
|
TRUE);
|
|
|
|
s_cEventHeight = nHeight;
|
|
s_cEventWidth = nWidth;
|
|
ListView_SetColumnWidth(hwnd, 1, LVSCW_AUTOSIZE_USEHEADER);
|
|
}
|
|
|
|
void
|
|
UpdateControls(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
UpdateControls
|
|
|
|
Desc: Updates/redraws the controls when we need to update them, this will be needed
|
|
when we show the save as dialog box or the open dialog box.
|
|
The controls below the dialog box need to be repainted.
|
|
--*/
|
|
{
|
|
UpdateWindow(DBTree.m_hLibraryTree);
|
|
UpdateWindow(g_hwndToolBar);
|
|
UpdateWindow(g_hwndStatus);
|
|
UpdateWindow(g_hwndRichEdit);
|
|
|
|
if (g_bIsContentListVisible) {
|
|
UpdateWindow(g_hwndContentsList);
|
|
} else {
|
|
UpdateWindow(g_hwndEntryTree);
|
|
}
|
|
}
|
|
|
|
void
|
|
ProcessSwitches(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
ProcessSwitches
|
|
|
|
Desc: Processes the various switches. The switches have to be prefixed with
|
|
either a '-' or a '/'
|
|
Present switches are:
|
|
1. x: Expert mode
|
|
--*/
|
|
{
|
|
INT iArgc = 0;
|
|
LPWSTR* arParams = CommandLineToArgvW(GetCommandLineW(), &iArgc);
|
|
|
|
if (arParams) {
|
|
|
|
*g_szAppPath = 0;
|
|
|
|
GetModuleFileName(g_hInstance, g_szAppPath, ARRAYSIZE(g_szAppPath) - 1);
|
|
|
|
for (int iIndex = 1; iIndex < iArgc; ++iIndex) {
|
|
|
|
if (arParams[iIndex][0] == TEXT('-') || arParams[iIndex][0] == TEXT('/')) {
|
|
|
|
switch (arParams[iIndex][1]) {
|
|
case TEXT('X'):
|
|
case TEXT('x'):
|
|
|
|
g_bExpert = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
GlobalFree(arParams);
|
|
}
|
|
}
|
|
|
|
void
|
|
OnExitCleanup(
|
|
void
|
|
)
|
|
/*++
|
|
OnExitCleanup
|
|
|
|
Desc: Does cleaning up of critical sections, other stuff.
|
|
This module is called when we are sure that we are going to exit
|
|
|
|
--*/
|
|
{
|
|
g_strlMRU.DeleteAll();
|
|
|
|
InstalledDataBaseList.RemoveAll();
|
|
CleanupDbSupport(&GlobalDataBase);
|
|
|
|
//
|
|
// NOTE: It is possible that after we have deleted the cs, some other thread might try
|
|
// to use it.
|
|
// So this function should not be called in the release bits.
|
|
// HELP_BOUND_CHECK should not be defined.
|
|
//
|
|
DeleteCriticalSection(&g_critsectShowMain);
|
|
DeleteCriticalSection(&s_csExpanding);
|
|
DeleteCriticalSection(&g_csInstalledList);
|
|
|
|
if (g_arrhEventNotify[IND_PERUSER]) {
|
|
CloseHandle(g_arrhEventNotify[IND_PERUSER]);
|
|
}
|
|
|
|
if (g_arrhEventNotify[IND_ALLUSERS]) {
|
|
CloseHandle(g_arrhEventNotify[IND_ALLUSERS]);
|
|
}
|
|
|
|
if (g_hThreadWait) {
|
|
CloseHandle(g_hThreadWait);
|
|
}
|
|
|
|
ImageList_Destroy(g_hImageList);
|
|
ImageList_Destroy(s_hImageListToolBar);
|
|
ImageList_Destroy(s_hImageListToolBarHot);
|
|
}
|
|
|
|
void
|
|
ShowIncludeStatusMessage(
|
|
IN HWND hwndTree,
|
|
IN HTREEITEM hItem
|
|
)
|
|
/*++
|
|
|
|
ShowIncludeStatusMessage
|
|
|
|
Desc: Sets the status message when the htree item in question is an "include" item
|
|
|
|
Params:
|
|
IN HWND hwndTree: The handle to the tree. Should be one of
|
|
g_hwndTree or DBTree.m_hLibraryTree
|
|
|
|
IN HTREEITEM hItem: The tree-item for which we need the status message
|
|
--*/
|
|
{
|
|
TVITEM tvi;
|
|
|
|
*g_szData = 0;
|
|
|
|
tvi.mask = TVIF_TEXT;
|
|
tvi.hItem = hItem;
|
|
tvi.pszText = g_szData;
|
|
tvi.cchTextMax = ARRAYSIZE(g_szData);
|
|
|
|
if (TreeView_GetItem(hwndTree, &tvi)) {
|
|
//
|
|
// Special status messages if we have * or .EXE
|
|
//
|
|
if (lstrcmpi(g_szData, TEXT("*")) == 0) {
|
|
SetStatus(IDS_STA_ALL_INCLUDED);
|
|
|
|
} else if (lstrcmpi(g_szData, GetString(IDS_INCLUDEMODULE)) == 0) {
|
|
SetStatus(IDS_STA_EXE_INCLUDED);
|
|
|
|
} else {
|
|
//
|
|
// Default inlude message
|
|
//
|
|
SetStatus(IDS_STA_INCLUDE);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ShowExcludeStatusMessage(
|
|
IN HWND hwndTree,
|
|
IN HTREEITEM hItem
|
|
)
|
|
/*++
|
|
|
|
ShowExcludeStatusMessage
|
|
|
|
Desc: Sets the status message when the htree item in question is a "exclude" item
|
|
|
|
Params:
|
|
IN HWND hwndTree: The handle to the tree. Should be one of
|
|
g_hwndTree or DBTree.m_hLibraryTree
|
|
|
|
IN HTREEITEM hItem: The tree-item for which we need the status message
|
|
--*/
|
|
{
|
|
TVITEM tvi;
|
|
|
|
*g_szData = 0;
|
|
tvi.mask = TVIF_TEXT;
|
|
tvi.hItem = hItem;
|
|
tvi.pszText = g_szData;
|
|
tvi.cchTextMax = ARRAYSIZE(g_szData);
|
|
|
|
if (TreeView_GetItem(DBTree.m_hLibraryTree, &tvi)) {
|
|
//
|
|
// Special status messages if we have * or .EXE
|
|
//
|
|
if (lstrcmpi(g_szData, TEXT("*")) == 0) {
|
|
SetStatus(IDS_STA_ALL_EXCLUDED);
|
|
} else if (lstrcmpi(g_szData, GetString(IDS_INCLUDEMODULE)) == 0) {
|
|
SetStatus(IDS_STA_EXE_EXCLUDED);
|
|
} else {
|
|
//
|
|
// Default exclude message
|
|
//
|
|
SetStatus(IDS_STA_EXCLUDE);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ShowHelp(
|
|
IN HWND hdlg,
|
|
IN WPARAM wCode
|
|
)
|
|
/*++
|
|
|
|
ShowHelp
|
|
|
|
Desc: Shows the help window(s) for CompatAdmin
|
|
|
|
Params:
|
|
IN HWND hdlg: The main app window
|
|
IN WPARAM wCode: The menu item chosen
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
TCHAR szDrive[MAX_PATH * 2], szDir[MAX_PATH];
|
|
INT iType = 0;
|
|
|
|
*szDir = *szDrive = 0;
|
|
|
|
_tsplitpath(g_szAppPath, szDrive, szDir, NULL, NULL);
|
|
|
|
StringCchCat(szDrive, ARRAYSIZE(szDrive), szDir);
|
|
StringCchCat(szDrive, ARRAYSIZE(szDrive), TEXT("CompatAdmin.chm"));
|
|
|
|
switch (wCode) {
|
|
case ID_HELP_TOPICS:
|
|
|
|
iType = HH_DISPLAY_TOC;
|
|
break;
|
|
|
|
case ID_HELP_INDEX:
|
|
|
|
iType = HH_DISPLAY_INDEX;
|
|
break;
|
|
|
|
case ID_HELP_SEARCH:
|
|
|
|
iType = HH_DISPLAY_SEARCH;
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
if (iType != HH_DISPLAY_SEARCH) {
|
|
HtmlHelp(GetDesktopWindow(), szDrive, iType, 0);
|
|
} else {
|
|
|
|
HH_FTS_QUERY Query;
|
|
|
|
ZeroMemory(&Query, sizeof(Query));
|
|
Query.cbStruct = sizeof(Query);
|
|
HtmlHelp(GetDesktopWindow(), szDrive, iType, (DWORD_PTR)&Query);
|
|
}
|
|
}
|
|
|
|
void
|
|
ShowEventsWindow(
|
|
void
|
|
)
|
|
/*++
|
|
ShowEventsWindow
|
|
|
|
Desc: Shows the events window. (This is not the same as the shim log)
|
|
|
|
Params:
|
|
void
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
HWND hwnd = NULL;
|
|
|
|
if (g_hwndEventsWnd) {
|
|
//
|
|
// If we have the events window already, then just display it and
|
|
// set the focus to it
|
|
//
|
|
ShowWindow(g_hwndEventsWnd, SW_SHOWNORMAL);
|
|
SetFocus(GetDlgItem(g_hwndEventsWnd, IDC_LIST));
|
|
} else {
|
|
//
|
|
// We need to create the events window
|
|
//
|
|
hwnd = CreateDialog(g_hInstance,
|
|
MAKEINTRESOURCE(IDD_EVENTS),
|
|
GetDesktopWindow(),
|
|
EventDlgProc);
|
|
|
|
ShowWindow(hwnd, SW_NORMAL);
|
|
}
|
|
}
|
|
|
|
void
|
|
OnEntryTreeSelChange(
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
|
|
OnEntryTreeSelChange
|
|
|
|
Desc: Handles the TVN_SELCHANGED for the entry tree (RHS)
|
|
|
|
Params:
|
|
IN LPARAM lParam: The lParam that comes with WM_NOTIFY
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
LPNMTREEVIEW pnmtv = (LPNMTREEVIEW)lParam;
|
|
|
|
if (pnmtv == NULL) {
|
|
return;
|
|
}
|
|
|
|
HTREEITEM hItem = pnmtv->itemNew.hItem;
|
|
|
|
if (hItem != 0) {
|
|
//
|
|
// Now we have to find the root entry, because that is the exe
|
|
//
|
|
HTREEITEM hItemParent = TreeView_GetParent(g_hwndEntryTree, hItem);
|
|
|
|
while (hItemParent != NULL) {
|
|
hItem = hItemParent;
|
|
hItemParent = TreeView_GetParent(g_hwndEntryTree, hItem);
|
|
}
|
|
|
|
TVITEM Item;
|
|
|
|
Item.mask = TVIF_PARAM;
|
|
Item.hItem = hItem;
|
|
|
|
if (!TreeView_GetItem(g_hwndEntryTree, &Item)) {
|
|
goto End;
|
|
}
|
|
|
|
TYPE type = (TYPE)GetItemType(g_hwndEntryTree, hItem);
|
|
|
|
if (type == TYPE_UNKNOWN) {
|
|
goto End;
|
|
}
|
|
|
|
if (type == TYPE_ENTRY) {
|
|
PDBENTRY pEntry = (PDBENTRY)Item.lParam;
|
|
g_pSelEntry = pEntry;
|
|
} else {
|
|
//
|
|
// CAUTION: Note that when we shut down CompatAdmin, then it is possible
|
|
// that we have deleted all the entries from the database, but not
|
|
// from the entry tree. In this case if we get focus there then
|
|
// the lParam will point to some invalid entry.
|
|
//
|
|
goto End;
|
|
}
|
|
|
|
SetStatusStringEntryTree(pnmtv->itemNew.hItem);
|
|
SetTBButtonStatus(g_hwndToolBar, g_hwndEntryTree);
|
|
|
|
CSTRING strToolTip;
|
|
TCHAR szText[256];
|
|
*szText = 0;
|
|
CTree::GetTreeItemText(g_hwndEntryTree,
|
|
pnmtv->itemNew.hItem,
|
|
szText,
|
|
ARRAYSIZE(szText));
|
|
|
|
LPARAM lParamTreeItem;
|
|
|
|
CTree::GetLParam(g_hwndEntryTree, pnmtv->itemNew.hItem, &lParamTreeItem);
|
|
|
|
GetDescriptionString(lParamTreeItem,
|
|
strToolTip,
|
|
NULL,
|
|
szText,
|
|
pnmtv->itemNew.hItem,
|
|
g_hwndEntryTree);
|
|
|
|
if (strToolTip.Length() > 0) {
|
|
SetDescription(szText, strToolTip.pszString);
|
|
} else {
|
|
SetDescription(NULL, TEXT(""));
|
|
}
|
|
}
|
|
|
|
End:
|
|
return;
|
|
}
|
|
|
|
BOOL
|
|
EndListViewLabelEdit(
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
|
|
EndListViewLabelEdit
|
|
|
|
Desc: Processes LVN_ENDLABELEDIT message for the contents list
|
|
|
|
Params:
|
|
EndListViewLabelEdit: The lParam that comes with WM_NOTIFY
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
g_hwndEditText = NULL;
|
|
|
|
NMLVDISPINFO FAR* pLvd = (NMLVDISPINFO FAR*)lParam;
|
|
LVITEM lvItem;
|
|
|
|
BOOL fValid = TRUE;
|
|
|
|
if (pLvd == NULL) {
|
|
fValid = FALSE;
|
|
goto end;
|
|
}
|
|
|
|
lvItem = pLvd->item;
|
|
|
|
if (lvItem.pszText == NULL) {
|
|
fValid = FALSE;
|
|
goto end;
|
|
}
|
|
|
|
TCHAR szText[256];
|
|
|
|
*szText = 0;
|
|
SafeCpyN(szText, lvItem.pszText, ARRAYSIZE(szText));
|
|
|
|
if (CSTRING::Trim(szText) == 0) {
|
|
fValid = FALSE;
|
|
goto end;
|
|
}
|
|
|
|
lvItem.lParam = NULL;
|
|
lvItem.mask = LVIF_PARAM;
|
|
|
|
if (!ListView_GetItem(g_hwndContentsList, &lvItem)) {
|
|
|
|
assert(FALSE);
|
|
fValid = FALSE;
|
|
goto end;
|
|
}
|
|
|
|
TYPE type = ConvertLparam2Type(lvItem.lParam);
|
|
|
|
switch (type) {
|
|
case TYPE_ENTRY:
|
|
{
|
|
PDBENTRY pEntry = (PDBENTRY)lvItem.lParam;
|
|
|
|
assert(pEntry);
|
|
PDBENTRY pApp = g_pPresentDataBase->pEntries;
|
|
|
|
if (!IsValidAppName(szText)) {
|
|
//
|
|
// The app name contains invalid chars
|
|
//
|
|
DisplayInvalidAppNameMessage(g_hDlg);
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check if we have some app of the same name that we are trying to give...
|
|
//
|
|
while (pApp) {
|
|
|
|
if (pApp->strAppName == szText) {
|
|
//
|
|
// Yes, we have, so do not allow this name
|
|
//
|
|
MessageBox(g_hDlg, GetString(IDS_SAMEAPPEXISTS), g_szAppName, MB_ICONWARNING);
|
|
fValid = FALSE;
|
|
}
|
|
|
|
pApp = pApp->pNext;
|
|
}
|
|
|
|
//
|
|
// Now change the name of all the entries for this app
|
|
//
|
|
while (pEntry) {
|
|
pEntry->strAppName = szText;
|
|
pEntry = pEntry->pSameAppExe;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case FIX_LAYER:
|
|
{
|
|
PLAYER_FIX plf = (PLAYER_FIX)lvItem.lParam;
|
|
|
|
if (plf == NULL) {
|
|
assert(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
if (FindFix(szText, FIX_LAYER, g_pPresentDataBase)) {
|
|
//
|
|
// A layer with the same name already exists in the system or the
|
|
// present database
|
|
//
|
|
MessageBox(g_hDlg,
|
|
GetString(IDS_LAYEREXISTS),
|
|
g_szAppName,
|
|
MB_ICONWARNING);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
plf->strName = szText;
|
|
}
|
|
|
|
break;
|
|
|
|
default: fValid = FALSE;
|
|
}// switch
|
|
|
|
end:
|
|
INT_PTR iStyle = GetWindowLongPtr(g_hwndContentsList, GWL_STYLE);
|
|
|
|
iStyle &= ~LVS_EDITLABELS;
|
|
|
|
SetWindowLongPtr(g_hwndContentsList, GWL_STYLE, iStyle);
|
|
|
|
if (fValid) {
|
|
|
|
g_pPresentDataBase->bChanged;
|
|
|
|
HTREEITEM hParent;
|
|
|
|
if (type == TYPE_ENTRY) {
|
|
hParent = g_pPresentDataBase->hItemAllApps;
|
|
} else if (type == FIX_LAYER) {
|
|
hParent = g_pPresentDataBase->hItemAllLayers;
|
|
} else {
|
|
assert(FALSE);
|
|
}
|
|
|
|
HTREEITEM hItem = DBTree.FindChild(hParent, lvItem.lParam);
|
|
|
|
assert(hItem);
|
|
|
|
//
|
|
// Refresh the entry in the tree
|
|
//
|
|
PostMessage(g_hDlg,
|
|
WM_USER_REPAINT_TREEITEM,
|
|
(WPARAM)hItem,
|
|
(LPARAM)lvItem.lParam);
|
|
|
|
PostMessage(g_hDlg,
|
|
WM_USER_REPAINT_LISTITEM,
|
|
(WPARAM)lvItem.iItem,
|
|
(LPARAM)lvItem.lParam);
|
|
return TRUE;
|
|
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
void
|
|
HandleMRUActivation(
|
|
IN WPARAM wCode
|
|
)
|
|
/*++
|
|
HandleMRUActivation
|
|
|
|
Desc: The user wishes to open a database that is in the MRU list. If this
|
|
database is already open then we simply select this database in the
|
|
database tree. (LHS)
|
|
|
|
Params:
|
|
IN WPARAM wCode: The LOWORD(wParam) that comes with WM_COMMAND. This will
|
|
identify which MRU menu item was activated
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
|
|
CSTRING strPath;
|
|
|
|
if (!g_strlMRU.GetElement(wCode - ID_FILE_FIRST_MRU, strPath)) {
|
|
assert(FALSE);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Test to see if we have the database open already.
|
|
// If it is open, we just highlight that and return
|
|
//
|
|
PDATABASE pDataBase = DataBaseList.pDataBaseHead;
|
|
BOOL bFound = FALSE;
|
|
|
|
while (pDataBase) {
|
|
|
|
if (pDataBase->strPath == strPath) {
|
|
|
|
TreeView_SelectItem(DBTree.m_hLibraryTree, pDataBase->hItemDB);
|
|
bFound = TRUE;
|
|
break;
|
|
|
|
}
|
|
|
|
pDataBase = pDataBase->pNext;
|
|
}
|
|
|
|
BOOL bLoaded = FALSE;
|
|
|
|
if (!bFound) {
|
|
|
|
SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
|
|
bLoaded = LoadDataBase((LPTSTR)strPath);
|
|
|
|
if (bLoaded) {
|
|
|
|
SetCursor(LoadCursor(NULL, IDC_ARROW));
|
|
|
|
AddToMRU(g_pPresentDataBase->strPath);
|
|
|
|
RefreshMRUMenu();
|
|
|
|
SetCaption();
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
OnDbRenameInitDialog(
|
|
IN HWND hdlg
|
|
)
|
|
/*++
|
|
OnDbRenameInitDialog
|
|
|
|
Description: Processes WM_INITDIALOG for IDD_DBRENAME.
|
|
Limits the text field
|
|
|
|
Params:
|
|
IN HWND hdlg: The handle to the rename dialog box
|
|
--*/
|
|
{
|
|
SendMessage(GetDlgItem(hdlg, IDC_NAME),
|
|
EM_LIMITTEXT,
|
|
(WPARAM)LIMIT_APP_NAME,
|
|
(LPARAM)0);
|
|
|
|
if (g_pPresentDataBase) {
|
|
SetDlgItemText(hdlg, IDC_NAME, (LPCTSTR)g_pPresentDataBase->strName);
|
|
}
|
|
|
|
CenterWindow(GetParent(hdlg), hdlg);
|
|
}
|
|
|
|
void
|
|
OnDbRenameOnCommandIDC_NAME(
|
|
IN HWND hdlg,
|
|
IN WPARAM wParam
|
|
)
|
|
/*++
|
|
OnDbRenameOnCommandIDC_NAME
|
|
|
|
Description: Processes WM_COMMAND for the text box in IDD_DBRENAME.
|
|
Disables the OK button if we do not have any text in there.
|
|
|
|
Params:
|
|
IN HWND hdlg: The handle to the rename dialog box
|
|
IN WPARAM wParam: The WPARAM that comes with WM_COMMAND
|
|
|
|
--*/
|
|
{
|
|
BOOL bEnable;
|
|
TCHAR szDBName[LIMIT_APP_NAME + 1];
|
|
|
|
if (hdlg == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (EN_CHANGE == HIWORD(wParam)) {
|
|
|
|
*szDBName = 0;
|
|
|
|
GetWindowText(GetDlgItem(hdlg, IDC_NAME), szDBName, ARRAYSIZE(szDBName));
|
|
bEnable = ValidInput(szDBName);
|
|
|
|
//
|
|
// Enable the OK button only if we have some text in the box
|
|
//
|
|
ENABLEWINDOW(GetDlgItem(hdlg, IDOK), bEnable);
|
|
}
|
|
}
|
|
|
|
void
|
|
OnDbRenameOnCommandIDOK(
|
|
IN HWND hdlg,
|
|
OUT CSTRING* pstrString
|
|
)
|
|
/*++
|
|
OnDbRenameOnCommandIDOK
|
|
|
|
Description: Handles the pressing of OK button in IDD_DBRENAME. Gets the text
|
|
from the text box and stores that in g_szData
|
|
|
|
Params:
|
|
IN HWND hdlg: The handle to the dialog rename window: IDD_DBRENAME
|
|
OUT CSTRING* pstrString: The pointer to the CSTRING that should contain the new name
|
|
--*/
|
|
{
|
|
TCHAR szDBName[LIMIT_APP_NAME + 1];
|
|
|
|
*szDBName = 0;
|
|
|
|
GetDlgItemText(hdlg, IDC_NAME, szDBName, ARRAYSIZE(szDBName));
|
|
CSTRING::Trim(szDBName);
|
|
|
|
//
|
|
// Change the name
|
|
//
|
|
*pstrString = szDBName;
|
|
}
|
|
|
|
|
|
INT_PTR CALLBACK
|
|
DatabaseRenameDlgProc(
|
|
IN HWND hdlg,
|
|
IN UINT uMsg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
DatabaseRenameProc
|
|
|
|
Description: Handles messages for the database rename option
|
|
|
|
Params: Standard dialog handler parameters
|
|
|
|
IN HWND hDlg
|
|
IN UINT uMsg
|
|
IN WPARAM wParam
|
|
IN LPARAM lParam: This will be a pointer to a CSTRING that should contain the new string
|
|
|
|
Return: Standard dialog handler return
|
|
|
|
--*/
|
|
{
|
|
int wCode = LOWORD(wParam);
|
|
int wNotifyCode = HIWORD(wParam);
|
|
static CSTRING* s_pstrParam = NULL;
|
|
|
|
switch (uMsg) {
|
|
case WM_INITDIALOG:
|
|
|
|
OnDbRenameInitDialog(hdlg);
|
|
s_pstrParam = (CSTRING*)lParam;
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch (wCode) {
|
|
case IDOK:
|
|
|
|
OnDbRenameOnCommandIDOK(hdlg, s_pstrParam);
|
|
EndDialog(hdlg, TRUE);
|
|
break;
|
|
|
|
case IDC_NAME:
|
|
|
|
OnDbRenameOnCommandIDC_NAME(hdlg, wParam);
|
|
break;
|
|
|
|
case IDCANCEL:
|
|
|
|
EndDialog(hdlg, FALSE);
|
|
break;
|
|
|
|
}
|
|
break;
|
|
|
|
default: return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
PDATABASE
|
|
GetCurrentDB(
|
|
void
|
|
)
|
|
{
|
|
return g_pPresentDataBase;
|
|
}
|
|
|
|
void
|
|
DisplayInvalidAppNameMessage(
|
|
IN HWND hdlg
|
|
)
|
|
/*++
|
|
|
|
DisplayInvalidAppNameMessage
|
|
|
|
Desc: If the app name contains one of the chars that cannot
|
|
be part of a dir name we will show this message.
|
|
|
|
Params:
|
|
IN HWND hdlg: The window where this dialog message should be shown.
|
|
)
|
|
--*/
|
|
{
|
|
CSTRING strMessage(IDS_ERROR_DEFAULTNAME);
|
|
|
|
strMessage.Strcat(TEXT(" \""));
|
|
|
|
MessageBox(hdlg,
|
|
strMessage,
|
|
g_szAppName,
|
|
MB_ICONWARNING);
|
|
}
|
|
|
|
INT
|
|
GetContentsListIndex(
|
|
IN HWND hwndList,
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
GetContentsListIndex
|
|
|
|
Desc: Gets the index of a item that has the LPARAM of lParam
|
|
|
|
Params:
|
|
IN HWND hwndList: The list view
|
|
IN LPARAM lParam: The LPARAM
|
|
|
|
Return:
|
|
The index of the item that has a LPARAM of lParam
|
|
or -1 if that does not exist
|
|
--*/
|
|
{
|
|
LVFINDINFO lvFind;
|
|
INT iIndex = 0;
|
|
|
|
lvFind.flags = LVFI_PARAM;
|
|
lvFind.lParam = lParam;
|
|
|
|
return ListView_FindItem(hwndList, -1, &lvFind);
|
|
}
|
|
|
|
BOOL
|
|
DeleteFromContentsList(
|
|
IN HWND hwndList,
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
DeleteFromContentsList
|
|
|
|
Desc: Deletesan element with LPARAM of lParam from the ListView hwndList
|
|
|
|
Params:
|
|
IN HWND hwndList: The list view from which we want to delete
|
|
IN LPARAM lParam: The LPARAM of the item that we want to delete
|
|
|
|
Return:
|
|
TRUE: The item was deleted successfully
|
|
FALSE: Otherwise
|
|
--*/
|
|
{
|
|
INT iIndex = -1;
|
|
BOOL bOk = FALSE;
|
|
|
|
iIndex = GetContentsListIndex(hwndList, lParam);
|
|
|
|
if (iIndex > -1) {
|
|
bOk = ListView_DeleteItem(hwndList, iIndex);
|
|
} else {
|
|
assert(FALSE);
|
|
Dbg(dlError, "DeleteFromContentsList", "Could not find Element with lParam = %X", lParam);
|
|
bOk = FALSE;
|
|
}
|
|
|
|
return bOk;
|
|
}
|