CMake/Source/MFCDialog/CMakeSetupDialog.cpp
Brad King 98c51ff6dc ENH: Overhaul CMake version numbering
This moves the version numbers into an isolated configured header so
that not all of CMake needs to rebuild when the version changes.

Previously we had spaces, dashes and/or the word 'patch' randomly chosen
before the patch number.  Now we always report version numbers in the
traditional format "<major>.<minor>.<patch>[-rc<rc>]".

We still use odd minor numbers for development versions.  Now we also
use the CCYYMMDD date as the patch number of development versions, thus
allowing tests for exact CMake versions.
2009-03-05 15:17:07 -05:00

1551 lines
45 KiB
C++

// pcbuilderdialogDlg.cpp : implementation file
//
#include "stdafx.h"
#include "shellapi.h"
// a fun undef for DOT NET
#undef DEBUG
#include "CMakeSetup.h"
#include "MakeHelp.h"
#include "cmVersion.h"
#include "PathDialog.h"
#include "CMakeSetupDialog.h"
#include "CMakeCommandLineInfo.h"
#include "../cmExternalMakefileProjectGenerator.h"
#include "../cmListFileCache.h"
#include "../cmCacheManager.h"
#include "../cmake.h"
#include "../cmGlobalGenerator.h"
#include "../cmDynamicLoader.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP();
void MFCMessageCallback(const char* m, const char* title, bool& nomore, void*)
{
std::string message = m;
message += "\n\n(Press Cancel to suppress any further messages.)";
if(::MessageBox(0, message.c_str(), title,
MB_OKCANCEL|MB_TASKMODAL) == IDCANCEL)
{
nomore = true;
}
}
/////////////////////////////////////////////////////////////////////////////
// CMakeSetupDialog dialog
void updateProgress(const char *msg, float prog, void *cd)
{
char* tmp = new char[strlen(msg) + 40];
if (prog >= 0)
{
sprintf(tmp,"%s %i%%",msg,(int)(100*prog));
}
else
{
sprintf(tmp,"%s",msg);
}
CMakeSetupDialog *self = (CMakeSetupDialog *)cd;
self->SetDlgItemText(IDC_PROGRESS, tmp);
CWnd* cancel = self->GetDlgItem(IDCANCEL);
//
// Retrieve and dispatch any waiting messages.
//
MSG wmsg;
while (::PeekMessage (&wmsg, NULL, 0, 0, PM_REMOVE))
{
switch(wmsg.message)
{
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_LBUTTONDBLCLK:
{
if(wmsg.hwnd == cancel->m_hWnd)
{
::DispatchMessage(&wmsg);
}
}
break;
case WM_COMMAND:
case WM_SETCURSOR:
case WM_PAINT:
::DispatchMessage(&wmsg);
break;
}
}
delete [] tmp;
}
// Convert to Win32 path (slashes). This calls the system tools one and then
// removes the spaces. It is not in system tools because we don't want any
// generators accidentally use it
std::string ConvertToWindowsPath(const char* path)
{
// Convert to output path.
// Remove the "" around it (if any) since it's an output path for
// the shell. If another shell-oriented feature is not designed
// for a GUI use, then we are in trouble.
// save the value of the force to unix path option
bool saveForce = cmSystemTools::GetForceUnixPaths();
// make sure we get windows paths no matter what for the GUI
cmSystemTools::SetForceUnixPaths(false);
std::string s = cmSystemTools::ConvertToOutputPath(path);
// now restore the force unix path to its previous value
cmSystemTools::SetForceUnixPaths(saveForce);
if (s.size())
{
std::string::iterator i = s.begin();
if (*i == '\"')
{
s.erase(i, i + 1);
}
i = s.begin() + s.length() - 1;
if (*i == '\"')
{
s.erase(i, i + 1);
}
}
return s;
}
CMakeSetupDialog::CMakeSetupDialog(const CMakeCommandLineInfo& cmdInfo,
CWnd* pParent /*=NULL*/)
: CDialog(CMakeSetupDialog::IDD, pParent)
{
m_GeneratorPicked = false;
m_Cursor = LoadCursor(NULL, IDC_ARROW);
m_RunningConfigure = false;
cmSystemTools::SetRunCommandHideConsole(true);
cmSystemTools::SetErrorCallback(MFCMessageCallback);
m_RegistryKey = "Software\\Kitware\\CMakeSetup\\Settings\\StartPath";
m_CacheEntriesList.m_CMakeSetupDialog = this;
m_CMakeInstance = new cmake;
m_CMakeInstance->SetCMakeEditCommand("CMakeSetup");
m_CMakeInstance->SetProgressCallback(updateProgress, (void *)this);
//{{AFX_DATA_INIT(CMakeSetupDialog)
//}}AFX_DATA_INIT
// Get the parameters from the command line info
// If an unknown parameter is found, try to interpret it too, since it
// is likely to be a file dropped on the shortcut :)
if (cmdInfo.m_LastUnknownParameter.IsEmpty())
{
this->m_WhereSource = cmdInfo.m_WhereSource;
this->m_WhereBuild = cmdInfo.m_WhereBuild;
this->m_GeneratorDialog.m_GeneratorChoiceString =
cmdInfo.m_GeneratorChoiceString;
this->m_AdvancedValues = cmdInfo.m_AdvancedValues;
}
else
{
this->m_WhereSource = _T("");
this->m_WhereBuild = _T("");
this->m_AdvancedValues = FALSE;
this->m_GeneratorDialog.m_GeneratorChoiceString =
cmdInfo.m_GeneratorChoiceString;
this->ChangeDirectoriesFromFile((LPCTSTR)cmdInfo.m_LastUnknownParameter);
}
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
m_BuildPathChanged = false;
// Find the path to the cmake.exe executable
char fname[1024];
::GetModuleFileName(NULL,fname,1023);
// extract just the path part
m_PathToExecutable = cmSystemTools::GetProgramPath(fname).c_str();
// add the cmake.exe to the path
m_PathToExecutable += "/cmake.exe";
m_oldCX = -1;
m_deltaXRemainder = 0;
}
CMakeSetupDialog::~CMakeSetupDialog()
{
delete m_CMakeInstance;
// clean up globals
cmDynamicLoader::FlushCache();
}
void CMakeSetupDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CMakeSetupDialog)
DDX_Control(pDX, IDC_AdvancedValues, m_AdvancedValuesControl);
DDX_Control(pDX, IDC_SUPPRESS_DEV_WARNINGS, m_SuppressDevWarningsControl);
DDX_Check(pDX, IDC_SUPPRESS_DEV_WARNINGS, m_SuppressDevValue);
DDX_Control(pDX, IDC_BROWSE_SOURCE, m_BrowseSource);
DDX_Control(pDX, IDC_BROWSE_BUILD, m_BrowseBuild);
DDX_Control(pDX, IDC_DELETE_BUTTON, m_DeleteButton);
DDX_Control(pDX, IDC_HELP_BUTTON, m_HelpButton);
DDX_Control(pDX, IDC_OK, m_OKButton);
DDX_Control(pDX, IDCANCEL, m_CancelButton);
DDX_CBStringExact(pDX, IDC_WhereSource, m_WhereSource);
DDX_CBStringExact(pDX, IDC_WhereBuild, m_WhereBuild);
DDX_Control(pDX, IDC_FRAME, m_ListFrame);
DDX_Control(pDX, IDC_WhereSource, m_WhereSourceControl);
DDX_Control(pDX, IDC_WhereBuild, m_WhereBuildControl);
DDX_Control(pDX, IDC_LIST2, m_CacheEntriesList);
DDX_Control(pDX, IDC_MouseHelpCaption, m_MouseHelp);
DDX_Control(pDX, IDC_PROGRESS, m_StatusDisplay);
DDX_Control(pDX, IDC_BuildProjects, m_Configure);
DDX_Check(pDX, IDC_AdvancedValues, m_AdvancedValues);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CMakeSetupDialog, CDialog)
//{{AFX_MSG_MAP(CMakeSetupDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON2, OnBrowseWhereSource)
ON_BN_CLICKED(IDC_BuildProjects, OnConfigure)
ON_BN_CLICKED(IDC_BUTTON3, OnBrowseWhereBuild)
ON_CBN_EDITCHANGE(IDC_WhereBuild, OnChangeWhereBuild)
ON_CBN_SELCHANGE(IDC_WhereBuild, OnSelendokWhereBuild)
ON_CBN_EDITCHANGE(IDC_WhereSource, OnChangeWhereSource)
ON_CBN_SELENDOK(IDC_WhereSource, OnSelendokWhereSource)
ON_WM_SIZE()
ON_WM_GETMINMAXINFO()
ON_BN_CLICKED(IDC_OK, OnOk)
ON_BN_CLICKED(IDC_DELETE_BUTTON, OnDeleteButton)
ON_BN_CLICKED(IDC_HELP_BUTTON, OnHelpButton)
ON_BN_CLICKED(IDC_AdvancedValues, OnAdvancedValues)
ON_BN_CLICKED(IDC_SUPPRESS_DEV_WARNINGS, OnSuppressDevValue)
ON_BN_DOUBLECLICKED(IDC_SUPPRESS_DEV_WARNINGS, OnDoubleclickedSuppressDevValue)
ON_BN_DOUBLECLICKED(IDC_AdvancedValues, OnDoubleclickedAdvancedValues)
ON_WM_DROPFILES()
ON_BN_CLICKED(IDCANCEL, OnCancel)
ON_WM_SETCURSOR()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CMakeSetupDialog message handlers
BOOL CMakeSetupDialog::OnInitDialog()
{
CDialog::OnInitDialog();
this->DragAcceptFiles(true);
// Add "Create shortcut" menu item to system menu.
// IDM_CREATESHORTCUT must be in the system command range.
ASSERT((IDM_CREATESHORTCUT & 0xFFF0) == IDM_CREATESHORTCUT);
ASSERT(IDM_CREATESHORTCUT < 0xF000);
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strCreateShortcutMenu;
strCreateShortcutMenu.LoadString(IDS_CREATESHORTCUT);
if (!strCreateShortcutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING,
IDM_CREATESHORTCUT,
strCreateShortcutMenu);
}
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING,
IDM_ABOUTBOX,
strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// Load source and build dirs from registry
this->LoadFromRegistry();
// try to load the cmake cache from disk
this->LoadCacheFromDiskToGUI();
m_WhereBuildControl.LimitText(2048);
m_WhereSourceControl.LimitText(2048);
// Set the version number
char tmp[1024];
sprintf(tmp,"CMake %s", cmVersion::GetCMakeVersion());
SetDlgItemText(IDC_PROGRESS, "");
this->SetWindowText(tmp);
this->UpdateData(FALSE);
return TRUE; // return TRUE unless you set the focus to a control
}
// About dialog invoke
void CMakeSetupDialog::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else if ((nID & 0xFFF0) == IDM_CREATESHORTCUT)
{
CreateShortcut();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CMakeSetupDialog::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CMakeSetupDialog::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
// Browse button
bool CMakeSetupDialog::Browse(CString &result, const char *title)
{
CString initialDir = result;
initialDir.Replace("/", "\\");
CPathDialog dlg("Select Path", title, initialDir);
if(dlg.DoModal()==IDOK)
{
result = dlg.GetPathName();
return true;
}
else
{
return false;
}
}
void CMakeSetupDialog::SaveToRegistry()
{
HKEY hKey;
DWORD dwDummy;
if(RegCreateKeyEx(HKEY_CURRENT_USER,
m_RegistryKey,
0, "", REG_OPTION_NON_VOLATILE, KEY_READ|KEY_WRITE,
NULL, &hKey, &dwDummy) != ERROR_SUCCESS)
{
return;
}
else
{
// save some values
CString regvalue;
// write the size of the dialog
CRect size;
this->GetWindowRect(&size);
unsigned long width = size.Width();
unsigned long height = size.Height();
RegSetValueEx(hKey, _T("Width"), 0, REG_DWORD,
(CONST BYTE *)(&width),4);
RegSetValueEx(hKey, _T("Height"), 0, REG_DWORD,
(CONST BYTE *)(&height),4);
this->ReadRegistryValue(hKey, &(regvalue),"WhereSource1","C:\\");
int shiftEnd = 9;
if(m_WhereSource != regvalue)
{
char keyName[1024];
char keyName2[1024];
int i;
for (i = 2; i < 10; ++i)
{
regvalue = "";
sprintf(keyName,"WhereSource%i",i);
this->ReadRegistryValue(hKey, &(regvalue),keyName,"");
// check for short circuit, if the new value is already in
// the list then we stop
if (m_WhereSource == regvalue)
{
shiftEnd = i - 1;
}
}
for (i = shiftEnd; i; --i)
{
regvalue = "";
sprintf(keyName,"WhereSource%i",i);
sprintf(keyName2,"WhereSource%i",i+1);
this->ReadRegistryValue(hKey, &(regvalue),keyName,"");
if (strlen(regvalue))
{
RegSetValueEx(hKey, _T(keyName2), 0, REG_SZ,
(CONST BYTE *)(const char *)regvalue,
regvalue.GetLength());
}
}
RegSetValueEx(hKey, _T("WhereSource1"), 0, REG_SZ,
(CONST BYTE *)(const char *)m_WhereSource,
m_WhereSource.GetLength());
}
this->ReadRegistryValue(hKey, &(regvalue),"WhereBuild1","C:\\");
if(m_WhereBuild != regvalue)
{
int i;
char keyName[1024];
char keyName2[1024];
for (i = 2; i < 10; ++i)
{
regvalue = "";
sprintf(keyName,"WhereBuild%i",i);
this->ReadRegistryValue(hKey, &(regvalue),keyName,"");
// check for short circuit, if the new value is already in
// the list then we stop
if (m_WhereBuild == regvalue)
{
shiftEnd = i - 1;
}
}
for (i = shiftEnd; i; --i)
{
regvalue = "";
sprintf(keyName,"WhereBuild%i",i);
sprintf(keyName2,"WhereBuild%i",i+1);
this->ReadRegistryValue(hKey, &(regvalue),keyName,"");
if (strlen(regvalue))
{
RegSetValueEx(hKey, _T(keyName2), 0, REG_SZ,
(CONST BYTE *)(const char *)regvalue,
regvalue.GetLength());
}
}
RegSetValueEx(hKey, _T("WhereBuild1"), 0, REG_SZ,
(CONST BYTE *)(const char *)m_WhereBuild,
m_WhereBuild.GetLength());
}
}
RegCloseKey(hKey);
}
void CMakeSetupDialog::ReadRegistryValue(HKEY hKey,
CString *val,
const char *key,
const char *adefault)
{
DWORD dwType, dwSize;
char *pb;
dwType = REG_SZ;
pb = val->GetBuffer(MAX_PATH);
dwSize = MAX_PATH;
if(RegQueryValueEx(hKey,_T(key), NULL, &dwType,
(BYTE *)pb, &dwSize) != ERROR_SUCCESS)
{
val->ReleaseBuffer();
*val = _T(adefault);
}
else
{
val->ReleaseBuffer();
}
}
void CMakeSetupDialog::LoadFromRegistry()
{
HKEY hKey;
if(RegOpenKeyEx(HKEY_CURRENT_USER,
m_RegistryKey,
0, KEY_READ, &hKey) != ERROR_SUCCESS)
{
return;
}
else
{
// load some values
DWORD dwSize = 4;
DWORD width, height;
if (RegQueryValueEx(hKey,_T("Width"), NULL, NULL,
(BYTE *)&width, &dwSize) == ERROR_SUCCESS &&
RegQueryValueEx(hKey,_T("Height"), NULL, NULL,
(BYTE *)&height, &dwSize) == ERROR_SUCCESS)
{
this->SetWindowPos(0,0,0,width,height,SWP_NOZORDER | SWP_NOMOVE);
}
if (m_WhereSource.IsEmpty())
{
this->ReadRegistryValue(hKey, &(m_WhereSource),"WhereSource1","C:\\");
}
if (m_WhereBuild.IsEmpty())
{
this->ReadRegistryValue(hKey, &(m_WhereBuild),"WhereBuild1","C:\\");
}
m_WhereSourceControl.AddString(m_WhereSource);
m_WhereBuildControl.AddString(m_WhereBuild);
char keyname[1024];
CString regvalue;
int i;
for (i = 2; i <= 10; ++i)
{
sprintf(keyname,"WhereSource%i",i);
regvalue = "";
this->ReadRegistryValue(hKey, &(regvalue),keyname,"C:\\");
if (strcmp("C:\\",regvalue))
{
m_WhereSourceControl.AddString(regvalue);
}
sprintf(keyname,"WhereBuild%i",i);
regvalue = "";
this->ReadRegistryValue(hKey, &(regvalue),keyname,"C:\\");
if (strcmp("C:\\",regvalue))
{
m_WhereBuildControl.AddString(regvalue);
}
}
}
RegCloseKey(hKey);
}
// Callback for browse source button
void CMakeSetupDialog::OnBrowseWhereSource()
{
this->UpdateData();
Browse(m_WhereSource, "Enter Path to Source");
this->UpdateData(false);
this->OnChangeWhereSource();
}
// Callback for browser build button
void CMakeSetupDialog::OnBrowseWhereBuild()
{
this->UpdateData();
Browse(m_WhereBuild, "Enter Path to Build");
this->UpdateData(false);
this->OnChangeWhereBuild();
}
void CMakeSetupDialog::RunCMake(bool generateProjectFiles)
{
if(!cmSystemTools::FileExists(m_WhereBuild))
{
std::string message =
"Build directory does not exist, should I create it?\n\n"
"Directory: ";
message += (const char*)m_WhereBuild;
if(MessageBox(message.c_str(), "Create Directory", MB_OKCANCEL) == IDOK)
{
cmSystemTools::MakeDirectory(m_WhereBuild);
}
else
{
MessageBox("Build Project aborted, nothing done.");
return;
}
}
// set the wait cursor
m_Cursor = LoadCursor(NULL, IDC_WAIT);
::SetCursor(m_Cursor);
m_RunningConfigure = true;
// get all the info from the dialog
this->UpdateData();
// always save the current gui values to disk
this->SaveCacheFromGUI();
// Make sure we are working from the cache on disk
this->LoadCacheFromDiskToGUI();
m_OKButton.EnableWindow(false);
// setup the cmake instance
if (generateProjectFiles)
{
if(m_CMakeInstance->Generate() != 0)
{
cmSystemTools::Error(
"Error in generation process, project files may be invalid");
}
}
else
{
m_CMakeInstance->SetHomeDirectory(m_WhereSource);
m_CMakeInstance->SetStartDirectory(m_WhereSource);
m_CMakeInstance->SetHomeOutputDirectory(m_WhereBuild);
m_CMakeInstance->SetStartOutputDirectory(m_WhereBuild);
m_CMakeInstance->SetGlobalGenerator(
m_CMakeInstance->CreateGlobalGenerator(m_GeneratorDialog.m_GeneratorChoiceString));
m_CMakeInstance->SetCMakeCommand(m_PathToExecutable);
m_CMakeInstance->LoadCache();
if(m_SuppressDevValue)
{
m_CMakeInstance->SetSuppressDevWarnings(true);
}
else
{
m_CMakeInstance->SetSuppressDevWarnings(false);
}
if(m_CMakeInstance->Configure() != 0)
{
cmSystemTools::Error(
"Error in configuration process, project files may be invalid");
}
// update the GUI with any new values in the caused by the
// generation process
this->LoadCacheFromDiskToGUI();
}
// save source and build paths to registry
this->SaveToRegistry();
// path is up-to-date now
m_BuildPathChanged = false;
// put the cursor back
m_Cursor = LoadCursor(NULL, IDC_ARROW);
::SetCursor(m_Cursor);
m_RunningConfigure = false;
cmSystemTools::ResetErrorOccuredFlag();
}
// Callback for build projects button
void CMakeSetupDialog::OnConfigure()
{
if(!m_GeneratorPicked)
{
m_GeneratorDialog.m_CMakeInstance = this->m_CMakeInstance;
if(m_GeneratorDialog.DoModal() != IDOK)
{
return;
}
// save the generator choice in the registry
HKEY hKey;
DWORD dwDummy;
if(RegCreateKeyEx(HKEY_CURRENT_USER,
m_RegistryKey,
0, "", REG_OPTION_NON_VOLATILE, KEY_READ|KEY_WRITE,
NULL, &hKey, &dwDummy) == ERROR_SUCCESS)
{
// save some values
RegSetValueEx(hKey, _T("LastGenerator"), 0, REG_SZ,
(CONST BYTE *)(const char *)m_GeneratorDialog.m_GeneratorChoiceString,
m_GeneratorDialog.m_GeneratorChoiceString.GetLength());
}
}
// enable error messages each time configure is pressed
cmSystemTools::EnableMessages();
this->RunCMake(false);
}
// callback for combo box menu where build selection
void CMakeSetupDialog::OnSelendokWhereBuild()
{
m_WhereBuildControl.GetLBText(m_WhereBuildControl.GetCurSel(),
m_WhereBuild);
m_WhereBuildControl.SetWindowText( m_WhereBuild);
this->UpdateData(FALSE);
this->OnChangeWhereBuild();
}
// callback for combo box menu where source selection
void CMakeSetupDialog::OnSelendokWhereSource()
{
m_WhereSourceControl.GetLBText(m_WhereSourceControl.GetCurSel(),
m_WhereSource);
this->UpdateData(FALSE);
this->OnChangeWhereSource();
}
// callback for chaing source directory
void CMakeSetupDialog::OnChangeWhereSource()
{
}
// callback for changing the build directory
void CMakeSetupDialog::OnChangeWhereBuild()
{
this->UpdateData();
// The build dir has changed, check if there is a cache, and
// grab the source dir from it
std::string path = this->m_WhereBuild;
cmSystemTools::ConvertToUnixSlashes(path);
// adjust the cmake instance
m_CMakeInstance->SetHomeOutputDirectory(m_WhereBuild);
m_CMakeInstance->SetStartOutputDirectory(m_WhereBuild);
std::string cache_file = path;
cache_file += "/CMakeCache.txt";
cmCacheManager *cachem = this->m_CMakeInstance->GetCacheManager();
cmCacheManager::CacheIterator it = cachem->NewIterator();
m_GeneratorPicked = false;
// make sure we have a normal cache file, specifically if one exists make
// sure it can be read
if (cmSystemTools::FileExists(cache_file.c_str()))
{
if (cachem->LoadCache(path.c_str()))
{
if (it.Find("CMAKE_HOME_DIRECTORY"))
{
path = ConvertToWindowsPath(it.GetValue());
this->m_WhereSource = path.c_str();
this->m_WhereSourceControl.SetWindowText(this->m_WhereSource);
this->OnChangeWhereSource();
m_GeneratorPicked = true;
}
}
else
{
//file exists but cqnnot be read
cmSystemTools::Error("There is a CMakeCache.txt file for the current binary tree but cmake does not have permission to read it. Please check the permissions of the directory you are trying to run CMake on.");
return;
}
}
m_CacheEntriesList.RemoveAll();
m_CacheEntriesList.ShowWindow(SW_SHOW);
this->LoadCacheFromDiskToGUI();
m_BuildPathChanged = true;
}
// copy from the cache manager to the cache edit list box
void CMakeSetupDialog::FillCacheGUIFromCacheManager()
{
cmCacheManager *cachem = this->m_CMakeInstance->GetCacheManager();
cmCacheManager::CacheIterator it = cachem->NewIterator();
// if there are already entries in the cache, then
// put the new ones in the top, so they show up first
bool reverseOrder = false;
// all the current values are not new any more
std::set<CPropertyItem*> items = m_CacheEntriesList.GetItems();
for(std::set<CPropertyItem*>::iterator i = items.begin();
i != items.end(); ++i)
{
// first check to see if it is still in the cache
CPropertyItem* item = *i;
if ( !it.Find((const char*)item->m_propName) )
{
m_CacheEntriesList.RemoveProperty((const char*)item->m_propName);
}
else
{
// if it is still in the cache then it is no longer new
item->m_NewValue = false;
}
}
for(cmCacheManager::CacheIterator i = cachem->NewIterator();
!i.IsAtEnd(); i.Next())
{
const char* key = i.GetName();
// if value has trailing space or tab, enclose it in single quotes
// to enforce the fact that it has 'invisible' trailing stuff
std::string value = i.GetValue();
if (value.size() &&
(value[value.size() - 1] == ' ' ||
value[value.size() - 1] == '\t'))
{
value = '\'' + value + '\'';
}
bool advanced = i.GetPropertyAsBool("ADVANCED");
switch(i.GetType() )
{
case cmCacheManager::BOOL:
if(cmSystemTools::IsOn(value.c_str()))
{
m_CacheEntriesList.AddProperty(key,
"ON",
i.GetProperty("HELPSTRING"),
CPropertyList::COMBO,"ON|OFF",
reverseOrder,
advanced
);
}
else
{
m_CacheEntriesList.AddProperty(key,
"OFF",
i.GetProperty("HELPSTRING"),
CPropertyList::COMBO,"ON|OFF",
reverseOrder, advanced
);
}
break;
case cmCacheManager::PATH:
m_CacheEntriesList.AddProperty(key,
value.c_str(),
i.GetProperty("HELPSTRING"),
CPropertyList::PATH,"",
reverseOrder, advanced
);
break;
case cmCacheManager::FILEPATH:
m_CacheEntriesList.AddProperty(key,
value.c_str(),
i.GetProperty("HELPSTRING"),
CPropertyList::FILE,"",
reverseOrder, advanced
);
break;
case cmCacheManager::STRING:
m_CacheEntriesList.AddProperty(key,
value.c_str(),
i.GetProperty("HELPSTRING"),
CPropertyList::EDIT,"",
reverseOrder, advanced
);
break;
case cmCacheManager::INTERNAL:
m_CacheEntriesList.RemoveProperty(key);
break;
}
}
if(m_CacheEntriesList.GetShowAdvanced())
{
m_CacheEntriesList.ShowAdvanced();
}
else
{
m_CacheEntriesList.HideAdvanced();
}
m_OKButton.EnableWindow(false);
if(cachem->GetSize() > 0 && !cmSystemTools::GetErrorOccuredFlag())
{
bool enable = true;
items = m_CacheEntriesList.GetItems();
for(std::set<CPropertyItem*>::iterator i = items.begin();
i != items.end(); ++i)
{
CPropertyItem* item = *i;
if(item->m_Advanced )
{
if(item->m_NewValue && m_CacheEntriesList.GetShowAdvanced())
{
enable = false;
break;
}
}
else
{
if(item->m_NewValue)
{
// if one new value then disable to OK button
enable = false;
break;
}
}
}
if(enable)
{
m_OKButton.EnableWindow(true);
}
}
// redraw the list
m_CacheEntriesList.SetTopIndex(0);
m_CacheEntriesList.Invalidate();
}
// copy from the list box to the cache manager
void CMakeSetupDialog::FillCacheManagerFromCacheGUI()
{
cmCacheManager *cachem = this->m_CMakeInstance->GetCacheManager();
std::set<CPropertyItem*> items = m_CacheEntriesList.GetItems();
cmCacheManager::CacheIterator it = cachem->NewIterator();
for(std::set<CPropertyItem*>::iterator i = items.begin();
i != items.end(); ++i)
{
CPropertyItem* item = *i;
if ( it.Find((const char*)item->m_propName) )
{
// if value is enclosed in single quotes ('foo') then remove them
// they were used to enforce the fact that it had 'invisible'
// trailing stuff
if (item->m_curValue.GetLength() >= 2 &&
item->m_curValue[0] == '\'' &&
item->m_curValue[item->m_curValue.GetLength() - 1] == '\'')
{
it.SetValue(item->m_curValue.Mid(
1, item->m_curValue.GetLength() - 2));
}
else
{
it.SetValue(item->m_curValue);
}
}
}
}
//! Load cache file from m_WhereBuild and display in GUI editor
void CMakeSetupDialog::LoadCacheFromDiskToGUI()
{
cmCacheManager *cachem = this->m_CMakeInstance->GetCacheManager();
if(m_WhereBuild != "")
{
if (!cachem->LoadCache(m_WhereBuild))
{
// if it does exist, but isn;t readable then warn the user
std::string cacheFile = m_WhereBuild;
cacheFile += "/CMakeCache.txt";
if(cmSystemTools::FileExists(cacheFile.c_str()))
{
cmSystemTools::Error("There is a CMakeCache.txt file for the current binary tree but cmake does not have permission to read it. Please check the permissions of the directory you are trying to run CMake on.");
return;
}
}
cmCacheManager::CacheIterator itm = cachem->NewIterator();
if ( itm.Find("CMAKE_HOME_DIRECTORY"))
{
std::string path = ConvertToWindowsPath(itm.GetValue());
this->m_WhereSource = path.c_str();
this->m_WhereSourceControl.SetWindowText(this->m_WhereSource);
this->OnChangeWhereSource();
}
m_CMakeInstance->SetHomeDirectory(m_WhereSource);
m_CMakeInstance->SetStartDirectory(m_WhereSource);
m_CMakeInstance->SetHomeOutputDirectory(m_WhereBuild);
m_CMakeInstance->SetStartOutputDirectory(m_WhereBuild);
m_CMakeInstance->PreLoadCMakeFiles();
this->FillCacheGUIFromCacheManager();
cmCacheManager::CacheIterator it =
cachem->GetCacheIterator("CMAKE_GENERATOR");
if(!it.IsAtEnd())
{
m_GeneratorPicked = true;
const char* extraGen = cachem->GetCacheValue("CMAKE_EXTRA_GENERATOR");
std::string curGen = cmExternalMakefileProjectGenerator::
CreateFullGeneratorName(it.GetValue(), extraGen);
if(m_GeneratorDialog.m_GeneratorChoiceString != curGen.c_str())
{
m_GeneratorDialog.m_GeneratorChoiceString = curGen.c_str();
this->UpdateData(FALSE);
}
}
}
}
//! Save GUI values to cmCacheManager and then save to disk.
void CMakeSetupDialog::SaveCacheFromGUI()
{
cmCacheManager *cachem = this->m_CMakeInstance->GetCacheManager();
this->FillCacheManagerFromCacheGUI();
if(m_WhereBuild != "")
{
cachem->SaveCache(m_WhereBuild);
}
}
void CMakeSetupDialog::OnSize(UINT nType, int cx, int cy)
{
if (nType == SIZE_MINIMIZED)
{
CDialog::OnSize(nType, cx, cy);
return;
}
if (m_oldCX == -1)
{
m_oldCX = cx;
m_oldCY = cy;
}
int deltax = cx - m_oldCX;
int deltay = cy - m_oldCY;
m_oldCX = cx;
m_oldCY = cy;
CDialog::OnSize(nType, cx, cy);
if (deltax == 0 && deltay == 0)
{
return;
}
if(m_CacheEntriesList.m_hWnd)
{
// get the original sizes/positions
CRect cRect;
m_AdvancedValuesControl.GetWindowRect(&cRect);
this->ScreenToClient(&cRect);
m_AdvancedValuesControl.SetWindowPos(&wndTop, cRect.left + deltax,
cRect.top,
0, 0,
SWP_NOCOPYBITS |
SWP_NOSIZE | SWP_NOZORDER);
m_SuppressDevWarningsControl.GetWindowRect(&cRect);
this->ScreenToClient(&cRect);
m_SuppressDevWarningsControl.SetWindowPos(&wndTop, cRect.left + deltax,
cRect.top,
0, 0,
SWP_NOCOPYBITS |
SWP_NOSIZE | SWP_NOZORDER);
m_BrowseSource.GetWindowRect(&cRect);
this->ScreenToClient(&cRect);
m_BrowseSource.SetWindowPos(&wndTop, cRect.left + deltax,
cRect.top,
0, 0,
SWP_NOCOPYBITS | SWP_NOSIZE | SWP_NOZORDER);
m_BrowseBuild.GetWindowRect(&cRect);
this->ScreenToClient(&cRect);
m_BrowseBuild.SetWindowPos(&wndTop, cRect.left + deltax,
cRect.top,
0, 0,
SWP_NOCOPYBITS | SWP_NOSIZE | SWP_NOZORDER);
m_WhereSourceControl.GetWindowRect(&cRect);
m_WhereSourceControl.SetWindowPos(&wndTop, cRect.left, cRect.top,
cRect.Width() + deltax,
cRect.Height(),
SWP_NOCOPYBITS |
SWP_NOMOVE | SWP_NOZORDER);
m_WhereBuildControl.GetWindowRect(&cRect);
m_WhereBuildControl.SetWindowPos(&wndTop, cRect.left, cRect.top,
cRect.Width() + deltax,
cRect.Height(),
SWP_NOCOPYBITS |
SWP_NOMOVE | SWP_NOZORDER);
m_ListFrame.GetWindowRect(&cRect);
m_ListFrame.SetWindowPos(&wndTop, cRect.left, cRect.top,
cRect.Width() + deltax,
cRect.Height() + deltay,
SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOZORDER);
m_CacheEntriesList.GetWindowRect(&cRect);
m_CacheEntriesList.SetWindowPos(&wndTop, cRect.left, cRect.top,
cRect.Width() + deltax,
cRect.Height() + deltay,
SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOZORDER);
m_StatusDisplay.GetWindowRect(&cRect);
this->ScreenToClient(&cRect);
m_StatusDisplay.SetWindowPos(&wndBottom, cRect.left,
cRect.top + deltay,
cRect.Width() + deltax, cRect.Height(),
SWP_NOCOPYBITS);
m_MouseHelp.GetWindowRect(&cRect);
this->ScreenToClient(&cRect);
m_MouseHelp.SetWindowPos(&wndTop, cRect.left ,
cRect.top + deltay,
cRect.Width() + deltax, cRect.Height(),
SWP_NOCOPYBITS | SWP_NOZORDER);
deltax = int(deltax + m_deltaXRemainder);
m_deltaXRemainder = float(deltax%2);
m_Configure.GetWindowRect(&cRect);
this->ScreenToClient(&cRect);
m_Configure.SetWindowPos(&wndTop, cRect.left + deltax/2,
cRect.top + deltay,
0, 0,
SWP_NOCOPYBITS | SWP_NOSIZE);
m_CancelButton.GetWindowRect(&cRect);
this->ScreenToClient(&cRect);
m_CancelButton.SetWindowPos(&wndTop, cRect.left + deltax/2,
cRect.top + deltay,
0, 0,
SWP_NOCOPYBITS | SWP_NOSIZE);
m_OKButton.GetWindowRect(&cRect);
this->ScreenToClient(&cRect);
m_OKButton.SetWindowPos(&wndTop, cRect.left + deltax/2,
cRect.top + deltay,
0, 0,
SWP_NOCOPYBITS | SWP_NOSIZE);
m_DeleteButton.GetWindowRect(&cRect);
this->ScreenToClient(&cRect);
m_DeleteButton.SetWindowPos(&wndTop, cRect.left + deltax/2,
cRect.top + deltay,
0, 0,
SWP_NOCOPYBITS | SWP_NOSIZE);
m_HelpButton.GetWindowRect(&cRect);
this->ScreenToClient(&cRect);
m_HelpButton.SetWindowPos(&wndTop, cRect.left + deltax/2,
cRect.top + deltay,
0, 0,
SWP_NOCOPYBITS | SWP_NOSIZE);
}
}
void CMakeSetupDialog::OnGetMinMaxInfo( MINMAXINFO FAR* lpMMI )
{
lpMMI->ptMinTrackSize.x = 550;
lpMMI->ptMinTrackSize.y = 272;
}
void CMakeSetupDialog::OnCancel()
{
if(m_RunningConfigure)
{
if(MessageBox("You are in the middle of a Configure.\n"
"If you Cancel now the configure information will be lost.\n"
"Are you sure you want to Cancel?", "Confirm Exit",
MB_YESNO) == IDYES)
{
cmSystemTools::SetFatalErrorOccured();
}
return;
}
if(m_CacheEntriesList.IsDirty())
{
if(MessageBox("You have changed options but not rebuilt, "
"are you sure you want to exit?", "Confirm Exit",
MB_YESNO) == IDYES)
{
CDialog::OnOK();
}
}
else
{
CDialog::OnOK();
}
}
void CMakeSetupDialog::OnOk()
{
// enable error messages each time configure is pressed
cmSystemTools::EnableMessages();
m_CacheEntriesList.ClearDirty();
this->RunCMake(true);
// save the size of the dialog
if (!(::GetKeyState(VK_SHIFT) & 0x1000))
{
CDialog::OnOK();
}
}
// Create a shortcut on the desktop with the current Source/Build dir.
int CMakeSetupDialog::CreateShortcut()
{
// Find the desktop folder and create the link name
HKEY hKey;
if(RegOpenKeyEx(HKEY_CURRENT_USER,
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders",
0, KEY_READ, &hKey) != ERROR_SUCCESS)
{
AfxMessageBox ("Create shortcut: unable to find 'Shell Folders' key in registry!");
return 1;
}
DWORD dwType, dwSize;
#define MAXPATH 1024
char link_name[MAXPATH];
dwSize = MAXPATH;
if(RegQueryValueEx(hKey,
(LPCTSTR)"Desktop",
NULL,
&dwType,
(BYTE *)link_name,
&dwSize) != ERROR_SUCCESS)
{
AfxMessageBox ("Create shortcut: unable to find 'Desktop' registry value in 'Shell Folders' key!");
return 1;
}
if(dwType != REG_SZ)
{
AfxMessageBox ("Create shortcut: 'Desktop' registry value in 'Shell Folders' key has wrong type!");
return 1;
}
strcat(link_name, "\\CMake - ");
std::string current_dir = cmSystemTools::GetFilenameName((LPCTSTR)m_WhereSource);
strcat(link_name, current_dir.c_str());
strcat(link_name, ".lnk");
// Find the path to the current executable
char path_to_current_exe[MAXPATH];
::GetModuleFileName(NULL, path_to_current_exe, MAXPATH);
// Create the shortcut
HRESULT hres;
IShellLink *psl;
// Initialize the COM library
hres = CoInitialize(NULL);
if (! SUCCEEDED (hres))
{
AfxMessageBox ("Create shortcut: unable to initialize the COM library!");
return 1;
}
// Create an IShellLink object and get a pointer to the IShellLink
// interface (returned from CoCreateInstance).
hres = CoCreateInstance(CLSID_ShellLink,
NULL,
CLSCTX_INPROC_SERVER,
IID_IShellLink,
(void **)&psl);
if (! SUCCEEDED (hres))
{
AfxMessageBox ("Create shortcut: unable to create IShellLink instance!");
return 1;
}
IPersistFile *ppf;
// Query IShellLink for the IPersistFile interface for
// saving the shortcut in persistent storage.
hres = psl->QueryInterface(IID_IPersistFile, (void **)&ppf);
if (SUCCEEDED (hres))
{
// Set the path to the shortcut target.
hres = psl->SetPath(path_to_current_exe);
if (! SUCCEEDED (hres))
{
AfxMessageBox ("Create shortcut: SetPath failed!");
}
// Set the arguments of the shortcut.
CString args = " /H=\"" + m_WhereSource + "\" /B=\"" + m_WhereBuild + "\" /G=\"" + m_GeneratorDialog.m_GeneratorChoiceString + "\" /A=\"" + (m_AdvancedValues ? "TRUE" : "FALSE") + "\"";
hres = psl->SetArguments(args);
if (! SUCCEEDED (hres))
{
AfxMessageBox ("Create shortcut: SetArguments failed!");
}
// Set the description of the shortcut.
hres = psl->SetDescription("Shortcut to CMakeSetup");
if (! SUCCEEDED (hres))
{
AfxMessageBox ("Create shortcut: SetDescription failed!");
}
// Ensure that the string consists of ANSI characters.
WORD wszAr[MAX_PATH];
LPWSTR wsz = (LPWSTR)wszAr;
MultiByteToWideChar(CP_ACP, 0, link_name, -1, (LPWSTR)(wsz), MAX_PATH);
// Save the shortcut via the IPersistFile::Save member function.
hres = ppf->Save(wsz, TRUE);
if (! SUCCEEDED (hres))
{
AfxMessageBox ("Create shortcut: Save failed!");
}
// Release the pointer to IPersistFile.
ppf->Release ();
}
// Release the pointer to IShellLink.
psl->Release ();
return 0;
}
void CMakeSetupDialog::OnHelpButton()
{
CMakeHelp dialog;
dialog.DoModal();
}
void CMakeSetupDialog::OnDeleteButton()
{
std::string message = "Are you sure you want to delete the CMakeCache.txt file for:\n";
message += m_WhereBuild;
if(::MessageBox(0, message.c_str(), "Delete Cache?",
MB_YESNO|MB_TASKMODAL) == IDNO)
{
return;
}
m_GeneratorPicked = false;
if(m_WhereBuild != "" && this->m_CMakeInstance)
{
this->m_CMakeInstance->GetCacheManager()->DeleteCache(m_WhereBuild);
}
// Make sure we are working from the cache on disk
this->LoadCacheFromDiskToGUI();
m_OKButton.EnableWindow(false);
}
void CMakeSetupDialog::ShowAdvancedValues()
{
m_CacheEntriesList.ShowAdvanced();
}
void CMakeSetupDialog::RemoveAdvancedValues()
{
m_CacheEntriesList.HideAdvanced();
}
void CMakeSetupDialog::OnSuppressDevValue()
{
}
void CMakeSetupDialog::OnDoubleclickedSuppressDevValue()
{
this->OnSuppressDevValue();
}
void CMakeSetupDialog::OnAdvancedValues()
{
this->UpdateData();
if(m_AdvancedValues)
{
this->ShowAdvancedValues();
}
else
{
this->RemoveAdvancedValues();
}
}
void CMakeSetupDialog::OnDoubleclickedAdvancedValues()
{
this->OnAdvancedValues();
}
// Handle param or single dropped file.
void CMakeSetupDialog::ChangeDirectoriesFromFile(const char* arg)
{
// Check if the argument refers to a CMakeCache.txt or
// CMakeLists.txt file.
std::string listPath;
std::string cachePath;
bool argIsFile = false;
if(cmSystemTools::FileIsDirectory(arg))
{
std::string path = cmSystemTools::CollapseFullPath(arg);
cmSystemTools::ConvertToUnixSlashes(path);
std::string cacheFile = path;
cacheFile += "/CMakeCache.txt";
std::string listFile = path;
listFile += "/CMakeLists.txt";
if(cmSystemTools::FileExists(cacheFile.c_str()))
{
cachePath = path;
}
if(cmSystemTools::FileExists(listFile.c_str()))
{
listPath = path;
}
}
else if(cmSystemTools::FileExists(arg))
{
argIsFile = true;
std::string fullPath = cmSystemTools::CollapseFullPath(arg);
std::string name = cmSystemTools::GetFilenameName(fullPath.c_str());
name = cmSystemTools::LowerCase(name);
if(name == "cmakecache.txt")
{
cachePath = cmSystemTools::GetFilenamePath(fullPath.c_str());
}
else if(name == "cmakelists.txt")
{
listPath = cmSystemTools::GetFilenamePath(fullPath.c_str());
}
}
// If there is a CMakeCache.txt file, use its settings.
if(cachePath.length() > 0)
{
cmCacheManager* cachem = m_CMakeInstance->GetCacheManager();
cmCacheManager::CacheIterator it = cachem->NewIterator();
if(cachem->LoadCache(cachePath.c_str()) && it.Find("CMAKE_HOME_DIRECTORY"))
{
std::string path = ConvertToWindowsPath(cachePath.c_str());
m_WhereBuild = path.c_str();
path = ConvertToWindowsPath(it.GetValue());
m_WhereSource = path.c_str();
m_GeneratorDialog.m_GeneratorChoiceString = _T("");
return;
}
}
// If there is a CMakeLists.txt file, use it as the source tree.
if(listPath.length() > 0)
{
std::string path = ConvertToWindowsPath(listPath.c_str());
m_WhereSource = path.c_str();
if(argIsFile)
{
// Source CMakeLists.txt file given. It was probably dropped
// onto the window or executable. Default to an in-source
// build.
m_WhereBuild = path.c_str();
}
else
{
// Source directory given on command line. Use current working
// directory as build tree.
std::string cwd = cmSystemTools::GetCurrentWorkingDirectory();
path = ConvertToWindowsPath(cwd.c_str());
m_WhereBuild = path.c_str();
}
}
}
// The framework calls this member function when the user releases the
// left mouse button over a window that has registered itself as the
// recipient of dropped files.
void CMakeSetupDialog::OnDropFiles(HDROP hDropInfo)
{
UINT nb_files = DragQueryFile(hDropInfo, 0xFFFFFFFF, NULL, 0);
if (nb_files > 0)
{
UINT buffer_size = DragQueryFile(hDropInfo, 0, NULL, 0);
char *buffer = new char [buffer_size + 1];
DragQueryFile(hDropInfo, 0, buffer, buffer_size + 1);
this->ChangeDirectoriesFromFile(buffer);
delete [] buffer;
this->m_WhereSourceControl.SetWindowText(this->m_WhereSource);
this->m_WhereBuildControl.SetWindowText(this->m_WhereBuild);
this->UpdateData(FALSE);
this->OnChangeWhereSource();
this->OnChangeWhereBuild();
}
DragFinish(hDropInfo);
}
BOOL CMakeSetupDialog::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
CDialog::OnSetCursor(pWnd, nHitTest, message);
if(m_Cursor == LoadCursor(NULL, IDC_WAIT))
{
::SetCursor(m_Cursor);
}
return true;
}