########################
Using Object Data Tables
########################

An ``ObjectDataTable`` is a table that – instead of simply containing text information – embeds objects such as JavaScript functions that can be accessed via callback functions.

The following sample relies on the :doc:`DemoDataModel </sample_codes>` example, which can be found in the distribution package in the publishadvanced sample solution.
For simplicity, all of the code below should be added to *DemoDataModel.cpp* in the :doc:`DemoDataModel </sample_codes>` project.


Defining an ObjectDataTable
===========================

First, let's write a simple function that will create our ``ObjectDataTable`` into which we'll later insert our JavaScript actions. We'll define our ``A3DPDFDataTableData`` just as we would any other table in Advanced Publishing, but to create the table we'll call ``A3DPDFObjectDataTableCreate()``, which will create an ``ObjectDataTable``:

.. code-block:: c

    A3DStatus stCreateObjectTable(A3DPDFDocument* pDoc, const std::vector<std::vector<std::string> > &aRows, A3DPDFDataTable*& pDataTable)
    {
        A3DStatus iRet = A3D_SUCCESS;
        A3DUns32 iNbRows = (int)aRows.size();
        A3DUns32 iNbCols = (iNbRows > 0) ? (int)aRows[0].size() : 0;
        A3DPDFDataTableData sTableData = A3D_MAKE_DATA(A3DPDFDataTableData);
        sTableData.m_iNbRows = iNbRows;
        sTableData.m_iNbCols = iNbCols;
        sTableData.m_ppcTexts = (A3DUTF8Char**)malloc(iNbRows * iNbCols * A3DUns32(sizeof(A3DUTF8Char*)));
        for (A3DUns32 ir = 0; ir < iNbRows; ir++)
        {
            for (A3DUns32 ic = 0; ic < iNbCols; ic++)
                mallocAndSetString(
                (A3DUTF8Char*)(char*)aRows[ir][ic].c_str(),       // src
                    sTableData.m_ppcTexts[ir*iNbCols + ic]);        // dest
        }
        CHECK_RET(A3DPDFObjectDataTableCreate(pDoc, &sTableData, &pDataTable));
        stFreeTable(sTableData);
        return iRet;
    }


Injecting JavaScript
====================

Now that we've created an ``ObjectDataTable``, we can add custom actions by embedding JavaScript code.
Continuing with the *DemoDataModel* example from the Advanced Publishing package, we'll replace the existing stBuildDocument() function with some new code specifically for our ``ObjectDataTable``.
The full code for our stBuildDocument() function is available at the bottom of this page.

Let's define a simple JavaScript function to serve as a callback in our PDF document. This callback function can be triggered in JavaScript actions we'll later assign to individual fields:

.. code-block:: c

    std::basic_ostringstream<char> osJsOnDoc;
    osJsOnDoc << "\n\
    \n\
    function scrolltable_callback_withuserdata(i, j, userdata)\n\
    {\n\
      app.alert( { cMsg:\" JavaScript action on cell [\"+i+\" - \"+j+\"]\\n model name \"+userdata.modelname+\" ; owner \"+userdata.modelowner+\"  \" } );\n\
      console.println(\"in scrolltable_callback ; i=\"+i+\" - j=\"+j);\n\
      console.println(\"in scrolltable_callback userdata=\"+userdata);\n\
      console.println(\"in scrolltable_callback userdata.modelname=\"+userdata.modelname);\n\
      \n\
    }\n\
    ";
    CHECK_RET(A3DPDFDocumentAddJavascriptFromString(pDoc, "STCallbacks", osJsOnDoc.str().c_str()));

In this snippet, we're simply reading our JavaScript function into a C++ stream and calling A3DPDFDocumentAddJavascriptFromString() to embed it in our PDF document.

Next we'll create a ``A3DPDFDataTable`` and add rows to it one at a time. Each row is a standard vector object, and each element in the vector represents a column in the ``A3DPDFDataTable``. Within each column, we'll define an ``actionjscallback`` function and an ``actionjscallbackdata`` object, which we'll use to write some information to an alert window whenever a user clicks on that particular cell.

In addition, we'll define an ``actionurl``, which will launch a browser window and retrieve a URL whenever the user clicks on the associated icon.

.. code-block:: c

    A3DPDFDataTable* pDataTable_Bom = NULL;
    // structure for Tables definitions
    std::vector<std::vector<std::string> > aRows;
    std::vector<std::string> aOneRow;
    aRows.clear();
    aOneRow.clear();
    aOneRow.push_back("{ caption:\"1\" }"); // default action: action datamodel
    aOneRow.push_back("{ caption:\"crank damper\", actionjscallback:\"scrolltable_callback_withuserdata\", actionjscallbackdata: { modelname:\"crank_damper\" , modelowner:\"Joe\" } }");
    aOneRow.push_back("{ caption:\"2\"}"); // default action: action datamodel
    aOneRow.push_back("{ caption:\"iron\"}"); // default action: action datamodel
    sIconId = stStoreIcon(pDoc, std::string(ICONS_PATH) + "crank_damper.jpg");
    aOneRow.push_back(std::string("{ icon:\"") + sIconId + std::string("\", actionurl:\"https://www.techsoft3d.com/get?model=crank_damper\" }"));
    aRows.push_back(aOneRow);
    //...add 5 more rows here.
    stCreateObjectTable(pDoc, aRows, pDataTable_Bom);
    // --- Widgets binding for Table BOM
    const A3DInt32 piMapColumns_Bom[] = { 0, 1, 2, 3, 4, 5 };
    CHECK_RET(A3DPDFScrollTableBindToTable(pScrollTableBom, pDataTable_Bom, sizeof(piMapColumns_Bom) / sizeof(A3DInt32), piMapColumns_Bom));

In the final section of the snippet, we've called ``A3DPDFScrollTableBindToTable()`` to embed our ``ObjectDataTable`` inside of a ``A3DPDFScrollTable``.


Running the Sample
==================

Compiling and then running the *DemoDataModel* sample will create a new PDF in the samples directory called *sample_DemoDataModel.pdf*:

.. image:: object_table.png
    :align: center

The PDF will contain a part list – clicking on a part name will trigger the ``actionjscallback`` custom event, popping up an alert window containing the data retrieved with our JavaScript functions. Clicking on an image icon will launch the ``actionurl`` in a browser.

.. image:: object_table_alert.png
    :align: center


Full Sample Code
================

.. code-block:: c

    A3DStatus stCreateObjectTable(A3DPDFDocument* pDoc, const std::vector<std::vector<std::string> > &aRows, A3DPDFDataTable*& pDataTable)
    {
        A3DStatus iRet = A3D_SUCCESS;
        A3DUns32 iNbRows = (int)aRows.size();
        A3DUns32 iNbCols = (iNbRows > 0) ? (int)aRows[0].size() : 0;
        A3DPDFDataTableData sTableData = A3D_MAKE_DATA(A3DPDFDataTableData);
        sTableData.m_iNbRows = iNbRows;
        sTableData.m_iNbCols = iNbCols;
        sTableData.m_ppcTexts = (A3DUTF8Char**)malloc(iNbRows * iNbCols * A3DUns32(sizeof(A3DUTF8Char*)));
        for (A3DUns32 ir = 0; ir < iNbRows; ir++)
        {
            for (A3DUns32 ic = 0; ic < iNbCols; ic++)
                mallocAndSetString(
                (A3DUTF8Char*)(char*)aRows[ir][ic].c_str(),       // src
                    sTableData.m_ppcTexts[ir*iNbCols + ic]);        // dest
        }
        CHECK_RET(A3DPDFObjectDataTableCreate(pDoc, &sTableData, &pDataTable));
        stFreeTable(sTableData);
        return iRet;
    }
    // In DemoDataModel example, replace existing stBuildDocument() function with this code.
    A3DStatus stBuildDocument(A3DPDFDocument* pDoc)
    {
        A3DStatus iRet = A3D_SUCCESS;
        A3DPDFPage* pPage = NULL;
        int indexpage = 0;
        std::string sSuffixFields;
        // populating the document metadata
        A3DPDFDocumentInformationData sInformationData = A3D_MAKE_DATA(A3DPDFDocumentInformationData);
        sInformationData.m_pcTitle = (char*) "Advanced Publishing Functionalities";
        sInformationData.m_pcCreator = (char*) "Advanced Publishing Sample Application";
        sInformationData.m_pcSubject = (char*) "This sample demoes the data model functionality of advanced publishing";
        sInformationData.m_pcAuthor = (char*) "Tech Soft 3D";
        CHECK_RET(A3DPDFDocumentSetInformation(pDoc, &sInformationData));
        // creating a document page from scratch
        A3DPDFPageData2 sPageData = A3D_MAKE_DATA(A3DPDFPageData2);
        sPageData.m_ePageOrientation = kA3DPDFPageLandscape;
        sPageData.m_ePageSize = kA3DPDFPageLetter;     // standard letter format: 612 x 792
        const int iPageWidth = 792, iPageHeight = 612;
        CHECK_RET(A3DPDFDocumentAppendNewPage2(pDoc, &sPageData, &pPage));
        if (pPage != NULL)
        {
            std::string sIconId;
            // create Scroll table on the page
            A3DPDFScrollTable* pScrollTableBom = NULL;
            std::vector<int> aiWidthCols; // in points
            aiWidthCols.push_back(30);
            aiWidthCols.push_back(170);
            aiWidthCols.push_back(30);
            aiWidthCols.push_back(60);
            aiWidthCols.push_back(50);
            std::vector<std::string> aRowHeader;
            aRowHeader.push_back("No");
            aRowHeader.push_back("NAME PART");
            aRowHeader.push_back("QTY");
            aRowHeader.push_back("MATERIAL");
            aRowHeader.push_back("SNAPSHOT");
            std::vector<A3DPDFEColumnType> aeTypes;
            aeTypes.push_back(kA3DPDFTextContent);
            aeTypes.push_back(kA3DPDFTextContent);
            aeTypes.push_back(kA3DPDFTextContent);
            aeTypes.push_back(kA3DPDFTextContent);
            aeTypes.push_back(kA3DPDFImageContent);
            CHECK_RET(stAddScrollTable(pDoc, pPage,
                20, 500, // iPosLeft, iPosTop
                15, //iSliderWidth
                3,  // iNbRowsInFrame
                20, // iHeightRowHeader - in points
                40, // iHeightRows - in points
                aiWidthCols, // in points
                aRowHeader,
                aeTypes,
                &pScrollTableBom));
            // store JavaScript functions on the document. these functions can be used by JavaScript stored on the fields.
            std::basic_ostringstream<char> osJsOnDoc;
            osJsOnDoc << "\n\
    \n\
    function scrolltable_callback_withuserdata(i, j, userdata)\n\
    {\n\
      app.alert( { cMsg:\" JavaScript action on cell [\"+i+\" - \"+j+\"]\\n model name \"+userdata.modelname+\" ; owner \"+userdata.modelowner+\"  \" } );\n\
      console.println(\"in scrolltable_callback ; i=\"+i+\" - j=\"+j);\n\
      console.println(\"in scrolltable_callback userdata=\"+userdata);\n\
      console.println(\"in scrolltable_callback userdata.modelname=\"+userdata.modelname);\n\
      \n\
    }\n\
    ";
            CHECK_RET(A3DPDFDocumentAddJavascriptFromString(pDoc, "STCallbacks", osJsOnDoc.str().c_str()));
            A3DPDFDataTable* pDataTable_Bom = NULL;
            //CHECK_RET(stCreateTable_Sple1_Manufacturers(pDoc, pDataTable_Bom));
            // structure for Tables definitions
            std::vector<std::vector<std::string> > aRows;
            std::vector<std::string> aOneRow;
            aRows.clear();
            aOneRow.clear();
            aOneRow.push_back("{ caption:\"1\" }"); // default action: action datamodel
            aOneRow.push_back("{ caption:\"crank damper\", actionjscallback:\"scrolltable_callback_withuserdata\", actionjscallbackdata: { modelname:\"crank_damper\" , modelowner:\"Joe\" } }");
            aOneRow.push_back("{ caption:\"2\"}"); // default action: action datamodel
            aOneRow.push_back("{ caption:\"iron\"}"); // default action: action datamodel
            sIconId = stStoreIcon(pDoc, std::string(ICONS_PATH) + "crank_damper.jpg");
            aOneRow.push_back(std::string("{ icon:\"") + sIconId + std::string("\", actionurl:\"https://www.techsoft3d.com/get?model=crank_damper\" }"));
            aRows.push_back(aOneRow);
            aOneRow.clear();
            aOneRow.push_back("{ caption:\"2\" }");
            aOneRow.push_back("{ caption:\"crank crankshaft\", actionjscallback:\"scrolltable_callback_withuserdata\", actionjscallbackdata: { modelname:\"crank_crankshaft\" , modelowner:\"James\" } }");
            aOneRow.push_back("{ caption:\"1\"}");// default: action datamodel
            aOneRow.push_back("{ caption:\"steel\" }"); // default: action datamodel
            sIconId = stStoreIcon(pDoc, std::string(ICONS_PATH) + "crank_crankshaft.jpg");
            aOneRow.push_back(std::string("{ icon:\"") + sIconId + std::string("\", actionurl:\"https://www.techsoft3d.com/get?model=crank_crankshaft\" }"));
            aRows.push_back(aOneRow);
            aOneRow.clear();
            aOneRow.push_back("{ caption:\"3\" }");
            aOneRow.push_back("{ caption:\"crank valve\", actionjscallback:\"scrolltable_callback_withuserdata\", actionjscallbackdata: { modelname:\"crank_valve\" , modelowner:\"Bill\" } }");
            aOneRow.push_back("{ caption:\"1\"}");
            aOneRow.push_back("{ caption:\"steel\", action:\"_rowdatamodel_\"}");
            sIconId = stStoreIcon(pDoc, std::string(ICONS_PATH) + "crank_valve.jpg");
            aOneRow.push_back(std::string("{ icon:\"") + sIconId + std::string("\", actionurl:\"https://www.techsoft3d.com/get?model=crank_valve\" }"));
            aRows.push_back(aOneRow);
            aOneRow.clear();
            aOneRow.push_back("{ caption:\"4\" }");
            aOneRow.push_back("{ caption:\"crank connectingrod boltnut\", actionjscallback:\"scrolltable_callback_withuserdata\", actionjscallbackdata: { modelname:\"crank_connectingrod_boltnut\" , modelowner:\"Neal\" } }");
            aOneRow.push_back("{ caption:\"1\"}");// default: action datamodel
            aOneRow.push_back("{ caption:\"steel\" }"); // default: action datamodel
            sIconId = stStoreIcon(pDoc, std::string(ICONS_PATH) + "crank_connectingrod_boltnut.jpg");
            aOneRow.push_back(std::string("{ icon:\"") + sIconId + std::string("\", actionurl:\"https://www.techsoft3d.com/get?model=crank_connectingrod_boltnut\" }"));
            aRows.push_back(aOneRow);
            aOneRow.clear();
            aOneRow.push_back("{ caption:\"5\" }");
            aOneRow.push_back("{ caption:\"crank pistonpin\", actionjscallback:\"scrolltable_callback_withuserdata\", actionjscallbackdata: { modelname:\"crank_pistonpin\" , modelowner:\"Henry\" } }");
            aOneRow.push_back("{ caption:\"2\"}");// default: action datamodel
            aOneRow.push_back("{ caption:\"steel2\" }"); // default: action datamodel
            sIconId = stStoreIcon(pDoc, std::string(ICONS_PATH) + "crank_pistonpin.jpg");
            aOneRow.push_back(std::string("{ icon:\"") + sIconId + std::string("\", actionurl:\"https://www.techsoft3d.com/get?model=crank_pistonpin\" }"));
            aRows.push_back(aOneRow);
            aOneRow.clear();
            aOneRow.push_back("{ caption:\"6\" }");
            aOneRow.push_back("{ caption:\"crank connectingrod bushing\", actionjscallback:\"scrolltable_callback_withuserdata\", actionjscallbackdata: { modelname:\"crank_connectingrod_bushing\" , modelowner:\"Jack\" } }");
            aOneRow.push_back("{ caption:\"3\"}");// default: action datamodel
            aOneRow.push_back("{ caption:\"steel3\" }"); // default: action datamodel
            sIconId = stStoreIcon(pDoc, std::string(ICONS_PATH) + "crank_connectingrod_bushing.jpg");
            aOneRow.push_back(std::string("{ icon:\"") + sIconId + std::string("\", actionurl:\"https://www.techsoft3d.com/get?model=crank_connectingrod_bushing\" }"));
            aRows.push_back(aOneRow);
            stCreateObjectTable(pDoc, aRows, pDataTable_Bom);
            // --- Widgets binding for Table BOM
            const A3DInt32 piMapColumns_Bom[] = { 0, 1, 2, 3, 4, 5 };
            CHECK_RET(A3DPDFScrollTableBindToTable(pScrollTableBom, pDataTable_Bom, sizeof(piMapColumns_Bom) / sizeof(A3DInt32), piMapColumns_Bom));
            indexpage++;
        }
        // end document
        CHECK_RET(A3DPDFDocumentSaveEx(pDoc, kA3DPDFSaveFull|kA3DPDFSaveOptimized, OUT_FILE));
        return iRet;
    }
