package Palm::Progect::Record;

use base 'Palm::Progect::VersionDelegator';
use Carp;
use strict;

use CLASS;
use base qw(Class::Accessor);

sub Accessors {


my %Categories = ('Unfiled' => 0);  # name => id

# Class method to set all available category names at once,
# using the Palm::StdAppinfo format of a list of hashrefs
# in the form of:
#     { name => 'Some cat', id => 7, renamed => 'who cares' }
sub set_categories {
    my $class = shift;

    my @categories = @_;

    if (!@categories or $categories[0]{'name'} !~ /^\s*unfiled\s*$/i ) {
        unshift @categories, { 'name' => 'Unfiled' };

    %Categories = ();

    # I don't know what the category ids are used
    # for, considering the 'category number'
    # associated with a pdb record refers to
    # the category's *position* within this array,
    # not its 'id'.

    for (my $id = 0; $id < @categories; $id++) {

        my $name  = $categories[$id]{'name'};

        next unless $id;   # Skip 'Unfiled'
        next unless $name; # Skip blank categories

        $Categories{$name} = $id;

    # Add the 'Unfiled' category, which is
    # always zero.
    $Categories{'Unfiled'} = 0;

# Class method to get all available category names, in
# the order of their category ids
sub get_categories {
    my $class = shift;

    # Since the keys and values are both meant to be
    # unique, we can reverse the %Categories hash:

    my %categories_by_id = reverse %Categories;

    my @categories;
    for my $id (sort { $a <=> $b } keys %categories_by_id) {
        push @categories, {
            id      => $id,
            name    => $categories_by_id{$id},
            renamed => 0,

    return @categories if wantarray;
    return \@categories;

# object method accessor
sub category_name {
    my $self = shift;
    if (defined $_[0]) {
        my $category_name = $_[0];

        $self->{category_name} = $category_name;

        if (not exists $Categories{$category_name}) {
            # Put this category_name in the Class-global %Categories
            # hash, setting it's category_id to the max number
            # of categories that are already there, plus one
            $Categories{$category_name} = (scalar keys %Categories);
    if ($self->{category_name} and $self->{category_name} eq 'Unfiled') {
        return '';
    else {
        return $self->{category_name};

# In order to assign a category id to a record, the category
# must already exist; i.e. it must have been set via
# the class methods set_categories or add_categories

sub category_id {
    my $self        = shift;
    my $category_id = shift;

    if (defined $category_id) {

        # Since both keys and values of %Categories are unique,
        # we can reverse the hash...
        my %cat_lookup = reverse %Categories;

        # Internally, we only maintain the category_name
        # and we lookup the id if its requested.
        # So if someone sets the category id, we actually
        # look up the category_name for that id and store it instead

        if (exists $cat_lookup{$category_id}) {
            $self->{'category_name'} = $cat_lookup{$category_id};
        else {
            croak "There is no category with the id #$category_id.  Before setting a record's category call Palm::Progect::Records->set_categories with the complete list of categories\n";

    my $cat_name = $self->{'category_name'} || 'Unfiled';
    return $Categories{$cat_name};



=head1 NAME

Palm::Progect::Record - Individual Records of the Progect Database



    for my $rec (@{ $progect->records }) {
        my $description = $rec->description;
        my $priority    = $rec->priority;
        my $category    = $rec->category_name;
        print "[$priority] {$category} $description\n";


Each L<Palm::Progect> object contains a list of records in its C<records> method.

Each record is a C<Palm::Progect::Record> object.


=over 4

=item set_categories

Class method to set all available category names at once,
using the Palm::StdAppinfo format of a list of hashrefs
in the form of:

    { name => 'Some cat', id => 7, renamed => 'who cares' }

=item get_categories

Class method to get all available categories
using the Palm::StdAppinfo format of a list of hashrefs
in the form of:

    { name => 'Some cat', id => 7, renamed => 'who cares' }


=head1 METHODS

=over 4

=item description

The text of the record

=item note

The note attached to the record, if any.  If the record does not have a
note, then this value will be false.

=item type

The type of the record

Type will be one of the six types of records:

=over 4








These symbols are available from the C<Palm::Progect::Constants> module.

For instance:

    use Palm::Progect::Constants;

    if ($rec->type == RECORD_TYPE_ACTION) {
        print "The record is an action item!\n";

=item priority

Priority of the item, 1 - 5, or 0, for B<No priority>.

=item completed

The value of this field depends on the type of the record.

For action items, completed is B<true> if the record has been completed,
C<false> otherwise.

For progress items, completed is the percent complete, a value from 0-100.

For numeric items (e.g. 5/20), completed reflects the percent complete
(see C<completed_actual> and C<completed_limit>, below).  Note that
setting the C<completed> method will not affect C<completed_actual> or

=item completed_actual

=item completed_limit

Numeric items indicate their 'completeness' with an arbitrary ratio:
Some value out of another value, for instance 5 out of 20, or 5/20,
where 5 is the C<completed_actual> and 20 is the C<completed_limit>.

=item level

The indent level of the record.

=item has_next

True if the record has another record following it, false otherwise.

=item has_child

True if the record contains another record, false otherwise.

=item has_prev

True if the record follows another record, false otherwise.

=item is_opened

True if the record is open, revealing the records it contains, false otherwise.

=item has_todo

True if the record links to a record in the todo database, false otherwise.

=item date_due

The due date of the record, in unix time format (i.e. seconds since the epoch).
If the record does not have a due date then this value will be false.

=item category_name

The name of the record's category, if any.  If you set C<category_name>
to the name of a category that doesn't yet exist, then that category will
be created.


=head1 AUTHOR

Michael Graham E<lt>mag-perl@occamstoothbrush.comE<gt>

Copyright (C) 2002-2005 Michael Graham.  All rights reserved.
This program is free software.  You can use, modify,
and distribute it under the same terms as Perl itself.

The latest version of this module can be found on

=head1 SEE ALSO