第六章 控件
在这一章中,我们将继续研究Contact Detail 程序,让它具有显示并设置日期时间的功能。我们要添加一个窗体来设置时间。这个时间和日期是可选的以供下次联系使用。
为实现此功能,我们将使用新的资源:选择触发器(selector triggers),开关按钮(push buttons),重复按钮(repeating buttons)。连同前面的按钮,它们都是Palm OS的控件。它们具有类似的属性,并且在触发时发出相同的事件。它们都可拥有自己的标签,它们都是被单击触发的。在单击后它们的形状都有所改变,不过有的只是瞬间改变就恢复了而已。
保存你的工程
当在修改工程之前,最好先制作一个它的副本。这样当出现问题时,你就可以拿出的工程的副本重新开始。步骤如下:
1. 打开Windows 资源管理器;
2. 找到工程所在的文件夹;
3. 选中工程,按下CTRL+C拷贝文件夹;
4. 选择想要保存到的文件夹;
5. 按下CTRL+V保存;
6. 将工程重新命名,以便你能清楚记忆。我将其命名为Contacts CH.5。
对Contatcs.rsrc文件内容的添加
这一部分我们为Contact Detail添加日期和时间的控件。我们还将创建一个用来改变时间的窗体。对改变日期,我们将调用Palm OS的标准对话框。
添加日期时间选择触发控件
向Contact Detail窗体添加两个标签和两个选择触发器(selector triggers)。我们将使用选择触发来显示下一次调用此contact的日期和时间。选择触发控件处理事件和按钮很相似,只是外形有很大不同。它被一个点壮矩形所环绕。和按钮相比,这个矩形在宽度和高度上都占有一个象素的额外空间,这一点在放置此控件时要考虑。有关选择触发器(selector triggers)的属性见表6-1。
Object Identifier 构造器用来代表资源头文件ID的常量
Selector Trigger ID 选择触发控件的资源ID;
Left Origin 水平方向上控件的最左端位置;
Top Origin 垂直方向上控件的最顶端位置;
Width 控件的最大宽度。此属性很少使用,因为控件的右
边界会随着标签文本的长度改变而改变;
Height 控件的高度;
Usable 决定次控件是否可见能用。如果没有选中,也可在通过函数调用来实现其可见;
Anchor Left 决定当文本长度改变时,控件的左侧或右侧是否做相应的伸缩;
Font 标签使用的字体;
Label 标签的缺省文本;
以下是添加控件的步骤:
1. 打开资源构造器;
2. 打开文件Contacts.rsrc。它在Src文件夹中;
3. 双击Contacts Detail窗体;
4. 选择Window | Catalog,产生控件模板;
5. 将一个标签拖到窗体上。置标签文本为Next Call Date。将它放在Phone Number 标签底下。设置Left Origin为0、Top Origin为60,文本字体为粗体;
6. 将一个选择触发器(selector triggers)拖到窗体上。设置Object Identifier为Date,Left Origin为81,Top Origin 为60,Width为78。向标签输入10个空格,这样可保证在缺省的情况下,当被finger按下时有充足的空间;
7. 向窗体上再拖一个标签。置标签文本为Next Call Time。将它放在Next Call Date 标签底下。设置Left Origin为12、Top Origin为80,文本字体为粗体;
8. 向窗体上再拖一个选择触发器(selector triggers)。设置Object Identifier为Time,Left Origin为81,Top Origin 为80,Width为78。也向标签输入10个空格。
9. 添加控件后的窗体如图6-1所示。按下右上角的X按钮,关闭窗体。
图6-1:Contact Detail 窗体
创建一个新的设置时间窗体
现在创建一个窗体:
1. 点击资源(resource)中的窗体(Forms)选项,按下CTRL-K创建一个新的窗体;
2. 点击name框并重命名为Enter Time;
3. 双击打开窗体进行编辑;
4. 首先设置窗体属性。复选中属性Modal和Save Behind。我们将此窗体作为对话框的形式出现;此窗体是我们接触到的第一个不是全屏显示的窗体;我们修改其宽度(Width)为156,其高度(Height)为53;
5. 为实现modal边框可见,我们需要在窗体和屏幕边界留出2个象素的宽度。所以宽度设为156,而左初始边界应为2,顶端初始边界应为105,这样就保证了两个象素的余度;
6. 修改窗体的名字(Name)属性为Enter Time。
添加开关按钮(push buttons)
开关按钮(push buttons)在表现为按下的状态时,不同于通常淡的底色和黑的文字,而是黑的底色和淡的文字。我们使用开关按钮(push buttons)显示小时、分钟、和上午/下午(AM/PM)。在放置开关按钮(push buttons)时,我们必须考虑它的边界所占用的一个象素的宽度。开关按钮(push buttons)的属性如表6-2所示:
Object Identifier 资源构造器用来代表资源头文件ID的常量
Push Button ID 开关按钮(push buttons)的资源ID
Left Origin 水平方向上按钮的最左端位置;
Top Origin 垂直方向上按钮的最顶端位置;
Width 按钮的宽度
Height 按钮的高度
Usable 用来定义控件是否可见及可用,如果没有选中,也可在通过函数调用来实现其可见
Group ID 表示当按钮按下时是否突出 显示。如果此数字为0,则按钮当被按下是将在“按下”和“没有按下”两个状态间切换。当此数字不为0时,按钮被按下是保持原来的状态。在每一组中所用的组(Group)ID应是唯一的,因为在后面的代码中将使用到组ID
Font 标签显示文字的字体
Label 标签本身的文字
添加步骤:
1. 将一个开关按钮(push buttons)拖到Enter Time 窗体上;
2. 既然此按钮显示时间,可将Object Identifier设置为Hours。设置属性:Left Origin=5,Top Origin=17,Width为18,Group ID为1。Font属性为Bold,清除标签内的文字;
3. 复制Hours开关按钮(push buttons)。可选中Hours开关按钮(push buttons)后,按下CTRL-D进行复制。修改Object Identifier为MinutesTens。设置属性:Left Origin=34,Top Origin=17,Width为12,因为它只包含一个数字;
4. 复制MinutesTens按钮。可选中MinutesTens按钮后,按下CTRL-D进行复制。修改Object Identifier设置为MinutesOnes。设置属性:Left Origin=50,Top Origin=17;
5. 再拖动一个开关按钮(push buttons)到窗体上。修改Object Identifier设置为AM。设置属性:Left Origin=109,Top Origin=17,Width为20,Group ID为2。设置标签内的文字为AM;
6. 复制AM开关按钮(push buttons)。修改Object Identifier为PM。设置属性:Left Origin=130,Top Origin=17,注意AM 按钮和PM按钮重叠是为了使他们之间只有一个象素的间隔,这就是相关的开关按钮(push buttons)如何归为一组的方法。设置标签内的文字为PM;
名字 描述
Object Identifier 构造器用来代表资源头文件ID的常量。
Push Button ID 开关按钮(push buttons)标志号。
Left Origin 开关按钮(push buttons)的左边缘的水平起始位置。
Top Origin 开关按钮(push buttons)的顶边缘的垂直起始位置。
Width 开关按钮(push buttons)的宽度。
Height 开关按钮(push buttons)的高度。
Usable 该参数定义该控件是否可视和被激活。如该参数未被选择,你可通过一函数调用使该控件可视和被激活。
Group ID 该参数影响开关按钮(push buttons)被选时是否仍保持加亮状态。如该参数为0,开关按钮(push buttons)被按下时,其状态在“on”和“off”之间切换。如果你要在程序中使用组号,则该组号应为唯一。
Font 标签显示文字的字体
Label 标签本身的文字
向设置时间窗体添加重复按钮(repeating buttons)
如果输入笔按在Repeating按钮上的时间超过半秒,该按钮将连续发ctlRepeatEvent事件。头半秒之后,ctlRepeatEvent事件每十分之一秒发一次。我们使用重复按钮(repeating buttons)来构造Up和down箭头以调整时间。如同一般的按钮一样,重复按钮(repeating buttons)可有一边框。在我们的事例中,这些按钮不使用边框,因此,它们像标签和文本框一样排列。
重复按钮(repeating buttons)的属性如下表:
名字 描述
Object Identifier 构造器用来代表资源头文件ID的常量。
Button ID 重复按钮(repeating buttons)标志号。
Left Origin 按钮的左边缘的水平起始位置。
Top Origin 按钮的顶边缘的垂直起始位置。
Width 按钮的宽度。
Height 按钮的高度。
Usable 该参数定义该控件是否可视和被激活。如该参数未被选择,你可通过一函数调用使该控件可视和被激活。
Anchor Left 如该特性被选择,按钮被程序重定大小时向右扩张。
Frame 如该特性被选择,按钮将有一边框。
Non-Bold Frame 如该特性被选择,按钮边框粗细为1像素。
Font 标签显示文字的字体
Label 标签本身的文字
向设置时间窗体添加一个Repeating按钮
1. 将一个Repeating按钮拖放至设置时间窗体。将Object Identifier改为TimeUp。参考位置为Left Origion = 109, Top Origion = 17。参考大小为Width=20,Height =8。不选Frame特性。
2. 选字体为Symble 7,在label上选Hex box。键入01。这样,你在form上看到的是空白标签,而运行代码后将显示一个向上的箭头。不要被hex 21所迷惑。当你运行代码时,hex 21显示为一个复选框。
3. 通过选择Repeating按钮并按CTL-D来复制该按钮。将将Object Identifier改为TimeDown。此按钮的位置应为Left Origion = 69, Top Origion = 25。将label设置为hex 02,则程序运行时显示一个向下的箭头。
向设置时间窗体添加一个复选框
复选框的左端有一个小框可被选择以指示某些事。在设置时间窗体中,该小框可让用户选择不输入时间。复选框没有边框,所以它们的排列如同一个域一样。复选框的属性如下表所示:
名字 描述
Object Identifier 资源构造器用来代表资源头文件ID的常量。
Checkbox ID 复选框标志号。
Left Origin 复选框的左边缘的水平起始位置。
Top Origin 复选框的顶边缘的垂直起始位置。
Width 复选框的宽度。
Height 复选框的高度。
Usable 该参数定义该控件是否可视和被激活。如该参数未被选中,你可通过一函数调用使该控件可视和被激活。
Selected 如该参数被选择,则复选框被画时缺省为被选。
Group ID 该参数影响复选框被选时是否仍保持加亮状态。如该参数为0,复选框被按下时,其状态在被选和未选之间切换。如果你要在程序中使用组号,则该组号应为唯一。
Font 标签显示文字的字体。
Label 标签本身的文字
现在向设置时间窗体添加一个复选框:
1. 从Catalog窗拖放一个复选框到设置时间窗体。
2. 将Object Identifier属性设置为NoTime。参考位置为Left Origion = 53, Top Origion = 37。设Width为50。选Selected。设Group ID为0因为并未成组。设Label为NoTime。
完善设置时间窗体
现在让我们来添加一些熟悉的控件:
1. 小时和分钟之间用冒号隔开会更好看。从Catalog窗体拖放一个label控件到设置时间窗体中。位置为Left Origion = 27, Top Origion = 17。设字体为Bold。加一个冒号。
2. 每一个对话框都需要一个OK按钮。从Catalog窗体拖放一个button控件到设置时间窗体中。位置为Left Origion = 5, Top Origion = 37。注意Left Origion设为5使按钮与其上的push按钮对齐,并在左边缘留出4象素的空间。
3. 有一个Cancel按钮也会很好。拖放一个button控件到窗体中。改设Object Identifier为Cancel。其位置为Left Origion = 115, Top Origion = 37。改设Label为Cancel。
4. 使Cancel按钮为此窗体的缺省按钮。记下Cancel按钮的Button ID。点击窗体背景的任意处以显示设置时间窗体的属性表。将窗体缺省按钮的ID设为Cancel按钮的Button ID。
你已经完成了设置时间窗体的构建。你的窗体应该如下图所示。点击右上角的X来关闭窗口。选File | Save来保存你的改变。
向Contacts.c添加代码
现在,为了在数据库、控件以及我们才添加的新窗体中支持日期和时间,我们向Contacts.c添加代码。
在数据库中初始化和保存日期和时间
为了在内部和数据库中保存和定义日期和时间,你需要一些变量和常量的定义。
// CH.6 Storage for the record's date and time in expanded form
static DateTimeType dateTime;
static Word timeSelect;
#define NO_DATE 0
#define NO_TIME 0x7fff
变量dateTime保存目前正被处理的记录的日期和时间。设置时间窗体使用timeSelect变量完成同样功能。常量NO_TIME和NO_DATAE同样使用dataTime来表示没有日期或没有时间或二者兼备。
在newRecord()功能中,加入代码设date和time的初始状态为没有日期和没有时间。
// CH.6 Initialize the date and time
MemSet( &dateTime, sizeof( dateTime ), 0 );
dateTime.year = NO_DATE;
dateTime.hour = NO_TIME;
DmWrite( precord, DB_DATE_TIME_START, &dateTime,
sizeof( DateTimeType ) );
注意我们使用MemSet()将整个记录清零。如果我们不这样做,记录中的域将有垃圾并且域功能将崩溃,因为我们并没有如域功能希望的那样发送以零定界的字符串。然后,我们把变量dateTime作为临时变量来初始化该记录。
图6-2:EnterTime 窗体
在setFields()功能中,从记录中载入dateTime变量的值。
// CH.6 Initialize the date and time variable
precord = MemHandleLock( hrecord );
MemMove( &dateTime, precord + DB_DATE_TIME_START,
sizeof( dateTime ) );
并且,设置日期和时间(selector triggers)的外观。我们将在讨论选择触发器时详细研究这些功能。
// CH.6 Initialize the date control
setDateTrigger();
// CH.6 Initialize the time control
setTimeTrigger();
支持日期和时间选择按钮
加入的第一行代码应是Contact Detail窗体事件处理句柄,对日期选择按钮来说,然后再添加向处理普通按钮那样事件处理过程。
// CH.6 Date selector trigger
case ContactDetailDateSelTrigger:
{
// CH.6 Initialize the date if necessary
if( dateTime.year == NO_DATE )
{
DateTimeType currentDate;
// CH.6 Get the current date
TimSecondsToDateTime( TimGetSeconds(),
¤tDate );
// CH.6 Copy it
dateTime.year = currentDate.year;
dateTime.month = currentDate.month;
dateTime.day = currentDate.day;
}
// CH.6 Pop up the system date selection form
SelectDay( selectDayByDay, &(dateTime.month),
&(dateTime.day), &(dateTime.year),
"Enter Date" );
// CH.6 Get the record
hrecord = DmQueryRecord( contactsDB, cursor );
// CH.6 Lock it down
precord = MemHandleLock( hrecord );
// CH.6 Write the date time field
DmWrite( precord, DB_DATE_TIME_START, &dateTime,
sizeof( DateTimeType ) );
// CH.6 Unlock the record
MemHandleUnlock( hrecord );
// CH.6 Mark the record dirty
isDirty = true;
}
break;
如果以前没有填入时间,把时间设为当前时间。获取当前时间是通过调用函数TimGetSeconds()并通过函数
TimSeconsToDateTime()将其输出转换为日期。这些函数是时间处理器的一部分,在Palm OS的Reference.pdf文件中有详细的描述。
时间初始化完成后,我们调用函数SelectDay()以产生Palm OS内置控件来选择日期。当选定时间后,我们锁定数据库当前记录,将新的时间值写入。
选择时间有些不一样。因为Palm OS中没有可以直接选择时间的控件,那么我们弹出Enter Time 窗体来和用户交互。
// CH.6 Time selector trigger
case ContactDetailTimeSelTrigger:
{
// CH.6 Pop up our selection form
FrmPopupForm( EnterTimeForm );
}
break;
在Contact Detail事件处理过程中我们所做的就是调出Enter Time 窗体。修改数据库的工作就由后者来处理。
函数setDateTrigger()用来更新日期选择按钮的外观,代码如下:
// CH.6 Set the Contact Detail date selector trigger
static void setDateTrigger( void )
{
FormPtr form; // CH.5 The contact detail form
// CH.6 Get the contact detail form pointer
form = FrmGetActiveForm();
// CH.6 If there is no date
if( dateTime.year == NO_DATE )
{
CtlSetLabel( getObject( form, ContactDetailDateSelTrigger ),
" " );
}
else
// CH.6 If there is a date
{
Char dateString[dateStringLength];
// CH.6 Get the date string
DateToAscii( dateTime.month, dateTime.day, dateTime.year,
(DateFormatType)PrefGetPreference( prefDateFormat ), dateString );
// CH.6 Set the selector trigger label
CtlSetLabel( getObject( form, ContactDetailDateSelTrigger ),
dateString );
}
// CH.6 We're done
return;
}
如果没有时间,我们控件中写入10个空格。由于选择触发按钮能够根据标签自动调整大小,这样就可保证触发按钮足够大可用手指来选择。记住一定要保证控件标签(包括触发按钮)中的文本大小不要超过最初在构造器中定义的数量。
如果已有时间,那我们返回时间。系统内的优先权将决定我们使用什么格式来显示时间。我们调用带参数preDateFormat的函数PrefGetPreference()来获取日期的优先权。我们选择类型DateFormat的原因是preGetPreference()能够返回许多不同的优先权,我们选最普遍的一种。
函数DateToAscii()用来将日期变量转换为我们定义的短时间格式,这是由我们从系统中获得的。当我们获得时间后,我们就将其写入触发按钮标签内。
函数SetTimeTrigger()用来设置时间触发按钮。它和setDateTrigger()很相似,除了用它自己相应的函数外。
支持开关按钮(push buttons)
像在Contact Details所做的那样,我们也将建立switch语句来处理ctlSelectEvent。Case语句建立在不同button ID值上,它可用真正的控件ID来表示。
首先来处理小时和分钟开关按钮(push buttons):
// CH.6 Hours button
case EnterTimeHoursPushButton:
// CH.6 Minute Tens button
case EnterTimeMinuteTensPushButton:
// CH.6 Minute Ones button
case EnterTimeMinuteOnesPushButton:
{
// CH.6 If no time was set
if( dateTime.hour == NO_TIME )
{
// CH.6 Set the time to 12 PM
dateTime.hour = 12;
dateTime.minute = 0;
// CH.6 Set the controls
setTimeControls();
}
// CH.6 Clear the old selection if any
if( timeSelect )
CtlSetValue( getObject( form, timeSelect ),
false );
// CH.6 Set the new selection
CtlSetValue( getObject( form, buttonID ), true );
timeSelect = buttonID;
}
break;
按钮将在它们的标签上显示各自的值。哪一个值和上或下箭头相关联是根据哪一个被选中决定的。如果当按钮被选中后但没有时间显示,12PM将被设置并显示。如果先前已选择了一个按钮,那么我们先清除这个选择,然后选中我们刚刚点击的那个按钮。
在函数setTimeControl()中,我们看它是如何设置标签文本的。实际上,它和其他任何控件处理标签一样。
// CH.6 Update the hour
hour = dateTime.hour % 12;
if( hour == 0 )
hour = 12;
CtlSetLabel( hourButton,
StrIToA( labelString, hour ) );
// CH.6 Update the minute tens
CtlSetLabel( minuteTensButton,
StrIToA( labelString, dateTime.minute / 10 ) );
// CH.6 Update the minute ones
CtlSetLabel( minuteOnesButton,
StrIToA( labelString, dateTime.minute % 10 ) );
时间是以24小时格式显示,我们把它转换为12小时格式并在按钮上显示。
在函数enterTimeHandleEvent()中也有两个Push按钮,用来进行AM/PM设置。它们的代码如下:
// CH.6 AM button
case EnterTimeAMPushButton:
{
// CH.6 If no time was set
if( dateTime.hour == NO_TIME )
{
// CH.6 Set the time to 12 AM
dateTime.hour = 0;
dateTime.minute = 0;
// CH.6 Set the controls
setTimeControls();
}
// CH.6 If it is PM
if( dateTime.hour > 11 )
{
// CH.6 Change to AM
dateTime.hour -= 12;
// CH.6 Set the controls
setTimeControls();
}
}
break;
在AM case语句中,如果没有时间,那么我们设置时间为12 AM;如果时间是PM,那么我们从24小时格式中减去12而变为AM。
PM按钮处理和AM差不多。
在setTimeControls()中显示AM和PM,代码如下:
// CH.6 Update AM
CtlSetValue( amButton, (dateTime.hour < 12) );
// CH.6 Update PM
CtlSetValue( pmButton, (dateTime.hour > 11) );
在需要布尔值的地方根据其逻辑变换布尔值是个很有意思的事。
支持重复按钮(repeating buttons)
Repeating按钮允许时间被增加或减少。为了使其有效,我们必须像处理ctlSelectEvent那样处理ctlRepeatEvent。在函数enterTimeHandleEvent()我们必须也要从事件句柄那里返回false,否则就不能产生repeating 事件。
// CH.6 Up button
case EnterTimeTimeUpRepeating:
{
// CH.6 If there's no time, do nothing
if( dateTime.hour == NO_TIME )
break;
// CH.6 Based on what push button is selected
switch( timeSelect )
{
// CH.6 Increase hours
case EnterTimeHoursPushButton:
{
// CH.6 Increment hours
dateTime.hour++;
// CH.6 If it was 11 AM, make it 12 AM
if( dateTime.hour == 12 )
dateTime.hour = 0;
// CH.6 If it was 11 PM, make it 12 PM
if( dateTime.hour == 24 )
dateTime.hour = 12;
}
break;
// CH.6 Increase tens of minutes
case EnterTimeMinuteTensPushButton:
{
// CH.6 Increment minutes
dateTime.minute += 10;
// CH.6 If it was 5X, roll over
if( dateTime.minute > 59 )
dateTime.minute -= 60;
}
break;
// CH.6 Increase minutes
case EnterTimeMinuteOnesPushButton:
{
// CH.6 Increment minutes
dateTime.minute++;
// CH.6 If it is zero, subtract ten
if( (dateTime.minute % 10) == 0 )
dateTime.minute -= 10;
}
break;
}
// Revise the controls
setTimeControls();
}
break;
如果没有时间显示,上下箭头不做任何事情。有时间显示时,根据所选择的开关按钮(push buttons),重复按钮(repeating buttons)将增加小时、分钟的十位、分钟的个位。其中也处理了必要的循环情况。
减少按钮和增加按钮基本相似。
支持复选框
复选框的处理和其它按钮一样。当被触发时,产生一个ctlSelectEvent。
// CH.6 No Time checkbox
case EnterTimeNoTimeCheckbox:
{
// CH.6 If we are unchecking the box
if( dateTime.hour == NO_TIME )
{
// CH.6 Set the time to 12 PM
dateTime.hour = 12;
dateTime.minute = 0;
// CH.6 Set the controls
setTimeControls();
// CH.6 Set the new selection
timeSelect = EnterTimeHoursPushButton;
CtlSetValue( getObject( form, timeSelect ),
true );
}
else
// CH.6 If we are checking the box
dateTime.hour = NO_TIME;
// CH.6 Set the controls
setTimeControls();
}
break;
为方便起见,如果没选复选框,我们将选中小时Push按钮,然后其它的事由函数setTimeControl()来处理。当选中复选框时将清空所有的控件内容。
结束Enter Time窗体
还有一些其它的窗口处理事件需要讨论。在enterTimeHandleEvent()中处理frmOpenEvent。
// CH.6 Initialize the form
case frmOpenEvent:
{
//CH.6 Store the time value
oldTime = dateTime;
// CH.6 Draw it
FrmDrawForm( form );
// CH.6 Set the time controls
setTimeControls();
}
break;
当打开窗体时,我们用函数setTimeControls()初始化窗体。我们也保存了当前时间以便按下了cancel能够恢复。
// CH.6 Cancel button
case EnterTimeCancelButton:
{
// CH.6 Restore time
dateTime = oldTime;
// CH.6 Return to calling form
FrmReturnToForm( 0 );
}
// CH.6 Always return true
return( true );
Cancel按钮事件储存了老的时间,并重新返回给Contact Detail窗体.
Ok按钮有些复杂。我们必须根据新时间相应的更新数据库和Contact Detail窗体。
在这里有几个重点。这个代码块表明了为什么变量hrecord在函数中是公用的。在这里由于hrecord是有效的,所有我们就用它来将新的时间值写入数据库中。
注意到在函数FrmReturnToForm()后调用的setTimeTrigger(),它用来触发Contact Details窗体的时间选择触发按钮。能够实现触发的原因是因为当执行FrmReturnToForm()后,活动窗体就变为了Contact Details窗体。这就使弹出窗体的数据能够顺利的传递到调用窗体上。
通常情况下,在调用FrmReturnToForm()后返回true,这是因为调用后老的窗体结构已经消失。然而如果返回了false,Palm OS就试图访问按钮结构以做更多的素材。由于窗口已经不在了,这样就会使程序崩溃。
调试
首先,你应该保证数据库记录能被正确的创建和修改。如果顺利的话,你所加的显示函数能够正确的显示所得结果。你的已有记录表现为no date,在界面上会显示12 AM,但不会造成危险。
下一步操作Contact Detail窗体上的日期触发按钮,看是否能按设计程序正常工作。对日期内置控件来说,这应比较容易。
对于时间控制,你需要调试Enter Time窗体。仔细的调试各个控件直到都能够可靠的工作。当窗体能够顺利运行后,你可验证数据库记录和时间选择触发按钮是否被正确的修改。
下一步
下一步,我们将向Contacts应用程序添加一个列表框窗体。然后修改代码使我们能够根据first name、last name、date 和time进行排序。
程序清单
下面是完整的Contacts.c程序清单。
// CH.2 The super-include for the Palm OS
#include <Pilot.h>
// CH.5 Added for the call to GrfSetState()
#include <Graffiti.h>
// CH.3 Our resource file
#include "Contacts_res.h"
// CH.4 Prototypes for our event handler functions
static Boolean contactDetailHandleEvent( EventPtr event );
static Boolean aboutHandleEvent( EventPtr event );
static Boolean enterTimeHandleEvent( EventPtr event );
static Boolean menuEventHandler( EventPtr event );
// CH.4 Constants for ROM revision
#define ROM_VERSION_2 0x02003000
#define ROM_VERSION_MIN ROM_VERSION_2
// CH.5 Prototypes for utility functions
static void newRecord( void );
static VoidPtr getObject( FormPtr, Word );
static void setFields( void );
static void getFields( void );
static void setText( FieldPtr, CharPtr );
static void getText( FieldPtr, VoidPtr, Word );
static void setDateTrigger( void );
static void setTimeTrigger( void );
static void setTimeControls( void );
// CH.5 Our open database reference
static DmOpenRef contactsDB;
static ULong numRecords;
static UInt cursor;
static Boolean isDirty;
static VoidHand hrecord;
// CH.5 Constants that define the database record
#define DB_ID_START 0
#define DB_ID_SIZE (sizeof( ULong ))
#define DB_DATE_TIME_START (DB_ID_START +\
DB_ID_SIZE)
#define DB_DATE_TIME_SIZE (sizeof( DateTimeType ))
#define DB_FIRST_NAME_START (DB_DATE_TIME_START +\
DB_DATE_TIME_SIZE)
#define DB_FIRST_NAME_SIZE 16
#define DB_LAST_NAME_START (DB_FIRST_NAME_START +\
DB_FIRST_NAME_SIZE)
#define DB_LAST_NAME_SIZE 16
#define DB_PHONE_NUMBER_START (DB_LAST_NAME_START +\
DB_LAST_NAME_SIZE)
#define DB_PHONE_NUMBER_SIZE 16
#define DB_RECORD_SIZE (DB_PHONE_NUMBER_START +\
DB_PHONE_NUMBER_SIZE)
// CH.6 Storage for the record's date and time in expanded form
static DateTimeType dateTime;
static Word timeSelect;
#define NO_DATE 0
#define NO_TIME 0x7fff
// CH.2 The main entry point
DWord PilotMain( Word cmd, Ptr, Word )
{
DWord romVersion; // CH.4 ROM version
FormPtr form; // CH.2 A pointer to our form structure
EventType event; // CH.2 Our event structure
Word error; // CH.3 Error word
// CH.4 Get the ROM version
romVersion = 0;
FtrGet( sysFtrCreator, sysFtrNumROMVersion, &romVersion );
// CH.4 If we are below our minimum acceptable ROM revision
if( romVersion < ROM_VERSION_MIN )
{
// CH.4 Display the alert
FrmAlert( LowROMVersionErrorAlert );
// CH.4 PalmOS 1.0 will continuously re-launch this app
// unless we switch to another safe one
if( romVersion < ROM_VERSION_2 )
{
AppLaunchWithCommand( sysFileCDefaultApp,
sysAppLaunchCmdNormalLaunch, NULL );
}
return( 0 );
}
// CH.2 If this is not a normal launch, don't launch
if( cmd != sysAppLaunchCmdNormalLaunch )
return( 0 );
// CH.5 Create a new database in case there isn't one
if( ((error = DmCreateDatabase( 0, "ContactsDB-PPGU", 'PPGU', 'ctct',
false )) != dmErrAlreadyExists) && (error != 0) )
{
// CH.5 Handle db creation error
FrmAlert( DBCreationErrorAlert );
return( 0 );
}
// CH.5 Open the database
contactsDB = DmOpenDatabaseByTypeCreator( 'ctct', 'PPGU',
dmModeReadWrite );
// CH.5 Get the number of records in the database
numRecords = DmNumRecords( contactsDB );
// CH.5 Initialize the record number
cursor = 0;
// CH.5 If there are no records, create one
if( numRecords == 0 )
newRecord();
// CH.4 Go to our starting page
FrmGotoForm( ContactDetailForm );
// CH.2 Our event loop
do
{
// CH.2 Get the next event
EvtGetEvent( &event, -1 );
// CH.2 Handle system events
if( SysHandleEvent( &event ) )
continue;
// CH.3 Handle menu events
if( MenuHandleEvent( NULL, &event, &error ) )
continue;
// CH.4 Handle form load events
if( event.eType == frmLoadEvent )
{
// CH.4 Initialize our form
switch( event.data.frmLoad.formID )
{
// CH.4 Contact Detail form
case ContactDetailForm:
form = FrmInitForm( ContactDetailForm );
FrmSetEventHandler( form, contactDetailHandleEvent );
break;
// CH.4 About form
case AboutForm:
form = FrmInitForm( AboutForm );
FrmSetEventHandler( form, aboutHandleEvent );
break;
// CH.6 Enter Time form
case EnterTimeForm:
form = FrmInitForm( EnterTimeForm );
FrmSetEventHandler( form, enterTimeHandleEvent );
break;
}
FrmSetActiveForm( form );
}
// CH.2 Handle form events
FrmDispatchEvent( &event );
// CH.2 If it's a stop event, exit
} while( event.eType != appStopEvent );
// CH.5 Close all open forms
FrmCloseAllForms();
// CH.5 Close the database
DmCloseDatabase( contactsDB );
// CH.2 We're done
return( 0 );
}
// CH.4 Our Contact Detail form handler function
static Boolean contactDetailHandleEvent( EventPtr event )
{
FormPtr form; // CH.3 A pointer to our form structure
VoidPtr precord; // CH.6 Points to a database record
// CH.3 Get our form pointer
form = FrmGetActiveForm();
// CH.4 Parse events
switch( event->eType )
{
// CH.4 Form open event
case frmOpenEvent:
{
// CH.2 Draw the form
FrmDrawForm( form );
// CH.5 Draw the database fields
setFields();
}
break;
// CH.5 Form close event
case frmCloseEvent:
{
// CH.5 Store away any modified fields
getFields();
}
break;
// CH.5 Parse the button events
case ctlSelectEvent:
{
// CH.5 Store any field changes
getFields();
switch( event->data.ctlSelect.controlID )
{
// CH.5 First button
case ContactDetailFirstButton:
{
// CH.5 Set the cursor to the first record
if( cursor > 0 )
cursor = 0;
}
break;
// CH.5 Previous button
case ContactDetailPrevButton:
{
// CH.5 Move the cursor back one record
if( cursor > 0 )
cursor--;
}
break;
// CH.5 Next button
case ContactDetailNextButton:
{
// CH.5 Move the cursor up one record
if( cursor < (numRecords - 1) )
cursor++;
}
break;
// CH.5 Last button
case ContactDetailLastButton:
{
// CH.5 Move the cursor to the last record
if( cursor < (numRecords - 1) )
cursor = numRecords - 1;
}
break;
// CH.5 Delete button
case ContactDetailDeleteButton:
{
// CH.5 Remove the record from the database
DmRemoveRecord( contactsDB, cursor );
// CH.5 Decrease the number of records
numRecords--;
// CH.5 Place the cursor at the first record
cursor = 0;
// CH.5 If there are no records left, create one
if( numRecords == 0 )
newRecord();
}
break;
// CH.5 New button
case ContactDetailNewButton:
{
// CH.5 Create a new record
newRecord();
}
break;
// CH.6 Date selector trigger
case ContactDetailDateSelTrigger:
{
// CH.6 Initialize the date if necessary
if( dateTime.year == NO_DATE )
{
DateTimeType currentDate;
// CH.6 Get the current date
TimSecondsToDateTime( TimGetSeconds(),
¤tDate );
// CH.6 Copy it
dateTime.year = currentDate.year;
dateTime.month = currentDate.month;
dateTime.day = currentDate.day;
}
// CH.6 Pop up the system date selection form
SelectDay( selectDayByDay, &(dateTime.month),
&(dateTime.day), &(dateTime.year),
"Enter Date" );
// CH.6 Get the record
hrecord = DmQueryRecord( contactsDB, cursor );
// CH.6 Lock it down
precord = MemHandleLock( hrecord );
// CH.6 Write the date time field
DmWrite( precord, DB_DATE_TIME_START, &dateTime,
sizeof( DateTimeType ) );
// CH.6 Unlock the record
MemHandleUnlock( hrecord );
// CH.6 Mark the record dirty
isDirty = true;
}
break;
// CH.6 Time selector trigger
case ContactDetailTimeSelTrigger:
{
// CH.6 Pop up our selection form
FrmPopupForm( EnterTimeForm );
}
break;
}
// CH.5 Sync the current record to the fields
setFields();
}
break;
// CH.5 Respond to field tap
case fldEnterEvent:
isDirty = true;
break;
// CH.3 Parse menu events
case menuEvent:
return( menuEventHandler( event ) );
break;
}
// CH.2 We're done
return( false );
}
// CH.4 Our About form event handler function
static Boolean aboutHandleEvent( EventPtr event )
{
FormPtr form; // CH.4 A pointer to our form structure
// CH.4 Get our form pointer
form = FrmGetActiveForm();
// CH.4 Respond to the Open event
if( event->eType == frmOpenEvent )
{
// CH.4 Draw the form
FrmDrawForm( form );
}
// CH.4 Return to the calling form
if( event->eType == ctlSelectEvent )
{
FrmReturnToForm( 0 );
// CH.4 Always return true in this case
return( true );
}
// CH.4 We're done
return( false );
}
// CH.6 Our Enter Time form event handler function
static Boolean enterTimeHandleEvent( EventPtr event )
{
FormPtr form; // CH.6 A form structure pointer
static DateTimeType oldTime; // CH.6 The original time
// CH.6 Get our form pointer
form = FrmGetActiveForm();
// CH.6 Switch on the event
switch( event->eType )
{
// CH.6 Initialize the form
case frmOpenEvent:
{
// CH.6 Store the time value
oldTime = dateTime;
// CH.6 Draw it
FrmDrawForm( form );
// CH.6 Set the time controls
setTimeControls();
}
break;
// CH.6 If a button was repeated
case ctlRepeatEvent:
// CH.6 If a button was pushed
case ctlSelectEvent:
{
Word buttonID; // CH.6 The ID of the button
// CH.6 Set the ID
buttonID = event->data.ctlSelect.controlID;
// CH.6 Switch on button ID
switch( buttonID )
{
// CH.6 Hours button
case EnterTimeHoursPushButton:
// CH.6 Minute Tens button
case EnterTimeMinuteTensPushButton:
// CH.6 Minute Ones button
case EnterTimeMinuteOnesPushButton:
{
// CH.6 If no time was set
if( dateTime.hour == NO_TIME )
{
// CH.6 Set the time to 12 PM
dateTime.hour = 12;
dateTime.minute = 0;
// CH.6 Set the controls
setTimeControls();
}
// CH.6 Clear the old selection if any
if( timeSelect )
CtlSetValue( getObject( form, timeSelect ),
false );
// CH.6 Set the new selection
CtlSetValue( getObject( form, buttonID ), true );
timeSelect = buttonID;
}
break;
// CH.6 Up button
case EnterTimeTimeUpRepeating:
{
// CH.6 If there's no time, do nothing
if( dateTime.hour == NO_TIME )
break;
// CH.6 Based on what push button is selected
switch( timeSelect )
{
// CH.6 Increase hours
case EnterTimeHoursPushButton:
{
// CH.6 Increment hours
dateTime.hour++;
// CH.6 If it was 11 AM, make it 12 AM
if( dateTime.hour == 12 )
dateTime.hour = 0;
// CH.6 If it was 11 PM, make it 12 PM
if( dateTime.hour == 24 )
dateTime.hour = 12;
}
break;
// CH.6 Increase tens of minutes
case EnterTimeMinuteTensPushButton:
{
// CH.6 Increment minutes
dateTime.minute += 10;
// CH.6 If it was 5X, roll over
if( dateTime.minute > 59 )
dateTime.minute -= 60;
}
break;
// CH.6 Increase minutes
case EnterTimeMinuteOnesPushButton:
{
// CH.6 Increment minutes
dateTime.minute++;
// CH.6 If it is zero, subtract ten
if( (dateTime.minute % 10) == 0 )
dateTime.minute -= 10;
}
break;
}
// Revise the controls
setTimeControls();
}
break;
// CH.6 Down button
case EnterTimeTimeDownRepeating:
{
// CH.6 If there's no time, do nothing
if( dateTime.hour == NO_TIME )
break;
// CH.6 Based on what push button is selected
switch( timeSelect )
{
// CH.6 Decrease hours
case EnterTimeHoursPushButton:
{
// CH.6 Decrement hours
dateTime.hour--;
// CH.6 If it was 12 AM, make it 11 AM
if( dateTime.hour == -1 )
dateTime.hour = 11;
// CH.6 If it was 12 PM, make it 11 PM
if( dateTime.hour == 11 )
dateTime.hour = 23;
}
break;
// CH.6 Decrease tens of minutes
case EnterTimeMinuteTensPushButton:
{
// CH.6 Decrement minutes
dateTime.minute -= 10;
// CH.6 If it was 0X, roll over
if( dateTime.minute < 0 )
dateTime.minute += 60;
}
break;
// CH.6 Decrease minutes
case EnterTimeMinuteOnesPushButton:
{
// CH.6 Decrement minutes
dateTime.minute--;
// CH.6 If it is 9, add ten
if( (dateTime.minute % 10) == 9 )
dateTime.minute += 10;
// CH.6 If less than zero, make it 9
if( dateTime.minute < 0 )
dateTime.minute = 9;
}
break;
}
// CH.6 Revise the controls
setTimeControls();
}
break;
// CH.6 AM button
case EnterTimeAMPushButton:
{
// CH.6 If no time was set
if( dateTime.hour == NO_TIME )
{
// CH.6 Set the time to 12 AM
dateTime.hour = 0;
dateTime.minute = 0;
// CH.6 Set the controls
setTimeControls();
}
// CH.6 If it is PM
if( dateTime.hour > 11 )
{
// CH.6 Change to AM
dateTime.hour -= 12;
// CH.6 Set the controls
setTimeControls();
}
}
break;
// CH.6 PM button
case EnterTimePMPushButton:
{
// CH.6 If no time was set
if( dateTime.hour == NO_TIME )
{
// CH.6 Set the time to 12 PM
dateTime.hour = 12;
dateTime.minute = 0;
// CH.6 Set the controls
setTimeControls();
}
// CH.6 If it is AM
if( dateTime.hour < 12 )
{
// CH.6 Change to PM
dateTime.hour += 12;
// CH.6 Set the controls
setTimeControls();
}
}
break;
// CH.6 No Time checkbox
case EnterTimeNoTimeCheckbox:
{
// CH.6 If we are unchecking the box
if( dateTime.hour == NO_TIME )
{
// CH.6 Set the time to 12 PM
dateTime.hour = 12;
dateTime.minute = 0;
// CH.6 Set the controls
setTimeControls();
// CH.6 Set the new selection
timeSelect = EnterTimeHoursPushButton;
CtlSetValue( getObject( form, timeSelect ),
true );
}
else
// CH.6 If we are checking the box
dateTime.hour = NO_TIME;
// CH.6 Set the controls
setTimeControls();
}
break;
// CH.6 Cancel button
case EnterTimeCancelButton:
{
// CH.6 Restore time
dateTime = oldTime;
// CH.6 Return to calling form
FrmReturnToForm( 0 );
}
// CH.6 Always return true
return( true );
// CH.6 OK button
case EnterTimeOKButton:
{
VoidPtr precord; // CH.6 Points to the record
// CH.6 Lock it down
precord = MemHandleLock( hrecord );
// CH.6 Write the date time field
DmWrite( precord, DB_DATE_TIME_START, &dateTime,
sizeof( DateTimeType ) );
// CH.6 Unlock the record
MemHandleUnlock( hrecord );
// CH.6 Mark the record dirty
isDirty = true;
// CH.6 Return to the Contact Details form
FrmReturnToForm( 0 );
// CH.6 Update the field
setTimeTrigger();
}
// CH.6 Always return true
return( true );
}
}
break;
}
// CH.6 We're done
return( false );
}
// CH.3 Handle menu events
static Boolean menuEventHandler( EventPtr event )
{
FormPtr form; // CH.3 A pointer to our form structure
Word index; // CH.3 A general purpose control index
FieldPtr field; // CH.3 Used for manipulating fields
// CH.3 Get our form pointer
form = FrmGetActiveForm();
// CH.3 Erase the menu status from the display
MenuEraseStatus( NULL );
// CH.4 Handle options menu
if( event->data.menu.itemID == OptionsAboutContacts )
{
// CH.4 Pop up the About form as a Dialog
FrmPopupForm( AboutForm );
return( true );
}
// CH.3 Handle graffiti help
if( event->data.menu.itemID == EditGraffitiHelp )
{
// CH.3 Pop up the graffiti reference based on
// the graffiti state
SysGraffitiReferenceDialog( referenceDefault );
return( true );
}
// CH.3 Get the index of our field
index = FrmGetFocus( form );
// CH.3 If there is no field selected, we're done
if( index == noFocus )
return( false );
// CH.3 Get the pointer of our field
field = FrmGetObjectPtr( form, index );
// CH.3 Do the edit command
switch( event->data.menu.itemID )
{
// CH.3 Undo
case EditUndo:
FldUndo( field );
break;
// CH.3 Cut
case EditCut:
FldCut( field );
break;
// CH.3 Copy
case EditCopy:
FldCopy( field );
break;
// CH.3 Paste
case EditPaste:
FldPaste( field );
break;
// CH.3 Select All
case EditSelectAll:
{
// CH.3 Get the length of the string in the field
Word length = FldGetTextLength( field );
// CH.3 Sound an error if appropriate
if( length == 0 )
{
SndPlaySystemSound( sndError );
return( false );
}
// CH.3 Select the whole string
FldSetSelection( field, 0, length );
}
break;
// CH.3 Bring up the keyboard tool
case EditKeyboard:
SysKeyboardDialogV10();
break;
}
// CH.3 We're done
return( true );
}
// CH.5 This function creates and initializes a new record
static void newRecord( void )
{
VoidPtr precord; // CH.5 Pointer to the record
// CH.5 Create the database record and get a handle to it
hrecord = DmNewRecord( contactsDB, &cursor, DB_RECORD_SIZE );
// CH.5 Lock down the record to modify it
precord = MemHandleLock( hrecord );
// CH.5 Clear the record
DmSet( precord, 0, DB_RECORD_SIZE, 0 );
// CH.6 Initialize the date and time
MemSet( &dateTime, sizeof( dateTime ), 0 );
dateTime.year = NO_DATE;
dateTime.hour = NO_TIME;
DmWrite( precord, DB_DATE_TIME_START, &dateTime,
sizeof( DateTimeType ) );
// CH.5 Unlock the record
MemHandleUnlock( hrecord );
// CH.5 Clear the busy bit and set the dirty bit
DmReleaseRecord( contactsDB, cursor, true );
// CH.5 Increment the total record count
numRecords++;
// CH.5 Set the dirty bit
isDirty = true;
// CH.5 We're done
return;
}
// CH.5 A time saver: Gets object pointers based on their ID
static VoidPtr getObject( FormPtr form, Word objectID )
{
Word index; // CH.5 The object index
// CH.5 Get the index
index = FrmGetObjectIndex( form, objectID );
// CH.5 Return the pointer
return( FrmGetObjectPtr( form, index ) );
}
// CH.5 Gets the current database record and displays it
// in the detail fields
static void setFields( void )
{
FormPtr form; // CH.5 The contact detail form
CharPtr precord; // CH.5 A record pointer
Word index; // CH.5 The object index
// CH.5 Get the contact detail form pointer
form = FrmGetActiveForm();
// CH.5 Get the current record
hrecord = DmQueryRecord( contactsDB, cursor );
// CH.6 Initialize the date and time variable
precord = MemHandleLock( hrecord );
MemMove( &dateTime, precord + DB_DATE_TIME_START,
sizeof( dateTime ) );
// CH.6 Initialize the date control
setDateTrigger();
// CH.6 Initialize the time control
setTimeTrigger();
// CH.5 Set the text for the First Name field
setText( getObject( form, ContactDetailFirstNameField ),
precord + DB_FIRST_NAME_START );
// CH.5 Set the text for the Last Name field
setText( getObject( form, ContactDetailLastNameField ),
precord + DB_LAST_NAME_START );
// CH.5 Set the text for the Phone Number field
setText( getObject( form, ContactDetailPhoneNumberField ),
precord + DB_PHONE_NUMBER_START );
MemHandleUnlock( hrecord );
// CH.5 If the record is already dirty, it's new, so set focus
if( isDirty )
{
// CH.3 Get the index of our field
index = FrmGetObjectIndex( form, ContactDetailFirstNameField );
// CH.3 Set the focus to the First Name field
FrmSetFocus( form, index );
// CH.5 Set upper shift on
GrfSetState( false, false, true );
}
// CH.5 We're done
return;
}
// CH.5 Puts any field changes in the record
void getFields( void )
{
FormPtr form; // CH.5 The contact detail form
// CH.5 Get the contact detail form pointer
form = FrmGetActiveForm();
// CH.5 Turn off focus
FrmSetFocus( form, -1 );
// CH.5 If the record has been modified
if( isDirty )
{
CharPtr precord; // CH.5 Points to the DB record
// CH.5 Lock the record
precord = MemHandleLock( hrecord );
// CH.5 Get the text for the First Name field
getText( getObject( form, ContactDetailFirstNameField ),
precord, DB_FIRST_NAME_START );
// CH.5 Get the text for the Last Name field
getText( getObject( form, ContactDetailLastNameField ),
precord, DB_LAST_NAME_START );
// CH.5 Get the text for the Phone Number field
getText( getObject( form, ContactDetailPhoneNumberField ),
precord, DB_PHONE_NUMBER_START );
// CH.5 Unlock the record
MemHandleUnlock( hrecord );
}
// CH.5 Reset the dirty bit
isDirty = false;
// CH.5 We're done
return;
}
// CH.5 Set the text in a field
static void setText( FieldPtr field, CharPtr text )
{
VoidHand hfield; // CH.5 Handle of field text
CharPtr pfield; // CH.5 Pointer to field text
// CH.5 Get the current field handle
hfield = FldGetTextHandle( field );
// CH.5 If we have a handle
if( hfield != NULL )
{
// CH.5 Resize it
MemHandleResize( hfield, StrLen( text ) + 1 );
}
else
// CH.5 Allocate a handle for the string
hfield = MemHandleNew( StrLen( text ) + 1 );
// CH.5 Lock it
pfield = MemHandleLock( hfield );
// CH.5 Copy the string
StrCopy( pfield, text );
// CH.5 Unlock it
MemHandleUnlock( hfield );
// CH.5 Give it to the field
FldSetTextHandle( field, hfield );
// CH.5 Draw the field
FldDrawField( field );
// CH.5 We're done
return;
}
// CH.5 Get the text from a field
static void getText( FieldPtr field, VoidPtr precord, Word offset )
{
CharPtr pfield; // CH.5 Pointer to field text
// CH.5 Get the text pointer
pfield = FldGetTextPtr( field );
// CH.5 Copy it
DmWrite( precord, offset, pfield, StrLen( pfield ) );
// CH.5 We're done
return;
}
// CH.6 Set the Contact Detail date selector trigger
static void setDateTrigger( void )
{
FormPtr form; // CH.5 The contact detail form
// CH.6 Get the contact detail form pointer
form = FrmGetActiveForm();
// CH.6 If there is no date
if( dateTime.year == NO_DATE )
{
CtlSetLabel( getObject( form, ContactDetailDateSelTrigger ),
" " );
}
else
// CH.6 If there is a date
{
Char dateString[dateStringLength];
// CH.6 Get the date string
DateToAscii( dateTime.month, dateTime.day, dateTime.year,
(DateFormatType)PrefGetPreference( prefDateFormat ), dateString );
// CH.6 Set the selector trigger label
CtlSetLabel( getObject( form, ContactDetailDateSelTrigger ),
dateString );
}
// CH.6 We're done
return;
}
// CH.6 Set the Contact Detail time selector trigger
static void setTimeTrigger( void )
{
FormPtr form; // CH.5 The contact detail form
// CH.6 Get the contact detail form pointer
form = FrmGetActiveForm();
// CH.6 If there's no time
if( dateTime.hour == NO_TIME )
{
CtlSetLabel( getObject( form, ContactDetailTimeSelTrigger ),
" " );
}
else
// CH.6 If there is a time
{
Char timeString[timeStringLength];
// CH.6 Get the time string
TimeToAscii( dateTime.hour, dateTime.minute,
(TimeFormatType)PrefGetPreference( prefTimeFormat ), timeString );
// CH.6 Set the selector trigger label
CtlSetLabel( getObject( form, ContactDetailTimeSelTrigger ),
timeString );
}
// CH.6 We're done
return;
}
// CH.6 Set the controls in the Enter Time form based on dateTime
static void setTimeControls( void )
{
FormPtr form;
ControlPtr hourButton;
ControlPtr minuteTensButton;
ControlPtr minuteOnesButton;
ControlPtr amButton;
ControlPtr pmButton;
ControlPtr noTimeCheckbox;
Char labelString[3];
SWord hour;
// CH.6 Get the form
form = FrmGetActiveForm();
// CH.6 Get the control pointers
hourButton = getObject( form, EnterTimeHoursPushButton );
minuteTensButton = getObject( form,
EnterTimeMinuteTensPushButton );
minuteOnesButton = getObject( form,
EnterTimeMinuteOnesPushButton );
amButton = getObject( form, EnterTimeAMPushButton );
pmButton = getObject( form, EnterTimePMPushButton );
noTimeCheckbox = getObject( form, EnterTimeNoTimeCheckbox );
// CH.6 If there is a time
if( dateTime.hour != NO_TIME )
{
// CH.6 Update the hour
hour = dateTime.hour % 12;
if( hour == 0 )
hour = 12;
CtlSetLabel( hourButton,
StrIToA( labelString, hour ) );
// CH.6 Update the minute tens
CtlSetLabel( minuteTensButton,
StrIToA( labelString, dateTime.minute / 10 ) );
// CH.6 Update the minute ones
CtlSetLabel( minuteOnesButton,
StrIToA( labelString, dateTime.minute % 10 ) );
// CH.6 Update AM
CtlSetValue( amButton, (dateTime.hour < 12) );
// CH.6 Update PM
CtlSetValue( pmButton, (dateTime.hour > 11) );
// CH.6 Uncheck the no time checkbox
CtlSetValue( noTimeCheckbox, false );
}
else
// If there is no time
{
// CH.6 Update the hour
CtlSetValue( hourButton, false );
CtlSetLabel( hourButton, "" );
// CH.6 Update the minute tens
CtlSetValue( minuteTensButton, false );
CtlSetLabel( minuteTensButton, "" );
// CH.6 Update the minute ones
CtlSetValue( minuteOnesButton, false );
CtlSetLabel( minuteOnesButton, "" );
// CH.6 Update AM
CtlSetValue( amButton, false );
// CH.6 Update PM
CtlSetValue( pmButton, false );
// CH.6 Uncheck the no time checkbox
CtlSetValue( noTimeCheckbox, true );
}
// CH.6 We're done
return;
}