mercredi, juin 15

WTL Custom DDX

The DoDataExchange (DDX) is a recognizable method for initializing and/or saving dialog data, especially for GUI developers who have had experience in MFC through the years.

The existing DDX macros available however may not be sufficient to accomodate all types of scenarios.

Take for example a combo or a date time picker control. There are no existing DDX macros for them, developers especially those who aren't familiar with how DoDataExchange works, do the process of reading and writing to the controls upfront.

With custom DDX, developers are able to define their own macros depending on the situation which keeps the code simple encapsulating the arduous task of reading and writing data.

Below is a sample of how it might look like in practice. Notice that there is a clear distinction between the dialog controls vs the dialog data. Never mix the two. Example: m_btnMarried is an instance of check box button while m_bMarried is the value of the check box button.
// Controls
CButton m_btnMarié;
CEdit m_editNom;
CComboBox m_comboTitre;
CDateTimePickerCtrl m_dtpAnniversaire;

// Dialog data
bool m_bMarié;
wstring m_strNom
enumTitre m_eTitre;
SYSTEMTIME m_stAnniversaire;

BEGIN_DDX_MAP(CMaView)

DDX_CONTROL_HANDLE(IDC_CHECK, m_btnMarié)
DDX_CONTROL_HANDLE(IDC_EDIT, m_editNom)
DDX_CONTROL_HANDLE(IDC_COMBO, m_comboTitre)
DDX_CONTROL_HANDLE(IDC_DATE, m_dtpAnniversaire)


// Exiting DDX
DDX_CHECK(IDC_CHECK, m_bMarié)
DDX_TEXT(IDC_EDIT_NOM, nom)

// Custom DDX
DDX_COMBO(IDC_COMBO, (int&) m_eTitre)
DDX_DATETIMEPICKER(IDC_DATEPICKER, m_stAnniversaire)

END_DDX_MAP()

Without using the DDX framework, the developer needs to call the following lines upfront to be able to get to the value specified on the controls.
m_btnMarie.GetCheck()
m_editNom.GetWindowText()
m_comboTitre.GetCurSel()
m_dtpAnniversaire.GetSystemTime()

In contrast, using the DDX framework, the developer needs only to call DoDataExchange(FALSE) to initialise and DoDataExchange(TRUE) to update dialog data.

Of course, there is no DDX_COMBO and DDX_DATETIMEPICKER, we need to define it ourselves. Open the atlddx.h file and define the custom DDXs.

Inside the DoDataExchange method ..
//
// Custom DDX begin
//

#define DDX_DATETIMEPICKER(nID, var) \
if(nCtlID == (UINT)-1 || nCtlID == nID) \
{ \
if(!DDX_DateTimePicker(nID, var, TRUE, bSaveAndValidate)) \
return FALSE; \
}

#define DDX_COMBO_INDEX(nID, var) \
if(nCtlID == (UINT)-1 || nCtlID == nID) \
{ \
if(!DDX_Combo_Index(nID, var, TRUE, bSaveAndValidate)) \
return FALSE; \
}

//
// Custom DDX end
//

Inside WTL::CWinDataExchange class
//
// Custom DDX begin
//

template
BOOL DDX_Combo(UINT nID, Type& nVal, BOOL bSigned, BOOL bSave, BOOL
bValidate = FALSE, Type nMin = 0, Type nMax = 0)
{
T* pT = static_cast(this);
BOOL bSuccess = TRUE;

if(bSave)
{
CComboBox combo;
combo.Attach(pT->GetDlgItem(nID));
nVal = combo.GetCurSel();

bSuccess = (nVal == CB_ERR ? false : true);
}
else
{
ATLASSERT(!bValidate || nVal >= nMin && nVal <= nMax);

CComboBox combo;
combo.Attach(pT->GetDlgItem(nID));
int iRet = combo.SetCurSel(nVal);

bSuccess = (iRet == CB_ERR ? false : true);
}

if(!bSuccess)
{
pT->OnDataExchangeError(nID, bSave);
}
else if(bSave && bValidate) // validation
{
ATLASSERT(nMin != nMax);
if(nVal < nMin || nVal > nMax)
{
_XData data;
data.nDataType = ddxDataInt;
data.intData.nVal = (long)nVal;
data.intData.nMin = (long)nMin;
data.intData.nMax = (long)nMax;
pT->OnDataValidateError(nID, bSave, data);
bSuccess = FALSE;
}
}

return bSuccess;
}

template
BOOL DDX_DateTimePicker(UINT nID, Type& value, BOOL bSigned, BOOL bSave, BOOL
bValidate = FALSE)
{
T* pT = static_cast(this);
BOOL bSuccess = TRUE;

if(bSave)
{
CDateTimePickerCtrl dtp;
dtp.Attach(pT->GetDlgItem(nID));
DWORD dwSuccess = dtp.GetSystemTime(&value);

bSuccess = (dwSuccess == GDT_ERROR ? false : true);
}
else
{
ATLASSERT(!bValidate);

CDateTimePickerCtrl dtp;
dtp.Attach(pT->GetDlgItem(nID));
bSuccess = dtp.SetSystemTime(GDT_VALID, &value);
}

if(!bSuccess)
{
pT->OnDataExchangeError(nID, bSave);
}

return bSuccess;
}

//
// Custom DDX end
//

jeudi, juin 9

grunbar

c'est grunbar pour notre campagne en ligne - la couronne charogne (carrion crown) avec mes amis de rpgdlsu.

Grunbar, level 1
Dwarf, Runepriest
Build: Wrathful Runepriest
Runic Artistry: Wrathful Hammer

FINAL ABILITY SCORES
Str 20, Con 16, Dex 11, Int 10, Wis 10, Cha 8.

STARTING ABILITY SCORES
Str 18, Con 14, Dex 11, Int 10, Wis 10, Cha 8.

AC: 17 Fort: 15 Reflex: 10 Will: 12
HP: 28 Surges: 10 Surge Value: 7

TRAINED SKILLS
Religion +5, Heal +5, Endurance +10, Athletics +10

UNTRAINED SKILLS
Acrobatics, Arcana, Bluff -1, Diplomacy -1, Dungeoneering +2, History, Insight, Intimidate -1, Nature, Perception, Stealth, Streetwise -1, Thievery

FEATS
Level 1: Dwarven Weapon Training

POWERS
Runepriest at-will 1: Word of Shielding
Runepriest at-will 1: Word of Exchange
Runepriest encounter 1: Flames of Purity
Runepriest daily 1: Rune of Endless Fire

ITEMS
Adventurer's Kit, Warhammer, Scale Armor, Holy Symbol, Standard Identification Papers, Sunrod (2)

PERSONALITY TRAITS
Glory Seeker. Grunbar hopes to become famous by performing heroic deeds. He dreams of becoming one of the heroes of legend whose deeds are still sung though the hero died a thousand years ago. He fights for glory, reveling in the action, and dreams of his portrayal by storytellers to generations yet unborn. His love of action causes him to take risks, but he will try to avoid endangering his companions. He will place himself in grave danger to protect others or to rescue them, or even just to see if he can survive it, if that action alone would be heroic.

APPEARANCE
Grunbar has black hair and beard which gives him the impression of being grumpy and stern. But the truth is far from it. He is young and agile. He enjoys being the center of attention, but is not necessarily a skilled speaker. He may recognize his weaknesses and allow others to lead in negotiations. He wishes only to be acknowledged for the hero he is. When dealing with dwarves, particularly if he is the only dwarf in a party, he will take command of the situation, or at least make it clear that he is the leader of the party, even if he is not. He wishes others to see him as an epic figure.

BACKGROUND
Grunbar grew up in a remote seminary dedicate to Moradin at the outskirts of the Mror Holds. Life spent taking care of the usual business of daily prayers, practice duels, and running errands for my superiors. He was left there by his own father so that he could make something out of himself.

Life had been tied up to a routine until a lengthy missive came bearing ill news of my father's explorer friend, famed Professor Lorrimor, in the town of Teryk. I have very little to do with Lorrimor except that I knew they were close and they travelled together on several occasions. Father must be devastated by it, to have gone all the trouble of sending me a summon.

It has been a while since I've entered the seminary where life had been for the most part routinary and isolated. So I took this as a welcome break and an excuse to take a permanent leave and finally prove myself to be true to my faith as a full pledged man of the "hammer".

compare function object

problem: kinailangan kong kuhain yung listahan ng files sa isang directory. bukod sa listahan, kinailangan ko din na magkaka sunodsunod yung paths sa listahan base sa date modified or time stamp ng mga ito.

solution: ginamitan ko ng boost filesystem para sa pag kuha sa listahan. medyo straightforward naman ito sa boost. at ginamitan ko ng std::sort gamit ang compare function object para masa-iayos ko ang pagkakasunodsunod ng files sa listahan. ang oSortPathByTimeStamp ay ang compare function object na siyang naglalaman ng logica para malaman kung saan ang dapat kinalalagyan ng file sa listahan.
include vector
include string
include boost filesystem.hpp

using namespace std;
using namespace boost::filesystem;

struct SortPathByTimeStamp
{
bool operator()(wpath left, wpath right)
{
time_t timeLeft = last_write_time(left);
time_t timeRight = last_write_time(right);

return (timeLeft < timeRight);
}
} oSortPathByTimeStamp;
ailleurs ..
// Traverse all files from the target folder
wstring strTargetPath(L"C:\\Téléchargements");
std::vector vPaths;
wdirectory_iterator end_itr;
for (wdirectory_iterator pPath(strTargetPath);
pPath != end_itr;
++pPath)
{
// If a file
if (is_regular_file(pPath->status()))
{
// Save the path to the vector
wpath pathFile(pPath->path());
vPaths.push_back(pPath->path());
}
}

// Sort the list of paths by date modified
std::sort(vPaths.begin(), vPaths.end(), oSortPathByTimeStamp);

samedi, juin 4

waitable timers

sabihin natin na mayruon akong dalawang threads (thread1 at thread2) na kinakailangan kong patakbuhin ng sabay. magagawa ko ito gamit ang boost::threads. gamit ko ang function fx sa dalawang threads na ito.
map of wstring and boost::thread mapThreads;
mapThreads["Un"] = new thread(&fx);
mapThreads["Deux"] = new thread(&fx);
sabihin din natin na kailangan kong patakbuhin yung dalawang threads sa takdang oras (sometime in the future). gamit ko ay ang boost::posix_time::local_time para makuha ko ang oras ngayon at ma compute ko kung ilang oras pa dapat magintay bago patakbuhin ang bawat thread. kung ang takdang oras ay alas diyes at a la cinco ngayon. kailan kong magintay ng cinco oras. tama?
ptime maintenant = local_time(); (5h00)
ptime commence = futur - maintenant; (5 heures = 10h00 - 5h00)
gusto ko sanang gamitin ang boost waitable timers, pero nagka-problema ako sa pag interrupt sa kanya sa halibawang kailangan kong tumigal sa pagintay. ginamitan ko na lang ng while loop sa ngayon. yung sleep ay isang paraan para mapagbigyan ang ibang process ng execution time. hati hati kasi ang mga ito sa processor.
// create and run the threads map  mapThreads;
mapThreads["Un"] = new thread(&fx);
mapThreads["Deux"] = new thread(&fx);


fx()
{
try
{
interruption_point;

// Wait until time to start
// Dito sana yung waitable timer
while (maintenant < commence) sleep(1000);

// Faitres les autres
}
catch(interrupt)
{
}
}
so kung may nakakaalam kung paano iinterrupt ang mga waitable timers. mas eleganteng solution ito sa tingin ko kaysa sa paggamit ng while loop at sleep.

sa loob ng fx, kailangan mag-define ng interruption point at isang try catch block para mahuli ang interrupt exception. para mainterrupt ang isang thread. tawagin lang yung interrupt function ng thread;
mapThread["Un"]->interrupt();
---

nga pala, isang taon ng patay si papa nuong mayo 24. namimiss ko pa din siya. kahit na hindi naman kami close nuon buhay pa siya. hindi ko akalain na mamimiss ko siya ng ganito. sana nga totoo ang mga sabi sabi ng mga nakakatanda na mayruon "kabilang buhay". sa ganitong paraan, makikita ko muli si papa.

nag birthday si ruth nuong mayo 29. kumain lang kami sa ayala triangle. kasama si appa at si emil. invite ko din sana si dandy. pero wala ata sya sa maynila.