//
// File: hdsNavtree.cpp
//
// Legal Notice: This file is part of the HadronZoo C++ Class Library. Copyright 2025 HadronZoo Project (http://www.hadronzoo.com)
//
// The HadronZoo C++ Class Library is free software: You can redistribute it, and/or modify it under the terms of the GNU Lesser General Public License, as published by the Free
// Software Foundation, either version 3 of the License, or any later version.
//
// The HadronZoo C++ Class Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along with the HadronZoo C++ Class Library. If not, see http://www.gnu.org/licenses.
//
#include <iostream>
#include <fstream>
#include <unistd.h>
#include <stdarg.h>
#include <dirent.h>
#include <netdb.h>
#include <signal.h>
#include "hzErrcode.h"
#include "hzDissemino.h"
using namespace std ;
/*
** Functions
*/
const hdsArticle* hdsNavtree::GetItem (const hzString& refname) const
{
_navitem item ; // Title/articleId
if (!m_ItemsByName.Exists(refname))
return 0 ;
item = m_ItemsByName[refname] ;
if (item.m_ItemId)
return m_Articles[item.m_ItemId - 1] ;
return 0 ;
}
const hdsArticle* hdsNavtree::GetItem (uint32_t itemId) const
{
if (itemId == 0 || itemId > m_Articles.Count())
return 0 ;
return m_Articles[itemId - 1] ;
}
hzEcode hdsNavtree::AddHead (const hzString& parent, const hzString& refname, const hzString& title, bool bSlct)
{
// Add a heading to the tree. No article so the _navitem only has the article title
_hzfunc("hdsNavtree::AddHead(1)") ;
_navitem item ; // New tree item
_navitem parItem ; // Parent item
// Qualify item
if (!refname) return hzerr(E_ARGUMENT, "%s: No item refname supplied", *m_Groupname) ;
if (!title) return hzerr(E_ARGUMENT, "%s: No item title supplied", *m_Groupname) ;
if (m_ItemsByName.Exists(refname))
hzexit(E_DUPLICATE, "%s: Heading %s (%s) already exists", *m_Groupname, *refname, *title) ;
if (parent)
{
if (!m_ItemsByName.Exists(parent))
return hzerr(E_CORRUPT, "%s: Heading %s (%s): Stated parent (%s) does not exist", *m_Groupname, *refname, *title, *parent) ;
parItem = m_ItemsByName[parent] ;
}
item.m_Title = title ;
if (bSlct)
item.m_bFlags |= HZ_TREEITEM_OPEN ;
item.m_bFlags |= HZ_TREEITEM_LINK ;
if (!parent)
item.m_nLevel = 0 ;
else
item.m_nLevel = parItem.m_nLevel + 1 ;
m_ItemsByParent.Insert(parent, refname) ;
m_ItemsByName.Insert(refname, item) ;
return E_OK ;
}
hzEcode hdsNavtree::AddItem (const hzString& parent, const hzString& refname, const hzString& title, hdsArticle* pArt, bool bSlct)
{
// Add an item. The first item should have a NULL parent
//
// Arguments: 1) parent Parent refname
// 2) refname Reference name of new item
// 3) title Title (how item appears in tree)
// 4) pItem Tree item (if any)
// 5) bSlct Show as open
//
// Returns: E_ARGUMENT If either the itemId or headline is not supplied
// E_DUPLICATE If the supplied item id already exists
_hzfunc("hdsNavtree::AddItem") ;
_navitem item ; // New item
_navitem parItem ; // Parent item
hzEcode rc = E_OK ; // Return code
// Qualify item
threadLog("Adding article %p %s\n", pArt, *refname) ;
if (!refname) return hzerr(E_ARGUMENT, "%s: No item item id supplied", *m_Groupname) ;
if (!title) return hzerr(E_ARGUMENT, "%s: No item headline supplied", *m_Groupname) ;
// Check item id (item id) is unique
if (m_ItemsByName.Exists(refname))
return hzerr(E_DUPLICATE, "%s: Duplicate entry attempted: Item %s alredy exists", *m_Groupname, *refname) ;
// If parent is supplied, it must exist
if (parent)
{
if (!m_ItemsByName.Count())
return hzerr(E_CORRUPT, "%s: Adding first item %s but with non-null parent", *m_Groupname, *title) ;
if (!m_ItemsByName.Exists(parent))
return hzerr(E_CORRUPT, "%s: Supplied parent (%s) does not exist", *m_Groupname, *parent) ;
parItem = m_ItemsByName[parent] ;
}
// ADD the item
m_Articles.Add(pArt) ;
item.m_ItemId = m_Articles.Count() ;
item.m_Title = title ;
if (bSlct)
item.m_bFlags |= HZ_TREEITEM_OPEN ;
item.m_bFlags |= HZ_TREEITEM_LINK ;
if (!parent)
item.m_nLevel = 0 ;
else
item.m_nLevel = parItem.m_nLevel + 1 ;
rc = m_ItemsByParent.Insert(parent, refname) ;
if (rc != E_OK)
return hzerr(rc, "%s: Could not insert parent for article %s", *m_Groupname, *title) ;
rc = m_ItemsByName.Insert(refname, item) ;
if (rc != E_OK)
return hzerr(rc, "%s: Could not insert article %s", *m_Groupname, *title) ;
return rc ;
}
void hdsNavtree::Clear (void)
{
_hzfunc("hdsNavtree::Clear") ;
hdsArticle* pArt ; // Current item
uint32_t n ; // Article iterator
for (n = 0 ; n < m_Articles.Count() ; n++)
{
pArt = m_Articles[n] ;
delete pArt ;
}
m_ItemsByParent.Clear() ;
m_ItemsByName.Clear() ;
}
hzEcode hdsNavtree::_procArticle (hzChain& Z, const hzString& parent) const
{
// Support function for ExportArticleSet (see below)
//
// Adds tree item to supplied chain
//
// Arguments: 1) J Chain in which tree is being built
// 2) pItem Tree item to process
//
// Returns: E_ARGUMENT If the tree item pointer is NULL
// E_OK If the tree is exported to the supplied chain
_hzfunc("hdsNavtree::_procArticle") ;
hdsArticle* pArt ; // Article
hdsArticleStd* pArtStd ; // Article
_navitem item ; // Next tree item
hzXDate now ; // Current time and date
hzString refname ; // Refname of item
hzString state ; // Open or closed
int32_t nLo ; // Lowest position in m_AllItemsByParent
int32_t nHi ; // Higest position in m_AllItemsByParent
int32_t n ; // Item counter
nLo = m_ItemsByParent.First(parent) ;
if (nLo < 0)
return E_OK ;
nHi = m_ItemsByParent.Last(parent) ;
now.SysDateTime() ;
// Print: Item is a folder. In the first instance (the only time the level is 1) for channel J we don't want to start with a comma, otherwise we do
for (n = nLo ; n <= nHi ; n++)
{
refname = m_ItemsByParent.GetObj(n) ;
item = m_ItemsByName[refname] ;
if (item.m_bFlags & HZ_TREEITEM_OPEN)
state = "open" ;
else
state = "closed" ;
if (!item.m_ItemId)
{
if (item.m_bFlags & HZ_TREEITEM_LINK)
Z.Printf("\n<xtreeItem parent=\"%s\" id=\"%s\" hdln=\"%s\" display=\"%s\"/>\n", *parent, *refname, *item.m_Title, *state) ;
else
Z.Printf("\n<xtreeHead parent=\"%s\" id=\"%s\" hdln=\"%s\" display=\"%s\"/>\n", *parent, *refname, *item.m_Title, *state) ;
}
else
{
pArt = m_Articles[item.m_ItemId - 1] ;
pArtStd = dynamic_cast<hdsArticleStd*>(pArt) ;
if (item.m_bFlags & HZ_TREEITEM_LINK)
{
Z.Printf("\n<xtreeItem parent=\"%s\" id=\"%s\" tdstamp=\"%s\" hdln=\"%s\" display=\"%s\">\n", *parent, *refname, *now, *item.m_Title, *state) ;
if (pArtStd)
//Z << pArtStd->m_Content ;
Z << pArtStd->m_rawHTML ;
Z << "\n</xtreeItem>\n" ;
}
else
{
Z.Printf("\n<xtreeHead parent=\"%s\" id=\"%s\" hdln=\"%s\" display=\"%s\">\n", *parent, *refname, *item.m_Title, *state) ;
if (pArtStd)
//Z << pArtStd->m_Content ;
Z << pArtStd->m_rawHTML ;
Z << "\n</xtreeHead>\n" ;
}
}
_procArticle(Z, refname) ;
}
return E_OK ;
}
hzEcode hdsNavtree::ExportArticleSet (hzChain& Z)
{
// Purpose: Export tree as an article set
//
// Arguments: 1) J The chain in which tree is being built
//
// Returns: E_NODATA If the tree is empty
// E_OK If the tree is exported to the supplied chain
_hzfunc("hdsNavtree::ExportArtcleSet") ;
if (!m_ItemsByName.Count())
return E_NODATA ;
hzString root ; // Blank string
_procArticle(Z, root) ;
return E_OK ;
}
hzEcode hdsNavtree::_procTreeitem (hzChain& J, const hzString& parent) const
{
// Support function for ExportDataScript (see below)
//
// Adds tree item to supplied chain
//
// Arguments: 1) J Chain in which tree is being built
// 2) pItem Tree item to process
//
// Returns: E_ARGUMENT If the tree item pointer is NULL
// E_OK If the tree is exported to the supplied chain
_hzfunc("hdsNavtree::_procTreeitem") ;
_navitem item ; // Tree item
hzString refname ; // Refname of item
int32_t nLo ; // Lowest position in m_AllItemsByParent
int32_t nHi ; // Higest position in m_AllItemsByParent
int32_t n ; // Item counter
int32_t i ; // Indent counter
int32_t nChild ; // True if link
//uint32_t id ; // ID of item
nLo = m_ItemsByParent.First(parent) ;
if (nLo < 0)
return E_OK ;
nHi = m_ItemsByParent.Last(parent) ;
for (n = nLo ; n <= nHi ; n++)
{
refname = m_ItemsByParent.GetObj(n) ;
item = m_ItemsByName[refname] ;
nChild = m_ItemsByParent.First(refname) >= 0 ? 1 : 0 ;
//nChild = 1 ;
if (item.m_nLevel || n > nLo)
J.AddByte(CHAR_COMMA) ;
J.AddByte(CHAR_NL) ;
for (i = item.m_nLevel ; i ; i--)
J.AddByte(CHAR_TAB) ;
if (item.m_bFlags & HZ_TREEITEM_LINK)
J.Printf("[%d,%d,%d,\"%s\",\"%s\"]", item.m_nLevel, nChild, item.m_bFlags & HZ_TREEITEM_OPEN ? 1:0 , *item.m_Title, *refname) ;
else
J.Printf("[%d,%d,%d,\"%s\",\"%s\"]", item.m_nLevel, nChild, item.m_bFlags & HZ_TREEITEM_OPEN ? 1:0 , *item.m_Title, *refname) ;
if (nChild)
_procTreeitem(J, refname) ;
}
return E_OK ;
}
hzEcode hdsNavtree::ExportDataScript (hzChain& J)
{
// Purpose: Build a HTML/JavaScript hierarchical tree.
//
// Generate data script for the tree. A data script for trees is always an array of treeitems which in turn comprise four values. These are the
// level, a 0 or a 1 for either item/folder, the title and the URL (link to item).
//
// The data script is used in conjuction with the _dsmScript_navtree/makeTree script which uses the data script to effect the tree in HTML bu a series of
// document.write operations.
//
// Arguments: 1) J The chain in which tree is being built
//
// Returns: E_NODATA If the tree is empty
// E_OK If the tree is exported to the supplied chain
_hzfunc("hdsNavtree::ExportDataScript") ;
if (!this)
hzexit(E_CORRUPT, "No instance") ;
if (!m_ItemsByName.Count())
return E_NODATA ;
hzString root ; // Blank start
J << "var ntX=new Array();\nntX=[" ;
_procTreeitem(J, root) ;
J << "\n\t];\n" ;
// threadLog("BEGIN NAV\n") ;
// threadLog(J) ;
// threadLog("ENDF NAV\n") ;
return E_OK ;
}