分享
 
 
 

PalmOS开发教程-6

王朝other·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

第六章 控件

在这一章中,我们将继续研究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(),

&currentDate );

// 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(),

&currentDate );

// 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;

}

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有