/*++

Copyright (c) 1995  Microsoft Corporation
All rights reserved.

Module Name:

    data.hxx

Abstract:

    Queries the printer for Item data.

Author:

    Albert Ting (AlbertT)  27-Jan-1995

Revision History:

--*/

#ifndef _DATA_HXX
#define _DATA_HXX


class VDataNotify;
class VDataRefresh;
class MDataClient;

/********************************************************************

    The FieldTable translates a DATA_INDEX index into a field value.
    The DATA_INDEX is used in GetInfo.

********************************************************************/

typedef struct FIELD_TABLE {
    UINT cFields;
    PFIELD pFields;
} *PFIELD_TABLE;


/********************************************************************

    ITEM and PRINTER changes.

    When a change occurs, a callback is called with a ITEM_CHANGE or
    CONTAINER_CHANGE parameter that indicates how the object changed.

********************************************************************/

enum ITEM_CHANGE {
    kItemNull,                // Prevent 0 message value.
    kItemCreate,              // Item being created.
    kItemDelete,              // Item being deleted.
    kItemPosition,            // Item position changed.
    kItemInfo,                // Affects list/icon and report view.
    kItemAttributes,          // Affects report view only (not list/icon view).
    kItemName                 // Item name changed.
};


enum CONTAINER_CHANGE {
    kContainerNull,

    kContainerConnectStatus,   // Connect state changed -> add to title bar
    kContainerErrorStatus,     // Error occured -> add to status bar
    kContainerServerName,      // Server name changed
    kContainerName,            // Container name changed

    kContainerStatus,          // Container status changed -> add to title bar
    kContainerAttributes,      // Attributes changed
    kContainerStateVar,        // StateVar additions
    kContainerNewBlock,        // New data block available

    /********************************************************************

        The following are synchronous, but may be called from
        any thread.

    ********************************************************************/

    //
    // UI should release all references to VData data.  This occurs
    // either at the start of a refresh, or when VData is being deleted.
    // Called from any thread.
    // INFO = VOID
    //
    kContainerClearItems,

    //
    // Refresh data has been completely sent to UI thread; called from
    // UI thread only.
    // INFO = VOID
    //
    kContainerRefreshComplete,

    //
    // Reload based on new hBlock; called from UI thread only.
    // INFO = cItems.
    //
    // A drastic change has occured to the datastore.  The data object
    // now contains INFO new items.  During this call, the client
    // should release all pointer references to old objects and
    // reload the UI with the new objects.
    //
    // It will get a RefreshComplete when the reload has completed.
    //
    kContainerReloadItems
};


/********************************************************************

    Connect status codes and map.

    Note: if you change or move any of the CONNECT_STATUS k* contants,
    you must update the CONNECT_STATUS_MAP.  Used by queue.cxx code.

********************************************************************/

enum CONNECT_STATUS {
    kConnectStatusNull = 0,
    kConnectStatusInvalidPrinterName,
    kConnectStatusAccessDenied,
    kConnectStatusOpenError,
    kConnectStatusOpen,
    kConnectStatusInitialize,
    kConnectStatusRefresh,
    kConnectStatusCommand,
    kConnectStatusPoll
};

#define CONNECT_STATUS_MAP {     \
    0,                           \
    IDS_SB_INVALID_PRINTER_NAME, \
    IDS_SB_ACCESS_DENIED,        \
    IDS_SB_OPEN_ERROR,           \
    IDS_SB_OPEN,                 \
    IDS_SB_INITIALIZE,           \
    IDS_SB_REFRESH,              \
    IDS_SB_COMMAND,              \
    IDS_SB_POLL                  \
}


typedef HANDLE HITEM, *PHITEM;          // Opaque packet holding job item info.
typedef UINT DATA_INDEX, *PDATA_INDEX;  // Selector for printer data.


/********************************************************************

    MDataClient

    User of Data structure supports this interface.  Both the
    Queue and Printer support this interface.

    The support could be placed in Queue only, allowing Data to
    talk through Printer.Queue, but the lifetime of Queue is managed
    through Printer, so we must still talk through printer.
    (The lifetime of Printer is identical to Data.)

********************************************************************/

class VInfoClient {

public:

    /********************************************************************

        Callbacks for VData

        vContainerChanged( ContainerChange, Info )
            Indicates something Container wide has changed.

            May be called from UI, Notify, or Worker thread.

        vItemChanged( ItemChange, hItem, Info, InfoNew );
            Indicates that a single Item has changed and needs to be
            refreshed.

            Only called from UI thread.

    ********************************************************************/

    //
    // Something printer wide has changed.  Notify the UI thread.
    // This call must return quickly, and can not use any
    // SendMessages since it acquires the printer critical section.
    //
    virtual
    VOID
    vContainerChanged(
        CONTAINER_CHANGE ContainerChange,
        INFO Info
        ) = 0;

    //
    // A single Item has changed (or possibly moved).  Notify the
    // UI that it needs to refresh.  Always called from UI thread.
    //
    virtual
    VOID
    vItemChanged(
        ITEM_CHANGE ItemChange,
        HITEM hItem,
        INFO Info,
        INFO InfoNew
        ) = 0;


    /********************************************************************

        Saving and restoring selections.

    ********************************************************************/

    //
    // When all Items change, the client needs to save its solections,
    // refresh, then restore selections.  After we call vContainerChanged
    // with new Items, we restore the selections.
    //
    // These calls are _non-reentrant_ since they are called from the
    // UI thread.
    //
    virtual
    VOID
    vSaveSelections(
        VOID
        ) = 0;

    virtual
    VOID
    vRestoreSelections(
        VOID
        ) = 0;


    /********************************************************************

        Creation of TData{Refresh/Notify}.

    ********************************************************************/

    virtual
    VDataNotify*
    pNewNotify(
        MDataClient* pDataClient
        ) const = 0;

    virtual
    VDataRefresh*
    pNewRefresh(
        MDataClient* pDataClient
        ) const = 0;
};


class MDataClient : public VInfoClient {

    SIGNATURE( 'dtc' )

public:

    virtual
    LPTSTR
    pszServerName(
        LPTSTR pszServerBuffer
        ) const = 0;

    virtual
    LPTSTR
    pszPrinterName(
        LPTSTR pszServerBuffer
        ) const = 0;

    virtual
    HANDLE
    hPrinter(
        VOID
        ) const = 0;

    virtual
    HANDLE
    hPrinterNew(
        VOID
        ) const = 0;
};


/********************************************************************

    VData provides an abstraction layer for retrieving data and
    notifications from a printer.  The TPrinter object gets the
    hPrinter and hEvent, then allows VData to reference them.

    The state of the notification is managed through the following
    worker functions:

        svStart   - Begins the notification
        svEnd     - Ends the notification
        svRefresh - Refreshs data about the connection

    These worker functions return a STATEVAR.  The printer/
    notification is managed as a simple state machine.  When
    a call fails, the StateVar indicates what should happen
    next (it is not intended to return error code information).

    The VData will retrieve a Item buffer using GetItem.  To retrieve
    specific field data about a Item (such as page count, time
    submitted, etc.), call GetInfo with the field index.

        GetItem         - Gets an hItem based on Item order index.
        GetInfo         - Gets info about a particular hItem.
        GetId           - Gets Item id about a particular hItem.
        GetNaturalIndex - Gets the natural index of a ItemId.

    For example,

        hItem = pData->GetItem( NaturalIndex );

        if( hItem ){
            Info = GetInfo( hItem, DataIndexForName );
        }

        if( Info.pszData ){
            OutputDebugString( Info.pszData );
        }

    The VData* type _must_ call vRefreshComplete (inherited from
    VData*) as soon as a refresh has occurred, but before it
    re-initializes the notification scheme.  This allows the client
    to know the boundary between pre-refresh notifications and
    post-refresh.

********************************************************************/

class VData : public MNotifyWork {

    ALWAYS_VALID
    SIGNATURE( 'dtv' )

public:

    VAR( HANDLE, hNotify );

    /********************************************************************

        Creation and deletion should be handled from pNew and
        vDelete rather than the regular new and delete, since
        we create a derived class that is determined at runtime.

    ********************************************************************/

    static
    STATEVAR
    svNew(
        IN     MDataClient* pDataClient,
        IN     STATEVAR StateVar,
           OUT VData*& pData
        );

    VOID
    vDelete(
        VOID
        );

    /********************************************************************

        Data retrievers: called from UI thread.

    ********************************************************************/

    virtual
    HITEM
    GetItem(
        NATURAL_INDEX NaturalIndex
        ) const = 0;

    virtual
    HITEM
    GetNextItem(
        HITEM hItem
        ) const = 0;

    virtual
    INFO
    GetInfo(
        HITEM hItem,
        DATA_INDEX DataIndex
        ) const = 0;

    virtual
    IDENT
    GetId(
        HITEM hItem
        ) const = 0;

    virtual
    NATURAL_INDEX
    GetNaturalIndex(
        IDENT id,
        PHITEM phItem
        ) const = 0;

    /********************************************************************

        Item block manipulators.

        vBlockProcess - A block that has been given to the client via
            vBlockAdd should now be consumed by calling vBlockProcess.

    ********************************************************************/

    VOID
    vBlockProcess(
        VOID
        );

    /********************************************************************

        Executes in worker threads.

    ********************************************************************/

    virtual
    STATEVAR
    svNotifyStart(
        STATEVAR StateVar
        ) = 0;

    virtual
    STATEVAR
    svNotifyEnd(
        STATEVAR StateVar
        ) = 0;

    virtual
    STATEVAR
    svRefresh(
        STATEVAR StateVar
        ) = 0;

protected:

    struct UI_GUARD {
        COUNT _cItems;
    } UIGuard;

    class TBlock {
    friend VData;

        SIGNATURE( 'dabl' )
        SAFE_NEW
        ALWAYS_VALID

    private:

        DLINK( TBlock, Block );

        DWORD _dwParam1;
        DWORD _dwParam2;
        HANDLE _hBlock;

        TBlock(
            DWORD dwParam1,
            DWORD dwParam2,
            HANDLE hBlock
            );

        ~TBlock(
            VOID
            );
    };

    MDataClient* _pDataClient;
    PFIELD_TABLE _pFieldTable;

    DLINK_BASE( TBlock, Block, Block );

    VData(
        MDataClient* pDataClient,
        PFIELD_TABLE pFieldTable
        );

    virtual
    ~VData(
        VOID
        );

    VOID
    vBlockAdd(
        DWORD dwParam1,
        DWORD dwParam2,
        HANDLE hBlock
        );

    virtual
    VOID
    vBlockDelete(
        HANDLE hBlock
        ) = 0;

    virtual
    VOID
    vBlockProcessImp(
        DWORD dwParam1,
        DWORD dwParam2,
        HBLOCK hBlock
        ) = 0;

private:

    //
    // Virtual definition for MNotifyWork.
    //
    HANDLE
    hEvent(
        VOID
        ) const;

    static
    MCritSec&
    csData(
        VOID
        )
    {
        return *gpCritSec;
    }
};

/********************************************************************

    VDataRefresh implements the downlevel version: it receives
    a single DWORD value for notifications and completely refreshes
    all Items when a change occurs.

********************************************************************/

class VDataRefresh : public VData {

    SIGNATURE( 'dtrv' )

public:

    VDataRefresh(
        MDataClient* pDataClient,
        PFIELD_TABLE pFieldTable,
        DWORD fdwWatch
        );

    ~VDataRefresh(
        VOID
        );

    /********************************************************************

        Exported services (statics) for related data refresh functions.

    ********************************************************************/

    static
    BOOL
    bEnumJobs(
        HANDLE hPrinter,
        DWORD dwLevel,
        PVOID* ppvBuffer, CHANGE
        PDWORD pcbBuffer,
        PDWORD pcJobs
        );

    static
    BOOL
    bEnumPrinters(
        DWORD dwFlags,
        LPCTSTR pszServer,
        DWORD dwLevel,
        PVOID* ppvBuffer,
        PDWORD pcbBuffer,
        PDWORD pcPrinters
        );

    static
    BOOL
    bGetPrinter(
        HANDLE hPrinter,
        DWORD dwLevel,
        PVOID* ppvBuffer,
        PDWORD pcbBuffer
        );

    static
    BOOL
    bGetPrinterDriver(
        HANDLE hPrinter,
        LPCTSTR pszEnvironment,
        DWORD dwLevel,
        PVOID* ppvBuffer,
        PDWORD pcbBuffer
        );

    /********************************************************************

        Executes in worker threads.

    ********************************************************************/

    STATEVAR
    svNotifyStart(
        STATEVAR StateVar
        );

    STATEVAR
    svNotifyEnd(
        STATEVAR StateVar
        );

protected:

    enum _CONSTANTS {
        kInitialJobHint = 0x400,
        kInitialPrinterHint = 0x400,
        kExtraJobBufferBytes = 0x80,
        kExtraPrinterBufferBytes = 0x80,
        kMaxPrinterInfo2 = 0x1000,
        kInitialDriverInfo3Hint = 0x400
    };

private:

    DWORD _fdwWatch;

    struct EXEC_GUARD {
        //
        // We need a separate hPrinter for notifications since downlevel
        // print providers that don't support F*PCN will default to
        // WaitForPrinterChange.  The WPC call is synchronous and
        // may take a long time (10 sec->1 min).  Since RPC synchronizes
        // handle access, this make the Get/Enum call very slow.
        //
        VAR( HANDLE, hPrinterWait );
    } ExecGuard;

    /********************************************************************

        Notify support (callbacks when object is notified).

    ********************************************************************/

    VOID
    vProcessNotifyWork(
        TNotify* pNotify
        );
};


/********************************************************************

    There are two flavors: Jobs and Printers

    TDataRJob: Handles print queues (item unit = print job).
    TDataRPrinter: Handles server view/print folder (item = printer)

********************************************************************/

class TDataRJob : public VDataRefresh {

    SIGNATURE( 'dtrj' )

public:

    enum CONSTANTS {
        kfdwWatch = PRINTER_CHANGE_JOB | PRINTER_CHANGE_PRINTER
    };

    TDataRJob(
        MDataClient* pDataClient
        );

    ~TDataRJob(
        VOID
        );

    /********************************************************************

        Data retrievers.

    ********************************************************************/

    HITEM
    GetItem(
        NATURAL_INDEX NaturalIndex
        ) const;

    HITEM
    GetNextItem(
        HITEM hItem
        ) const;

    INFO
    GetInfo(
        HITEM hItem,
        DATA_INDEX DataIndex
        ) const;

    IDENT
    GetId(
        HITEM hItem
        ) const;

    NATURAL_INDEX
    GetNaturalIndex(
        IDENT id,
        PHITEM phItem
        ) const;


    /********************************************************************

        Block manipulators.

    ********************************************************************/

    VOID
    vBlockDelete(
        HBLOCK hBlock
        );

    VOID
    vBlockProcessImp(
        DWORD dwParam1,
        DWORD dwParam2,
        HBLOCK hBlock
        );

    /********************************************************************

        Executes in worker threads.

    ********************************************************************/

    STATEVAR
    svRefresh(
        STATEVAR StateVar
        );

private:

    //
    // All fields here are guarded by ExecGuard.
    //
    struct EXEC_GUARD {
        VAR( COUNTB, cbJobHint );
    } ExecGuard;

    //
    // All fields here are accessed only from the UI thread.
    //
    struct UI_GUARD {

        VAR( PJOB_INFO_2, pJobs );

    } UIGuard;
};

class TDataRPrinter: public VDataRefresh {

    SIGNATURE( 'dtrp' )

public:

    //
    // Keep it simple: we could just watch for PRINTER_CHANGE_ADD_PRINTER
    // and PRINTER_CHANGE_DELETE_PRINTER in the non-details view,
    // but that only helps us w/ NT 3.1, 3.5 servers.  (3.51 support
    // FFPCN, and lm/wfw/win95 trigger full notifications anyway).
    //
    enum CONSTANTS {
        kfdwWatch = PRINTER_CHANGE_PRINTER
    };

    TDataRPrinter(
        MDataClient* pDataClient
        );

    ~TDataRPrinter(
        VOID
        );

    static
    BOOL
    bSinglePrinter(
        LPCTSTR pszDataSource
        );

    /********************************************************************

        Data retrievers.

    ********************************************************************/

    HITEM
    GetItem(
        NATURAL_INDEX NaturalIndex
        ) const;

    HITEM
    GetNextItem(
        HITEM hItem
        ) const;

    INFO
    GetInfo(
        HITEM hItem,
        DATA_INDEX DataIndex
        ) const;

    IDENT
    GetId(
        HITEM hItem
        ) const;

    NATURAL_INDEX
    GetNaturalIndex(
        IDENT id,
        PHITEM phItem
        ) const;


    /********************************************************************

        Block manipulators.

    ********************************************************************/

    VOID
    vBlockDelete(
        HBLOCK hBlock
        );

    VOID
    vBlockProcessImp(
        DWORD dwParam1,
        DWORD dwParam2,
        HBLOCK hBlock
        );

    /********************************************************************

        Executes in worker threads.

    ********************************************************************/

    STATEVAR
    svRefresh(
        STATEVAR StateVar
        );

private:

    //
    // Used to determine whether EnumPrinters or GetPrinter
    // should be called.
    //
    BOOL _bSinglePrinter;

    //
    // All fields here are guarded by ExecGuard.
    //
    struct EXEC_GUARD {
        VAR( COUNTB, cbPrinterHint );
    } ExecGuard;

    //
    // All fields here are accessed only from the UI thread.
    //
    struct UI_GUARD {
        VAR( PPRINTER_INFO_2, pPrinters );
    } UIGuard;
};

/********************************************************************

    VDataNotify implements the uplevel version: with eacph
    notification, it receives information about the change and
    doesn't need to refresh the entire buffer.

********************************************************************/

class VDataNotify : public VData {

    SIGNATURE( 'dtnv' )

public:

    VDataNotify(
        MDataClient* pDataClient,
        PFIELD_TABLE pFieldTable,
        DWORD TypeItem
        );

    ~VDataNotify(
        VOID
        );

    class TItemData {
    public:

        VAR( IDENT, Id );
        VAR( VDataNotify*, pDataNotify );

        DLINK( TItemData, ItemData );

        static TItemData*
        pNew(
            VDataNotify* pDataNotify,
            IDENT Id
            );

        VOID
        vDelete(
            VOID
            );

        //
        // Must be last, since variable size.
        //
        INFO _aInfo[1];

    private:

        //
        // Not defined; always use pNew since this isn't a
        // first-class class.
        //
        TItemData();
    };

    /********************************************************************

        Data retrievers.

    ********************************************************************/

    HITEM
    GetItem(
        NATURAL_INDEX NaturalIndex
        ) const;

    HITEM
    GetNextItem(
        HITEM hItem
        ) const;

    INFO
    GetInfo(
        HITEM hItem,
        DATA_INDEX DataIndex
        ) const;

    IDENT
    GetId(
        HITEM hItem
        ) const;

    NATURAL_INDEX
    GetNaturalIndex(
        IDENT id,
        PHITEM phItem
        ) const;

    /********************************************************************

        Block manipulators.

    ********************************************************************/

    VOID
    vBlockDelete(
        HBLOCK hBlock
        );

    VOID
    vBlockProcessImp(
        DWORD dwParam1,
        DWORD dwParam2,
        HBLOCK hBlock
        );

    /********************************************************************

        Executes in worker threads.

    ********************************************************************/

    STATEVAR
    svNotifyStart(
        STATEVAR StateVar
        );

    STATEVAR
    svNotifyEnd(
        STATEVAR StateVar
        );

    STATEVAR
    svRefresh(
        STATEVAR StateVar
        );

protected:

    //
    // The linked list data structure is not very efficient if we
    // continually need to look up Ids.  Each data field from the
    // notification comes in single pieces (name, size, etc.), but
    // they are clumped, so the cache prevents duplicate lookups.
    //
    // Since we are in the UI thread, we don't have to worry about
    // items being deleted while the cache is used.
    //
    typedef struct CACHE {

        TItemData* pItemData;
        IDENT Id;
        NATURAL_INDEX NaturalIndex;
        BOOL bNew;

    } PCACHE;

    //
    // All fields here are accessed only from the UI thread.
    //
    struct UI_GUARD {

        DLINK_BASE( TItemData, ItemData, ItemData );

    } UIGuard;

    //
    // Allow a derived class to override the creation of a new
    // pItemData.  In the normal case, just call the TItemData->pNew.
    //
    virtual
    TItemData*
    pNewItemData(
        VDataNotify* pDataNotify,
        IDENT Id
        )
    {
        return TItemData::pNew( pDataNotify, Id );
    }

    /********************************************************************

        Standard method of updating a pInfo.

    ********************************************************************/

    VOID
    vUpdateInfoData(
        IN const PPRINTER_NOTIFY_INFO_DATA pData,
        IN TABLE Table,
        IN PINFO pInfo
        );

private:

    enum PROCESS_TYPE {
        kProcessRefresh,
        kProcessIncremental
    };

    //
    // Indicates the *_NOTIFY_TYPE that applies to items.
    //
    DWORD _TypeItem;

    /********************************************************************

        Notify support (callbacks when object is notified).

    ********************************************************************/

    VOID
    vProcessNotifyWork(
        TNotify* pNotify
        );

    virtual
    PPRINTER_NOTIFY_OPTIONS
    pNotifyOptions(
        VOID
        ) = 0;

    //
    // Returns TRUE if item should be deleted.
    //
    virtual
    BOOL
    bUpdateInfo(
        const PPRINTER_NOTIFY_INFO_DATA pData,
        DATA_INDEX DataIndex,
        CACHE& Cache
        ) = 0;

    /*++

    Routine Description:

        Requests the child update a particular item.

    Arguments:

        pData - New data about the item stored in Cache.

        DataIndex - DataIndex that pData refers to.

        Cache - Cached information about the item, including
            pItemData, Id, NaturalIndex, and bNew (indicates whether
            the item is a new one or not; set when a new item is
            added and for all subsequent notifications that
            immediately follow it in the same notification block.

    Return Value:

        TRUE if item should be deleted, FALSE otherwise.

    --*/


    /********************************************************************

        Private member functions.

    ********************************************************************/

    TItemData*
    GetItem(
        IDENT Id,
        NATURAL_INDEX* pNaturalIndex
        );

    BOOL
    bItemProcess(
        const PPRINTER_NOTIFY_INFO_DATA pData,
        CACHE& Cache
        );

    VOID
    vContainerProcess(
        const PPRINTER_NOTIFY_INFO_DATA pData
        );

    VOID
    vDeleteAllItemData(
        VOID
        );

#if DBG
    VOID
    vDbgOutputInfo(
        const PPRINTER_NOTIFY_INFO pInfo
        );
#endif
friend VDataNotify::TItemData;
};


/********************************************************************

    DataNotify tables.

    Define the visible columns in the UI, and also the extra fields
    that follow it.

    We maintain two conceptual arrays, and therefore two index types
    (one for each array):

        COLUMN: Array of Fields corresponding to the UI columns.
        DATA_INDEX: Array of Fields corresponding to a Item's data
            structure.

    The DATA_INDEX array is a superset of the COLUMN array: i.e., for all
    valid COLUMN, aFieldColumn[COLUMN] == aFieldIndex[COLUMN].

    The entire next block is highly self-dependent.  Adding/deleting,
    or changing a field must be done very carefully.

    These definitions are in the classes next to the constants.

********************************************************************/


/********************************************************************

    There are two flavors: Jobs and Printers

    TDataRJob: Handles print queues (job unit = print job).
    TDataRPrinter: Handles server view/print folder (job unit = printer)

********************************************************************/

class TDataNJob : public VDataNotify {
friend TDataRJob;

    SIGNATURE( 'dtnj' )

public:

    enum CONSTANTS {
        kFieldOtherSize = 4,
        kTypeSize = 2,

#define JOB_COLUMN_FIELDS           \
    JOB_NOTIFY_FIELD_DOCUMENT,      \
    JOB_NOTIFY_FIELD_STATUS_STRING, \
    JOB_NOTIFY_FIELD_USER_NAME,     \
    JOB_NOTIFY_FIELD_TOTAL_PAGES,   \
    JOB_NOTIFY_FIELD_TOTAL_BYTES,   \
    JOB_NOTIFY_FIELD_SUBMITTED,     \
    JOB_NOTIFY_FIELD_PORT_NAME

        kColumnFieldsSize = 7,

#define JOB_INDEX_EXTRA_FIELDS      \
    JOB_NOTIFY_FIELD_PAGES_PRINTED, \
    JOB_NOTIFY_FIELD_BYTES_PRINTED, \
    JOB_NOTIFY_FIELD_POSITION,      \
    JOB_NOTIFY_FIELD_STATUS

        kIndexExtraFieldsSize = 4,
        kIndexPagesPrinted = kColumnFieldsSize,
        kIndexBytesPrinted = kColumnFieldsSize + 1,
        kIndexStatus       = kColumnFieldsSize + 3,

        kFieldTableSize = kColumnFieldsSize +
                          kIndexExtraFieldsSize
    };

    TDataNJob(
        MDataClient* pDataClient
        );

    ~TDataNJob(
        VOID
        );

private:

    static PRINTER_NOTIFY_OPTIONS gNotifyOptions;
    static FIELD gaFieldOther[kFieldOtherSize];
    static PRINTER_NOTIFY_OPTIONS_TYPE gaNotifyOptionsType[kTypeSize];

    static FIELD_TABLE gFieldTable;
    static FIELD gaFields[kFieldTableSize+1];

    PPRINTER_NOTIFY_OPTIONS
    pNotifyOptions(
        VOID
        )
    {
        return &gNotifyOptions;
    }

    BOOL
    bUpdateInfo(
        const PPRINTER_NOTIFY_INFO_DATA pData,
        DATA_INDEX DataIndex,
        CACHE& Cache
        );
};


class TDataNPrinter: public VDataNotify {
friend TDataRPrinter;

    SIGNATURE( 'dtnp' )

public:

    enum CONSTANTS {

        kTypeSize = 1,

#define PRINTER_COLUMN_FIELDS           \
    PRINTER_NOTIFY_FIELD_PRINTER_NAME,  \
    PRINTER_NOTIFY_FIELD_CJOBS,         \
    PRINTER_NOTIFY_FIELD_STATUS,        \
    PRINTER_NOTIFY_FIELD_COMMENT

        kIndexPrinterName = 0,
        kIndexCJobs,
        kIndexStatus,
        kIndexComment,

        kColumnFieldsSize = 4,

#define PRINTER_INDEX_EXTRA_FIELDS      \
    PRINTER_NOTIFY_FIELD_ATTRIBUTES

        kIndexExtraFieldsSize = 1,
        kIndexAttributes = kColumnFieldsSize,

        kFieldTableSize = kColumnFieldsSize +
                          kIndexExtraFieldsSize
    };

    TDataNPrinter(
        MDataClient* pDataClient
        );

    ~TDataNPrinter(
        VOID
        );

private:

    static PRINTER_NOTIFY_OPTIONS gNotifyOptions;
    static PRINTER_NOTIFY_OPTIONS_TYPE gaNotifyOptionsType[kTypeSize];

    static FIELD_TABLE gFieldTable;
    static FIELD gaFields[kFieldTableSize+1];

    TItemData*
    pNewItemData(
        VDataNotify* pDataNotify,
        IDENT Id
        );

    PPRINTER_NOTIFY_OPTIONS
    pNotifyOptions(
        VOID
        )
    {
        return &gNotifyOptions;
    }

    BOOL
    bUpdateInfo(
        const PPRINTER_NOTIFY_INFO_DATA pData,
        DATA_INDEX DataIndex,
        CACHE& Cache
        );
};



#endif // ndef _DATA_HXX