Products >Resource Editor

The development of an application with LFC (the "lcc-win32 foundation classes")is shown below. The steps are :

To show you how to use WEDIT and LFC, we are going to develop a little application that will help us to memorize informations concerning our friends.

The user interface will be a dialog box like this one :

the application we want to develop

The tab control selects the first letter of the name, the combo box shows all the names, and when we select a name, the radio buttons give Mrs, Miss or Mr, the date control shows the birth date and the edit field gives additional information.
We can modify the information when a name is selected, so we have to memorize it when the selection is changed : we can select another name in the listbox or select another tab.
All this information is contained in a file that is read when the application is loaded, and written when we quit the application.

The application will have a tab control : we then click on a tab to select all our friends whose name begins with this letter. Then we select one friend, so we can see the corresponding information, like the birth date.

First step : we load Wedit and create the new project :

Create Wedit project

We give the project name :

Give the project name

We want to create a Windows executable :

Select Windows executable

Now we ask Wedit to create the skeleton of the application :

Ask for the wizard

Obviously this is a french version of Windows ! But we can go on... We want a dialog box based application :

Dialog based application

The application is created :

Give the project name

Now we have to give informations to the compiler. As we will use LFC, we have to add 'gc.lib', 'lfc.lib' and 'weditres.lib' to the link. Click on 'Next' to go to the next page :

First information page : next Second information page : libraries, and next Last information page : terminate

Now we can compile the application if we want to, but we could only test a dialog box that we can close. We have to use LFC to go further.

Top of page Main product page

Choose the menu item 'Design' and open the resource :

Open the resource with LFC

As it is the first time we open the resource, we have to specify which file contains the definitions and is to be included :

Specify the include file

Select the dialog box with a double-click :

Select the dialog box

Now the serious things are coming. We see the dialog box that has been automatically created by the wizard. We can select the entire dialog box with a click in it's area (aclick on a button selects it). The object that is selected is shown in a special rectangle with red handle. They allow us to resize the object. We can also see and change the static and dynamic settings of the selected control if we hit the Ctrl-Y key :

Select the entire dialog box See the settings

Top of page Main product page

You can close the settings window. We do not want the OK and Cancel buttons : select one and press Ctrl-Del, select the other one and press Ctrl-Del. We have a dialog box with a title and the system buttons. We can give it a more reasonable size : select the lower right red handle and push it to the right and the bottom of the screen :

Select the entire dialog box

Top of page Main product page

We can now add the tab control. Leave a few moments the cursor on the first tool to obtain the list of available controls :

Ask for the list of controls

and select the tab control :

Select the tab control

Put the control with a simple click on the top left of the dialog, and resize it :

Place and resize the tab control

Now we put a combo box in the tab control : select the combo box control and put it into the tab control :

Select the combo box control

The default combo box does not have the style we prefer, so press the right button on the combo box and select 'Properties' :

Get the properties

This shows the properties. We can choose the simple style :

Select the simple style

The displayed combo box looks now like this :

The new display of the combo box

Take the bottom right handle to resize the combo. We want it to go to the bottom of the dialog, and it may be relatively large. We can already set up the property that will specify that we want to know when the selection changes. First select the dynamic properties, and set the 'Selection change' option :

Select the dynamic properties We want the 'Selection change' notification

Now we add the first radio button. Let the mouse on the first tool button and select the control 'Radio button' :

Select the radio button

Drop the radio button on the right of the combo box, and go to the name field to give it the right name (RB_MRS) and the text (Mrs) :

The name and text

Do the same for the other radio buttons, named RB_MISS and RB_MR, with the text Miss and Mr :

Select the radio button

Select the date control and name it ID_DATE. Select the text label and put one just before the date control. You should obtain something like :

The birth date

Now select the edit field and resize it :

Resize the edit field
Top of page Main product page

Now all the controls are created. You can rearrange them so they have a convenient size. Take care of the tab control : we need 3 lines of tabulations in the example that is shown, so we need to leave enough room for it.

When you want to see how it looks, you just have to click on the 'Test' button :

Test the dialog

Don't forget that the tab control will contain more than 4 tabs, so you have to leave more room that the test mode seems to show.
The test mode shows a dialog box that is created with the definitions we gave. Just close it to terminate the test mode :

Exit the test mode

It's time to verify the properties concerning all the controls. If the properties window is closed, open it (press Ctrl-Y or right click and choose 'Properties').
You can now select a control and see if the properties are correct. Modify them if necessary.
We want for the tab control :

Static properties, tab control

for the static properties (multi-line on) and

Dynamic properties, tab control

for the dynamic properties (we want the click notification).

The properties for the other controls are set to the default, except the combo box, but we have already set them. The last properties concern the dialog box itself. Select it and selexct the dynamic properties. We sould have :

Dynamic properties, dialog box

Save the dialog box definition, now we only have to add the callbacks :

Save the dialog box
 

Top of page Main product page

To add a callback concerning a control, select it with a click, and right-click and choose 'Callbacks'. We do that for the combo box. We obtain :

Callbacks, combo box

The callbacks we have to write are displayed in the left-hand listbox. They correspond to the dynamic properties : we get one callback when we set the corresponding option to On, and the name is that one we have asked for. We can use the default names.
To create a callback, just double-click on it's name. The file containing the callbacks is opened in Wedit, and the cursor is in the desired function. If necessary, the function is created, with the correct prototype, as now :

Callbacks, in the C file

This function will be called when the selection of the combo box changes. The first argument corresponds to the control (here the combo box) and the second one corresponds to the entire dialog box, and allows us to access to each control.
The function in this example is :

Callbacks, first function

We get the selection :
ctrl->GetCurSel();
The result is -1 if no line is selected, or the index of the line. So we get the text with :
ctrl->GetLBText(SelectedFriend,Name);
 
To access to another control, we use the dlg argument. For instance, we set the text of the edit field with :
dlg->id_text->SetWindowText(f->Text);
 
Top of page Main product page To go back to the design window, select Resources in the Window menu :

Return to the design window

You can now select another callback. Note that all the callbacks that are set up to On in the properties have to exist, it will be impossible to compile the application if one is missing. But it is easy to set the option to Off if you don't need it...

The callbacks we have to write are Dlg100Combo_box102SelChange, Dlg100Tab_control101Selected, Dlg100Init and Dlg100Destroy. The user interface does not need more functions !
 
Of course, we write other functions, because we have to limit each function to a readable code.
 

We have two modifications to do in clients.c, in the WinMain function.

Open friends.c and go to the line number 30. Insert these lines after WNDCLASS wc :


	INITCOMMONCONTROLSEX IC;
	IC.dwSize = sizeof(IC);
	IC.dwICC = ICC_WIN95_CLASSES | ICC_DATE_CLASSES | ICC_USEREX_CLASSES;
	InitCommonControlsEx(&IC);
And we have to call the LFC function instead of the default one for use with the dialog box. The last line of this function must be :

	return DialogBox(hinst, MAKEINTRESOURCE(IDD_MAINDIALOG), NULL, (DLGPROC) Dlg100);

The last thing to do is to add the additional files to the Wedit project. Select Project, Add/Delete files :

Configure the compiler
Top of page Main product page

Then add the 2 files friends_cbk.c and friends_gen.c. The project can be compiled, and you can run the application.

The entire file we have to write is :


#include <windows.h>
#include "friendsres.h"
#include <gc.h>
#include >stdio.h<

// A structure for one friend, chained to the next one
struct SFRIENDS {
	struct SFRIENDS *Next;
	char Name[256];
	int Mrs;
	SYSTEMTIME Birth;
	char Text[4096];
} *Friends;
int SelectedFriend;

// Write the file when we leave
void WriteFriends(HWND hwnd)
{
	int i;
	HANDLE file;
	char buffer[6000];
	DWORD nb;

	// The file is named "friends.txt" and is in the default directory.
	file=CreateFile("friends.txt",GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
	if (file==INVALID_HANDLE_VALUE) {
		MessageBox(hwnd,"Unable to create the file \"friends.txt\"","Error",MB_OK|MB_SETFOREGROUND);
		return;
	}
	// The file is created
	while (Friends) {
		sprintf(buffer,"\"%s\",%d,%d,%d,%d,\"%s\"\r\n",Friends->Name,Friends->Mrs,
				Friends->Birth.wYear,Friends->Birth.wMonth,Friends->Birth.wDay,Friends->Text);
		WriteFile(file,buffer,strlen(buffer),&nb,NULL);
		Friends=Friends->Next;
	}
	CloseHandle(file);
}

// Get rid of double quotes in the string
char * GetString(char *dest, char *inp, int nb)
{
	int i;
	char c;
	for (i=0; ielt;nb; i++) {
		c=inp[i];
		if (c==0) break;
		if (c=='"') {
			i++;
			break;
		}
		*dest++=c;
	}
	*dest=0;
	// Return next position
	return inp+i;
}

// Read the file friends.txt in the default directory
struct SFRIENDS *ReadFriends(void)
{
	HANDLE file;
	char *buffer, *p, c;
	DWORD size, read;
	struct SFRIENDS *f;

	Friends=0;
	// Try to open the file friends.txt
	file=CreateFile("friends.txt",GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
	if (file==INVALID_HANDLE_VALUE) return Friends;
	size=GetFileSize(file, NULL);
	if (size==0xFFFFFFFF) {
		CloseHandle(file);
		return Friends;
	}

	// Read the entire file
	buffer=GC_malloc(size);
	ReadFile(file,buffer,size,&read,NULL);
	CloseHandle(file);

	// Put the data into chained structures
	p=buffer;
	while (p && *p) {
		f=GC_malloc(sizeof(struct SFRIENDS));
		// Go to next '"' char
		p=strchr(p,'"');
		if (!p) break;
		p++;
		// The first string in quotes is the name
		p=GetString(f->Name,p,255);
		if (!(p && *p)) break;
		p=strchr(p,',');
		if (!p) break;
		p++;
		// A number represents Mrs, Miss or Mr
		f->Mrs=atoi(p);
		p=strchr(p,',');
		if (!p) break;
		p++;
		// The date is stored in 3 numbers
		f->Birth.wYear=atoi(p);
		p=strchr(p,',');
		if (!p) break;
		p++;
		f->Birth.wMonth=atoi(p);
		p=strchr(p,',');
		if (!p) break;
		p++;
		f->Birth.wDay=atoi(p);
		p=strchr(p,',');
		if (!p) break;
		p++;
		// Go to the next quote
		p=strchr(p,'"');
		if (!p) break;
		p++;
		// Read all the text in quotes
		p=GetString(f->Text,p,4095);
		f->Next=Friends;
		Friends=f;
	}
	return Friends;
}

// Get rid of new lines in the text.
void VerifyChars(char *str)
{
	char *dest=str, c;
	while (0!=(c=*str++)) {
		if (c=='"') c='\'';
		else if (c=='\r') continue;
		else if (c=='\n') c=' ';
		*dest++=c;
	}
	*dest=0;
}

// Add a friend in a SFRIENDS structure and chain it
void StoreCurrentFriend(struct _Dlg100 *dlg)
{
	struct SFRIENDS *f, *g, *o;
	int addit;
	char c, init;

	f=GC_malloc(sizeof(struct SFRIENDS));

	dlg->id_names->GetWindowText(f->Name,255);
	if (!*f->Name) return; // Nothing to store

	// Avoid stange chars in name
	VerifyChars(f->Name);

	// Before store the new one, forget the old selected friend
	if (SelectedFriend!=-1) {
		char name[256];
		dlg->id_names->GetLBText(SelectedFriend,name);
		o=0;
		for (g=Friends; g; o=g, g=g->Next) {
			if (!strcmp(g->Name,name)) {
				if (o) o->Next = g->Next;
				else Friends=g->Next;
				break;
			}
		}
	}

	if (dlg->rb_mrs->GetState()& BST_CHECKED) f->Mrs=1;
	else if (dlg->rb_miss->GetState()& BST_CHECKED) f->Mrs=2;
	else if (dlg->rb_mr->GetState()& BST_CHECKED) f->Mrs=3;
	dlg->id_date->GetSystemTime(&f->Birth);
	dlg->id_text->GetWindowText(f->Text,4096);

	// Avoid stange chars in text
	VerifyChars(f->Text);

	// And force the line in the combo to be that one
	if (SelectedFriend!=-1) {
		dlg->id_names->DeleteString(SelectedFriend);
	}
	// Leave the name if it is not in that tab
	init='A'+dlg->id_tab->GetCurSel();
	c=*f->Name;
	addit=1;
	if (init<='Z') {
		if (c!=init && c!=init+'a'-'A') addit=0;
	}
	else {
		if ((c>='A' && c<='Z') || (c>='a' && c<='z')) addit=0;
	}
	if (addit) dlg->id_names->AddString(f->Name);

	// Chain the structure
	f->Next=Friends;
	Friends=f;
}

// Clear all the frind information on the screen
void ClearSelectedFriend(struct _Dlg100 *dlg)
{
	SYSTEMTIME st;
	dlg->id_names->SetWindowText("");
	dlg->rb_mrs->SetCheck(BST_UNCHECKED);
	dlg->rb_miss->SetCheck(BST_UNCHECKED);
	dlg->rb_mr->SetCheck(BST_UNCHECKED);
	GetSystemTime(&st);
	dlg->id_date->SetSystemTime(GDT_VALID,&st);
	dlg->id_text->SetWindowText("");
	SelectedFriend=-1;
}

// Callback : the selected item of the combo box has changed
long Dlg100Combo_box102SelChange(ST_COMBOBOX *ctrl,struct _Dlg100 *dlg)
{
	struct SFRIENDS *f;
	char Name[256];

	// First store the currently selected friend
	StoreCurrentFriend(dlg);

	// Get the selected friend
	SelectedFriend=ctrl->GetCurSel();
	if (SelectedFriend==-1) {
		// No one selected : clear the screen
		ClearSelectedFriend(dlg);
		return 0;
	}

	// Find the structure corresponding to the selected friend
	ctrl->GetLBText(SelectedFriend,Name);
	for (f=Friends; f; f=f->Next) {
		if (!strcmp(f->Name,Name)) {
			// It's that friend : display it's information
			dlg->id_names->SetWindowText(f->Name);
			dlg->rb_mrs->SetCheck((f->Mrs==1)?BST_CHECKED:BST_UNCHECKED);
			dlg->rb_miss->SetCheck((f->Mrs==2)?BST_CHECKED:BST_UNCHECKED);
			dlg->rb_mr->SetCheck((f->Mrs==3)?BST_CHECKED:BST_UNCHECKED);
			dlg->id_date->SetSystemTime(GDT_VALID,&f->Birth);
			dlg->id_text->SetWindowText(f->Text);
			break;
		}
	}
	return 1;
}

// Select a letter in the tab control : clear the displayed friend and display the new list
void SelectLetter(ST_TABCTRL *ctrl,struct _Dlg100 *dlg)
{
	struct SFRIENDS *f;
	char init, c;

	// Clear the actual friend
	ClearSelectedFriend(dlg);

	// Set up the list of friends with same initial
	dlg->id_names->ResetContent();
	init='A'+ctrl->GetCurSel();
	for (f=Friends; f; f=f->Next) {
		c=*f->Name;
		if (init<='Z') {
			if (c!=init && c!=init+'a'-'A') continue;
		}
		else {
			if ((c>='A' && c<='Z') || (c>='a' && c<='z')) continue;
		}
		dlg->id_names->AddString(f->Name);
	}
}

// Start-up : load the file, and initialize a few controls
long Dlg100Init(ST_DIALOGBOX *ctrl,struct _Dlg100 *dlg)
{
	// Initialize the dialog
	TC_ITEM item;
	int i;
	char buffer[256];

	// Read the file
	Friends = ReadFriends();

	// Limit size...
	dlg->id_text->SetLimitText(4095);
	dlg->id_names->LimitText(255);

	// Create the tabs : one for each letter and 'other'
	memset(&item,0,sizeof(item));
	item.mask=TCIF_TEXT;
	item.pszText=buffer;
	buffer[1]=0;
	for (i=0; i<26; i++) {
		*buffer='A'+i;
		dlg->id_tab->InsertItem(i,&item);
	}
	item.pszText="Other";
	dlg->id_tab->InsertItem(i,&item);

	// 'A' is selected
	SelectLetter(dlg->id_tab, dlg);

	return 0;
}

// A new letter is selected : store the current friend and change the letter
long Dlg100Tab_control101Selected(ST_TABCTRL *ctrl,struct _Dlg100 *dlg)
{
	// Store the current informations
	StoreCurrentFriend(dlg);
	// and select the new letter
	SelectLetter(ctrl, dlg);

	return 0;
}

// End of application : store the new file
void Dlg100Destroy(HWND hwnd)
{
	WriteFriends(hwnd);
}