// CKMRWDlg dialog implementation
(KM-RWDlg-Impl.h):
//================== CONSTRUCTION,
INITIALIZATION & SUCH ============================================================
// This message is sent when app starts and finds out it is set up
// tostart playback automatically:
static UINT NEAR WM_KMRW_AUTOPLAY =
RegisterWindowMessage("Automatic Playback");
// This one is sent at the end of each
playback cycle when app
// is set up to play Journal more than once:
static UINT NEAR WM_KMRW_PLAYAGAIN =
RegisterWindowMessage("Play Again");
BEGIN_MESSAGE_MAP(CKMRWDlg, CDialog)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_REGISTERED_MESSAGE(WM_KMRW_AUTOPLAY, AutoPlay)
ON_REGISTERED_MESSAGE(WM_KMRW_PLAYAGAIN, Play1Time)
ON_COMMAND(IDC_RECORD_BUTTON, DoRecord)
ON_COMMAND(IDC_PLAY_BUTTON, DoPlay)
ON_COMMAND(IDC_STOP_BUTTON, DoStop)
ON_COMMAND(IDC_PO_BUTTON, DoPSDialog)
ON_COMMAND(ID_INFO, DoHelpDlg)
END_MESSAGE_MAP()
//-------------------------------------------------------------------------------------------------------------------
// Wizard-created constructor:
CKMRWDlg::CKMRWDlg(CWnd* pParent /*=NULL*/)
: CDialog(CKMRWDlg::IDD, pParent), outstream(outstring,200)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
//-------------------------------------------------------------------------------------------------------------------
BOOL CKMRWDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// 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
// TODO: Add extra initialization here
//Get Controls:
recordBtn=(CButton *)GetDlgItem(IDC_RECORD_BUTTON);
playBtn=(CButton *)GetDlgItem(IDC_PLAY_BUTTON);
stopBtn=(CButton *)GetDlgItem(IDC_STOP_BUTTON);
outBox=(CListBox *)GetDlgItem(IDC_OUT_LIST);
//To make windows transparent:
lw.AddLayeredStyle(m_hWnd); // Modify the
style
lw.SetTransparentPercentage(m_hWnd, 100);
// Set the trasparency effect to __%
//Initialize the hook interface class:
cwfi.InitParent(this);
autoplay_in_progress=0;
//Load KMRW settings:
if(!settgs.LoadSettings()) {
WriteToBox("Failed to load settings from KMRWSettings.i44, defaults
loaded.");
settgs.SaveSettings();
}
//Start AutoPlay if necessary:
if(settgs.PlayOnStartup) {PostMessage(WM_KMRW_AUTOPLAY);}
status=STOPPED;
UpdateUI();
return TRUE;
// return TRUE unless you set the focus to a control
}
//-------------------------------------------------------------------------------------------------------------------
//This is wizard-created too:
void CKMRWDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this);
// device context for painting
SendMessage(WM_ICONERASEBKGND,
reinterpret_cast<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 function to obtain the cursor to display
while the user drags
// the minimized window.
HCURSOR CKMRWDlg::OnQueryDragIcon()
{
return
static_cast<HCURSOR>(m_hIcon);
}
//====================== SOME OTHER
UTILITIES
=======================================================================
void CKMRWDlg::DoPSDialog(){
//Do the settings dialog:
PSDialog psd(this);
psd.DoModal();
//Save settings:
settgs.SaveSettings();
}
//-------------------------------------------------------------------------------------------------------------------
// This functionjust adds a string to the app's ListBox:
void CKMRWDlg::WriteToBox(CString strg){
outBox->AddString(strg);
//Seems like the only way known to humanity
to scroll automatically:
outBox->SetCurSel(outBox->GetCurSel()+1);
}
//-------------------------------------------------------------------------------------------------------------------
//GLOBAL VERSION:
void WriteToBox(CString strg){
( (CKMRWDlg*)(AfxGetApp()->m_pMainWnd) )->WriteToBox(strg);
}
//==================== RECORDING FUNCTIONS
==========================================================================
// These functions start, maintain, pause and stop recording and
playback:
void CKMRWDlg::DoRecord(){
ContinueRecording();
}
//-------------------------------------------------------------------------------------------------------------------
void CKMRWDlg::ContinueRecording(){
//if(status==RECORDING) return;
status=RECORDING;
UpdateUI();
PrevMessageTime=::GetCurrentTime();
// This will insert a callback hook that
will record all events:
cwfi.HookupRecord();
}
//-------------------------------------------------------------------------------------------------------------------
void CKMRWDlg::PauseRecord(bool
paused_by_user){
//remove hook first:
cwfi.UnhookRecord();
// Program will know if user paused
recording by pressing <Ctrl+Break>,
// or system removed hook (in response to <Ctrl+Alt+Del> or I don't
know what).
if(paused_by_user) status=REC_PAUSED_USER;
else status=REC_PAUSED_SYSTEM;
UpdateUI();
}
//-------------------------------------------------------------------------------------------------------------------
void CKMRWDlg::StopRecord(){
status=STOPPED;
WriteToBox("Recording Stopped.");
UpdateUI();
if(AfxMessageBox("Recording has been
stopped, do you want to save Journal to file?",
MB_ICONQUESTION | MB_YESNO |MB_TASKMODAL)==IDYES){
SaveList();
}
//Every time recording or playback is
stopped, Journal is discarded.
Journal.RemoveAll();
}
//================ PLAYBACK FUNCTIONS
===============================================================================
void CKMRWDlg::DoPlay(){
//PLAY FROM FILE
if(status==PLAYING)
return;
if(status==PLAY_PAUSED_USER || status==PLAY_PAUSED_SYSTEM)
{ContinuePlaying(); return;}
//Clear Journal:
Journal.RemoveAll();
//Load Journal:
if(!ReadList())
return;
//Start playback:
StartPlayingSession();
}
//-------------------------------------------------------------------------------------------------------------------
//This function called as a response to a WM_KMRW_AUTOPLAY message:
afx_msg LRESULT CKMRWDlg::AutoPlay(WPARAM wParam, LPARAM lParam){
//SHOW WARNING:
WriteToBox(" ATTENTION !!!");
WriteToBox("-------------------------------------------");
WriteToBox("KMRW is currently set up to start playback");
WriteToBox("automatically. Automatic playback will start");
WriteToBox("in 10 seconds. Once it starts, you can stop it");
WriteToBox("by pressing Ctrl+Break, and then clicking Stop");
WriteToBox("button. You can change this setting later.");
WriteToBox("-------------------------------------------");
WriteToBox("");
//Wait 10 seconds:
Sleep(10000);
autoplay_in_progress=1;
WriteToBox("Auto Playback Started");
//LOAD LIST WITHOUT FileDialog:
CString filepath=settgs.PlayFilePath;
if(filepath=="")
return 0;
ifstream inf(filepath.GetString(),ios::binary);
CString status;
if(!inf) {
status=status+"Failed to open " +filepath+ " for reading";
WriteToBox(status);
return 0;
}
else {
Journal.RemoveAll();
StoreStruct tempsst;
while( inf.read((char*)&tempsst,sizeof(StoreStruct))
)
{ Journal.AddTail(tempsst); }
}
inf.close();
outstream.seekp(0);
outstream<<"Loaded "<<Journal.GetCount()<<" records from "<<filepath.GetString()<<ends;
WriteToBox(outstring);
//Start playback:
StartPlayingSession();
return 0;
}
//-------------------------------------------------------------------------------------------------------------------
void CKMRWDlg::StartPlayingSession(){
// Initializes Cycle count, the number of
times whole Journal is played:
PlayCycleCount=1;
//START PLAYBACK:
Play1Time(0,0);
}
//-------------------------------------------------------------------------------------------------------------------
// This function is called in response to WM_KMRW_PLAYAGAIN message,
// which is sent when Journal has not been played the preset number
of times yet:
afx_msg LRESULT CKMRWDlg::Play1Time(WPARAM wParam, LPARAM lParam){
// Initializes played event count
PlayEventCount=0;
tempss=&Journal.GetHead();
readPOS=Journal.GetHeadPosition();
ContinuePlaying();
return 0;
}
//-------------------------------------------------------------------------------------------------------------------
void CKMRWDlg::ContinuePlaying(){
status=PLAYING;
UpdateUI();
//This variable tells the callback if the
next event is redy to be fed:
FeedNextMsg=true;
//Insert hook:
cwfi.HookupPlay();
}
//-------------------------------------------------------------------------------------------------------------------
void CKMRWDlg::PausePlay(bool
paused_by_user){
// Remove hook first:
cwfi.UnhookPlay();
// Program will know if user paused
playback by pressing <Ctrl+Break>,
// or system removed hook (in response to <Ctrl+Alt+Del> or I don't
know what).
if(paused_by_user) status=PLAY_PAUSED_USER;
else status=PLAY_PAUSED_SYSTEM;
UpdateUI();
}
//-------------------------------------------------------------------------------------------------------------------
// This function is called when program is done playing Journal 1
time,
// i.e. at the end of the Journal.
void CKMRWDlg::StopPlay1Time(){
cwfi.UnhookPlay();
WriteToBox("Playback cycle ended.");
UpdateUI();
PlayCycleCount++;
//Play another cycle if needed:
if(PlayCycleCount <= settgs.PlayTimes)
PostMessage(WM_KMRW_PLAYAGAIN);
else {
StopPlay();
//Close app if it was an automatic
playback:
if(autoplay_in_progress)
PostMessage(WM_CLOSE);
}
}
//-------------------------------------------------------------------------------------------------------------------
void CKMRWDlg::StopPlay(){
if(status==STOPPED)
return;
status=STOPPED;
cwfi.UnhookPlay();
Journal.RemoveAll();
WriteToBox("Playback stopped.");
UpdateUI();
}
//====================== INTERRUPTION
FUNCTIONS
=====================================================================
// This function would respond to an interruption,
// determine who did it, and whether playback or
// recording was on at the time, to pause it appropriately
// and update UI.
void CKMRWDlg::InterruptionHandler(bool paused_by_user){
if(paused_by_user){WriteToBox("PAUSED
BY USER");}
else {WriteToBox("PAUSED BY SYSTEM");}
if(status==RECORDING)
PauseRecord(paused_by_user);
if(status==PLAYING)
PausePlay(paused_by_user);
}
//-------------------------------------------------------------------------------------------------------------------
//To determine how to respond when <Stop> button is clicked:
void CKMRWDlg::DoStop(){
if(status==REC_PAUSED_USER || status==REC_PAUSED_SYSTEM
|| status==RECORDING) {StopRecord(); return;}
if(status==PLAY_PAUSED_USER ||
PLAY_PAUSED_SYSTEM || status==PLAYING) {StopPlay();
return;}
}
//================= UPDATE USER INTERFACE
===========================================================================
void CKMRWDlg::UpdateUI(){
if(status==STOPPED){
// STOPPED
ModifyStyleEx(WS_EX_TRANSPARENT,0);
recordBtn->SetWindowText("Record");
playBtn->SetWindowText("Play");
recordBtn->EnableWindow(1);
playBtn->EnableWindow(1);
stopBtn->EnableWindow(0);
this->SetWindowText("STOPPED - (KM-RW)");
lw.SetTransparentPercentage(m_hWnd, 100);
// Set the trasparency effect to __%
}
else if(status==PLAYING){
ModifyStyleEx(0, WS_EX_TRANSPARENT);
recordBtn->SetWindowText("Record");
recordBtn->EnableWindow(0);
playBtn->EnableWindow(0);
stopBtn->EnableWindow(0);
SetWindowText("PLAYING-(KM-RW), <Ctrl+Break> to PAUSE");
lw.SetTransparentPercentage(m_hWnd, 35); //
Set the trasparency effect to __%
}
else if(status==RECORDING){
ModifyStyleEx(0, WS_EX_TRANSPARENT);
playBtn->SetWindowText("Play");
recordBtn->EnableWindow(0);
playBtn->EnableWindow(0);
stopBtn->EnableWindow(0);
SetWindowText("RECORDING - (KM-RW), <Ctrl+Break> to PAUSE");
lw.SetTransparentPercentage(m_hWnd, 35); //
Set the trasparency effect to __%
}
else if(status==PLAY_PAUSED_USER){
ModifyStyleEx(WS_EX_TRANSPARENT,0);
lw.SetTransparentPercentage(m_hWnd, 100);
// Set the trasparency effect to __%
SetWindowText("PAUSED PLAYBACK - (KM-RW)");
recordBtn->SetWindowText("Record");
recordBtn->EnableWindow(0);
playBtn->SetWindowText("Continue Playback");
playBtn->EnableWindow(1);
stopBtn->EnableWindow(1);
}
else if(status==PLAY_PAUSED_SYSTEM){
ModifyStyleEx(WS_EX_TRANSPARENT,0);
lw.SetTransparentPercentage(m_hWnd, 100);
// Set the trasparency effect to __%
SetWindowText("PAUSED PLAYBACK - (KM-RW)");
recordBtn->SetWindowText("Record");
recordBtn->EnableWindow(0);
playBtn->SetWindowText("Continue Playback");
playBtn->EnableWindow(1);
stopBtn->EnableWindow(1);
}
else if(status==REC_PAUSED_USER){
ModifyStyleEx(WS_EX_TRANSPARENT,0);
lw.SetTransparentPercentage(m_hWnd, 100);
// Set the trasparency effect to __%
SetWindowText("PAUSED RECORDING - (KM-RW)");
playBtn->SetWindowText("Play");
recordBtn->SetWindowText("Continue Recording");
recordBtn->EnableWindow(1);
playBtn->SetWindowText("Play");
playBtn->EnableWindow(0);
stopBtn->EnableWindow(1);
}
else if(status==REC_PAUSED_SYSTEM){
ModifyStyleEx(WS_EX_TRANSPARENT,0);
lw.SetTransparentPercentage(m_hWnd, 100);
// Set the trasparency effect to __%
SetWindowText("PAUSED RECORDING - (KM-RW)");
playBtn->SetWindowText("Play");
recordBtn->SetWindowText("Continue Recording");
recordBtn->EnableWindow(1);
playBtn->EnableWindow(0);
playBtn->SetWindowText("Play");
stopBtn->EnableWindow(1);
}
else {
WriteToBox("This is not supposed to happen!");
WriteToBox("Please write to dev@intellimech.com");
WriteToBox("and say you have a KMRW status problem.");
}
this->UpdateWindow();
}
//===================== HELP DIALOG (DHTML)
=========================================================================
//Runs the Help DHTML Dialog, when <Info> button is clicked, nothing
fancy:
void CKMRWDlg::DoHelpDlg(){
CHelpDHTMLDialog infodlg(this);
infodlg.DoModal();
}
//===================== FILE PROCESSING
=============================================================================
// Saves journal to disk:
bool CKMRWDlg::SaveList(){
CFileDialog fdlg(0,"i45","Journal.i45",
OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT|OFN_FILEMUSTEXIST,0,this);
fdlg.GetOFN().lpstrTitle="Save journal as:";
fdlg.GetOFN().lpstrFilter="KM-RW Journal Files (.i45)\0*.i45\0\0";
int result=fdlg.DoModal();
if(result!=IDOK)
return 0;
CString filepath=fdlg.GetPathName();
if(filepath=="")
return 0;
ofstream outf(filepath.GetString(), ios::binary);
CString status;
if(!outf) {
status=status+"Could not open "+filepath+ " for writing";
WriteToBox(status);
return 0;
}
else {
//Load the records:
POSITION currPOS=Journal.GetHeadPosition();
while(currPOS!=NULL) {
outf.write((char*)(&Journal.GetNext(currPOS)),sizeof(StoreStruct));
}
outf.close();
outstream.seekp(0);
outstream<<"Saved "<<Journal.GetCount()<<" records to "<<filepath.GetString()<<ends;
WriteToBox(outstring);
}
return 1;
}
//-------------------------------------------------------------------------------------------------------------------
// Loads Journal from disk:
bool CKMRWDlg::ReadList(){
CFileDialog fdlg(1,"i45", 0,
OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT|OFN_FILEMUSTEXIST,0,this);
fdlg.GetOFN().lpstrTitle="Open journal:";
fdlg.GetOFN().lpstrFilter="KM-RW Journal Files (.i45)\0*.i45\0\0";
int result=fdlg.DoModal();
if(result!=IDOK)
return 0;
CString filepath=fdlg.GetPathName();
if(filepath=="")
return 0;
ifstream inf(filepath.GetString(),ios::binary);
CString status;
if(!inf) {
status=status+"Could not open "+filepath+ " for reading";
WriteToBox(status);
return 0;
}
else {
Journal.RemoveAll();
StoreStruct tempsst;
while(
inf.read((char*)&tempsst,sizeof(StoreStruct)) )
{ Journal.AddTail(tempsst); }
inf.close();
outstream.seekp(0);
outstream<<"Loaded "<<Journal.GetCount()<<" records from
"<<filepath.GetString()<<ends;
WriteToBox(outstring);
}
return 1;
}
#ifndef PL_REC_PROCS_H
#define PL_REC_PROCS_H
//-------------------------------------------------------------------------------------------------------------------
void CKMRWDlg::ProcessRecordHook(int
code,WPARAM wparam,LPARAM lparam){
//Only processing HC_ACTION codes:
if(code!=HC_ACTION)
return;
EVENTMSG * pMsg=(EVENTMSG *)lparam;
currMSG = *pMsg;
outstream.seekp(0);
outstream<<" "<<Journal.GetCount()+1;
if(pMsg->message==WM_MOUSEMOVE) {outstream<<"
Mouse Move: ";}
else if(pMsg->message==WM_LBUTTONDOWN)
{outstream<<" Mouse Left Btn Down: ";}
else if(pMsg->message==WM_LBUTTONUP) {outstream<<"
Mouse Left Btn Up: ";}
else if(pMsg->message==WM_RBUTTONDOWN)
{outstream<<" Mouse Right Btn Down: ";}
else if(pMsg->message==WM_RBUTTONUP) {outstream<<"
Mouse Right Btn Up: ";}
else if(pMsg->message==WM_MOUSEWHEEL) {outstream<<"
Mouse Wheel: ";}
else if(pMsg->message==WM_MBUTTONDOWN)
{outstream<<" Mouse Mid Btn Down: ";}
else if(pMsg->message==WM_MBUTTONUP) {outstream<<"
Mouse Mid Btn Up: ";}
else if(pMsg->message==WM_KEYDOWN) {outstream<<"
Keyboard key down: ";}
else if(pMsg->message==WM_KEYUP) {outstream<<"
Keyboard key up: ";}
else if(pMsg->message==WM_SYSKEYDOWN) {outstream<<"
Keyboard sys. key down: ";}
else if(pMsg->message==WM_SYSKEYUP) {outstream<<"
Keyboard sys. key up: ";}
else {outstream<<" Unidentified event:
";}
currMSG.time=pMsg->time - PrevMessageTime;
PrevMessageTime=pMsg->time;
if(pMsg->hwnd)
outstream<<pMsg->hwnd<<" ";
outstream<<"("<<pMsg->paramL<<","<<pMsg->paramH<<") +";
outstream<<currMSG.time;
outstream<<ends;
WriteToBox(outstring);
Journal.AddTail(StoreStruct(KMMSG, &currMSG));
}
//-------------------------------------------------------------------------------------------------------------------
LRESULT CALLBACK CKMRWDlg::ProcessPlayHook(int
code,WPARAM wparam,LPARAM lparam){
//Process only HC_GETNEXT & HC_SKIP codes,
call CallNextHookEx() on anything else.
//(*)System calls JournalPlaybackProc with
HC_GETNEXT when a message is processed:
if(code==HC_GETNEXT){
//Stop playback when reached end of
Journal:
if(readPOS==0) {StopPlay1Time();
return 0;}
//If Keyboard/Mouse message:
if(tempss->GetCommand()==KMMSG) {
//If ready to process next message
(function was just called with HC_SKIP):
if(FeedNextMsg){
//Get next message from Journal, update
POSITION pointer:
tempss=&Journal.GetNext(readPOS);
//Load message to lparam:
*(EVENTMSG *)lparam=tempss->GetMsg();
//Until system is ready to take another
message:
FeedNextMsg=false;
}
//If not ready for next message, return 0;
else { *(EVENTMSG
*)lparam=tempss->GetMsg(); return 0; }
}
//If not Keyboard/Mouse message:
else { }
//add code for extended messages
//DISPLAY MESSAGE INFO:
EVENTMSG * lparammsg=(EVENTMSG *)lparam;
outstream.seekp(0);
outstream<<" "<<++PlayEventCount;
if(lparammsg->message==WM_MOUSEMOVE)
{outstream<<" Mouse Move: ";}
else if(lparammsg->message==WM_LBUTTONDOWN)
{outstream<<" Mouse Left Btn Down: ";}
else if(lparammsg->message==WM_LBUTTONUP)
{outstream<<" Mouse Left Btn Up: ";}
else if(lparammsg->message==WM_RBUTTONDOWN)
{outstream<<" Mouse Right Btn Down: ";}
else if(lparammsg->message==WM_RBUTTONUP)
{outstream<<" Mouse Right Btn Up: ";}
else if(lparammsg->message==WM_MOUSEWHEEL)
{outstream<<" Mouse Wheel: ";}
else if(lparammsg->message==WM_KEYDOWN)
{outstream<<" Keyboard key down: ";}
else if(lparammsg->message==WM_KEYUP)
{outstream<<" Keyboard key up: ";}
else if(lparammsg->message==WM_SYSKEYDOWN)
{outstream<<" Keyboard sys. key down: ";}
else if(lparammsg->message==WM_SYSKEYUP)
{outstream<<" Keyboard sys. key up: ";}
else if(lparammsg->message==WM_MBUTTONDOWN)
{outstream<<" Mouse Mid Btn Down: ";}
else if(lparammsg->message==WM_MBUTTONUP)
{outstream<<" Mouse Mid Btn Up: ";}
else {outstream<<" Unidentified
message: ("<<lparammsg->message<<") ";}
//if(lparammsg->hwnd)
outstream<<lparammsg->hwnd<<" ";
outstream<<"("<<lparammsg->paramL<<","<<lparammsg->paramH<<") +";
outstream<<lparammsg->time;
outstream<<ends;
//Add info to list box:
WriteToBox(outstring);
//This return will make system wait fo
lparammsg->time milliseconds:
return settgs.PlayAtRecSpeed ?
lparammsg->time+1 : 20; // <- default delay
is 20 ms.
//return lparammsg->time+1;
}
//(*)Framework passes this code when ready
to accept next message:
else if(code==HC_SKIP) {
//Allow function to feed new message on
next call:
FeedNextMsg=true;
return 0;
}
//(*)On any other code:
else return
::CallNextHookEx(cwfi.PlayHookHandle,code,wparam,lparam);
//return 0;
}
#endif