Export a List of Link Module Descriptors

Link Module Descriptors, aka linkset pairings, are stored in the parent folder of the source module. These determine which link module is used when creating links between two modules. This script loops through the current folder and recursively through subfolders and reports on the link module descriptors (LMD’s) it finds. Output is to a CSV file.

//  List Link Module Descriptors in Folder

/*
	Recursively scan the current folder or project and generates a list
	of Link Module Descriptors (LMD's).

	Output is to a CSV file. 

	Tony Goodman.
*/

pragma runLim,0  /* turn off timeout dialog */

DB  dbMain        = null
DBE dbeExport     = null
DBE dbeExportPath = null

Folder currFolder = null
Buffer  outBuf    = null

/************************************
	scanFolder
************************************/
void scanFolder(Folder f)
{
	LinkModuleDescriptor lmd

	Item   itm
	string s
	string t
	string l

	if (null f) return

	for lmd in f do
	{
		outBuf += fullName(f) ", " getSourceName(lmd) "," getTargetName(lmd) "," getName(lmd) "\n"
	}

	for itm in f do
	{
		if (null itm) continue
		if (isDeleted(itm)) continue

		if ((type (itm) == "Project") || (type (itm) == "Folder"))
		{
			scanFolder(folder(itm))
		}
	}
}

/************************************
	doClose
************************************/
void doClose(DB db)
{
	release(dbMain)
}

/************************************
	doList
************************************/
void doList(DB db)
{
	outBuf = create
    outBuf = ""

	string fName = get(dbeExportPath)

	Stream outfile = write(fName)

	outfile << "Folder, Source Module, Target Module, Link Module\n"

	scanFolder(currFolder)

	outfile << outBuf
	close(outfile)
	delete(outBuf)

	doClose(db)
}

/************************************
	MAIN
************************************/
currFolder = current Folder

dbMain = create("List Link Module Descriptors")

label(dbMain, "This utility exports a list of all link module descriptors\n" //-
              "Output is to a CSV file as follows:\n" //-
              "Folder, Source Module, Target Module, Link Module\n")

dbeExport     = field(dbMain, "Folder", fullName(currFolder), 50, true)
dbeExportPath = fileName(dbMain, "Report File", "C:/Temp/" name(currFolder) " Link Module Descriptors.csv")

ok(dbMain, "OK", doList)
close(dbMain, true, doClose)
realize dbMain

block(dbMain)

destroy(dbMain)
dbMain = null
Posted in Links, Metrics and Reports | Comments closed

Report the Number of Links Per Linkset

This script loops through the current folder and recursively through subfolders and reports on the linksets it finds. The output is a count of links found in each linkset. Output is to a CSV file.

//  List Link Counts per Linkset

/*
	Recursively scan the current folder or project and generates a
	count of Links per Linkset.

	Output is to a CSV file.

	Tony Goodman.
*/

pragma runLim,0  /* turn off timeout dialog */

DB  dbMain        = null
DBE dbeExport     = null
DBE dbeExportPath = null

Folder currFolder = null
Buffer  outBuf    = null

/************************************
	scanLinkModule
************************************/
void scanLinkModule(Item itm)
{
	Module        lm    = null
	Module        sm    = null
	Object        o     = null
	string        lmn   = ""
	string        src   = ""
	string        tgt   = ""
	int           count = 0
	ModuleVersion mv    = null
	Link          l     = null

	lmn = fullName(itm)

	// read the link module
	lm = read(lmn, false)

	for o in lm do
	{
		// get names of source and target modules
		src = o."source" ""
		tgt = o."target" ""

		// read the source module
		sm = read(src, false)

		filtering off

		for o in all(sm) do
		{
			for l in all(o->lmn) do
			{
				// check for target module for matching name
				mv = targetVersion(l)

				if (fullName(mv) == tgt)
				{
					count++
				}
			}
		}

		close(sm)

		// output count for this linkset
		outBuf += lmn "," src "," tgt "," count "\n"
	}

	close(lm)
}

/************************************
	scanFolder
************************************/
void scanFolder(Folder f)
{
	Item   itm

	if (null f) return

	for itm in f do
	{
		if (null itm) continue
		if (isDeleted(itm)) continue

		if (type(itm) == "Link")
		{
			scanLinkModule(itm)
		}
		else if ((type (itm) == "Project") || (type (itm) == "Folder"))
		{
			scanFolder(folder(itm))
		}
	}
}

/************************************
	doClose
************************************/
void doClose(DB db)
{
	release(dbMain)
}

/************************************
	doList
************************************/
void doList(DB db)
{
	outBuf = create
    outBuf = ""

	string fName = get(dbeExportPath)

	Stream outfile = write(fName)

	outfile << "Link Module, Source Module, Target Module, No.Links\n"

	scanFolder(currFolder)

	outfile << outBuf
	close(outfile)
	delete(outBuf)

	doClose(db)
}

/************************************
	MAIN
************************************/
currFolder = current Folder

dbMain = create("List Link Counts")

label(dbMain, "This utility exports a count of all links per linkset.\n" //-
              "Output is to a CSV file as follows:\n" //-
              "Link Module, Source Module, Target Module, No.Links\n")

dbeExport     = field(dbMain, "Folder", fullName(currFolder), 50, true)
dbeExportPath = fileName(dbMain, "Report File", "C:/Temp/" name(currFolder) " LinkCounts.csv")

ok(dbMain, "OK", doList)
close(dbMain, true, doClose)
realize dbMain

block(dbMain)

destroy(dbMain)
dbMain = null
Posted in Links, Metrics and Reports | Comments closed

Check Module for Bad Layout DXL

layout12It is common for modules to contain layout DXL columns that cause DXL errors when the view is loaded. This may be due to #included files being removed or because of syntax errors in the layout DXL itself. Most frustrating is when the default view for the module contains a column that causes DXL errors. This prevents scripts from running unattended. This function scans a module for layout DXL and reports any errors that are found.

// Check module for bad layout DXL

/*
   It is common for modules to contain layout DXL columns that cause
   DXL errors when the view is loaded. This may be due to #included
   files being removed or because of syntax errors in the layout DXL itself.

   Most frustrating is when the default view for the module contains
   a column that causes DXL errors. This prevents scripts from running
   unattended.

   This function scans a module for layout DXL and reports any errors
   that are found.

   Tony Goodman.
*/

/************************************
	checkModuleForBadLayoutDxl

	Checks the given module for bad layout DXL.
*************************************/
void checkModuleForBadLayoutDxl(string modName)
{
	Module m              = null
	string viewName       = ""
	string defViewUser    = ""
	string defViewModule  = ""
	string sRes           = ""
	Column c              = null
	string dxlCode        = ""
	bool   bRes           = false
	int    errors         = 0

	print("Module: " modName "\n")

	// suspend error reporting and open module in background
	noError

	m = read(modName, false)

	// we ignore any errors generated because these will be picked up
	// later when we loop through all the views.
	sRes = lastError

	// check that the module was opened successfully
	if (null m)
	{
		print("ERROR opening module: " sRes "\n")
		return
	}

	// get the user's default view
	defViewUser = getDefaultViewForUser(m)

	// if there is a default view then clear it to ensure module is opened in "Standard view"
	if (!null defViewUser)
	{
		sRes = clearDefaultViewForUser(m)

		if (sRes != "")
		{
			print("ERROR clearing default view for user: " sRes "\n")
			close(m)
			return
		}
	}

	// get the module's default view
	defViewModule = getDefaultViewForModule(m)

	// if there is a default view then clear it to ensure module is opened in "Standard view"
	if (!null defViewModule)
	{
		sRes = clearDefaultViewForModule(m)

		if (sRes != "")
		{
			print("ERROR clearing default view for module: " sRes "\n")
			close(m)
			return
		}
	}

	// open module visible so we can load the views
	m = read(modName, true)

	// loop through the views in the module
	for viewName in views(m) do
	{
		print("View \"" viewName "\"...\t")

		// error count for this view
		errors = 0

		// suspend error reporting
		noError

		// load the view
		bRes = load(m, view viewName)

		if (!bRes)
		{
			// view was not loaded - continue to next view
			print("ERROR loading view - Columns not checked!\n")
			lastError
			continue
		}

		// loop through the columns in the view
		for c in m do
	    {
		    // get the layout DXL - this will return null if this is not a layout dxl column
	        dxlCode = dxl(c)

	        if (dxlCode "" != "")
	        {
		        // check the layout dxl for errors
		        sRes = checkDXL(dxlCode)

		        if (sRes "" != "")
		        {
			        // report bugs
					print("ERROR in Layout DXL Column: \"" title(c) "\"\n")
					errors++
		        }
	        }
	    }

	    // resume error reporting - no need to check for errors because we
	    // already checked the layout dxl
	    sRes = lastError

	    // if there were no errors in this view - report OK to user
	    if (errors == 0)
	    {
		    print("OK\n")
	    }
	}

	// reset the user's default view (if there was one)
	if (!null defViewUser)
	{
		setDefaultViewForUser(m, defViewUser)
	}

	// reset the module's default view (if there was one)
	if (!null defViewModule)
	{
		setDefaultViewForModule(m, defViewModule)
	}

	// close the module
	close(m)

	print("\n")
}
Posted in Errors, Layout DXL | Comments closed

Recreating the Main Column

Layout DXL is used to populate a column within a DOORS view, typically to construct traceability or impact analysis reports. Layout DXL programs run in a context where the variable obj is pre-declared. The code calculates the value to display for each object. The current object to calculate is referred to as obj.

The following layout DXL displays the object heading and text in the same format as the main column.

//	Recreate Main Column in Layout DXL

/*
	Layout DXL to display the Object Heading and Text
	in the same format as the main column.

   	Tony Goodman
*/

if (obj."Object Heading" "" != "")
{
	font(getCanvas, level(obj), HeadingsFont)
	displayRich(number(obj) " " obj."Object Heading" "")
}

if (obj."Object Text" "" != "")
{
	font(getCanvas, level(obj), TextFont)
	displayRich(richTextWithOle(obj."Object Text"))
}
Posted in Columns, Layout DXL | Comments closed

Rebuild Object Hierarchy after Import

This script moves objects into a proper hierarchy after import from spreadsheet. Assumes that the “main” column in the spreadsheet that contains object text is prefixed with a paragraph number for heading objects. The first object in the module must be a level one object. This paragraph number is parsed and used to determine the level of the object in the hierarchy. These paragraph numbers are removed and the remaining text is moved into the Object Heading.

//  Build Hierarchy after import from spreadsheet

/*
    Description:

	Moves objects into a proper hierarchy after import from
	spreadsheet.

	Assumes that the "main" column in the spreadsheet that contains
	object text is prefixed with a paragraph number for heading objects.

	The first object in the module must be a level one object.

	This paragraph number is parsed and used to determine the level of the
	object in the hierarchy.

	These paragraph numbers are removed and the remaining text is moved
	into the Object Heading.

	smartdxl.com
*/

pragma runLim, 0

const string ATTR_LEVEL_INDICATOR = "Object Text"
const string ATTR_NEW_LEVEL       = "New Level"

/************************************
	createLevelAttr
************************************/
void createLevelAttr(Module m)
{
	AttrDef ad = null

	(current ModuleRef__) = m

	if (!exists attribute ATTR_NEW_LEVEL)
	{
		ad = create(object type "Integer" attribute ATTR_NEW_LEVEL)
	}
}

/************************************
	flattenHierarchy
************************************/
string flattenHierarchy(Module m)
{
	Object o       = null
	Object oFirst  = null
	Object oPrev   = null
	int    count   = 0
	Skip   objects = create

	oFirst = first(m)

	if (null oFirst || level(oFirst) != 1)
	{
		return("Module must begin with a level 1 object")
	}

	showExplorer(false)
	showTables(false)

	for o in m do
	{
		put(objects, count++, o)
	}

	for o in objects do
	{
		if (o == oFirst)
		{
			oPrev = o
		}
		else
		{
			//print("move " identifier(o) " after " identifier(oPrev) "\n")
			move(o, oPrev)

			oPrev = o
		}
	}

	delete(objects)
	return("")
}

/************************************
	getLevel

	Returns the proposed level of the object based on the contents of
	its Object Text. If the object text is empty or does not start with
	a digit then a level of zero is returned. Otherwise the level returned
	is based on the number of digits separated by periods that are found
	at the beginnign of the object text.
************************************/
int getLevel(Object o)
{
	string s = ""
	string levelString = ""
	int len = 0
	int i = 0
	int depth = 0

	s = o.ATTR_LEVEL_INDICATOR ""

	len = length(s)

	// empty string, return zero
	if (len == 0)
	{
		return(0)
	}

	// does not start with a number, return zero
	if (!isdigit(s[0]))
	{
		return(0)
	}

	// extract the legal number from the beginning of the
	// string. Add an extra period at the end to make the next bit easier
	for (i = 0; i < len; i++)
	{
		if (s[i] != '.' && !isdigit(s[i]))
		{
			levelString = s[0:i-1] "."
			break
		}
	}

	// count the periods to determine the depth
	for (i = 0; i < length(levelString); i++)
	{
		if (levelString[i] == '.') depth++
	}

	return(depth)
}

/************************************
	getHeading
************************************/
string getHeading(string s)
{
	int len = 0
	int i = 0

	len = length(s)

	for (i = 0; i < len; i++)
	{
		if (isalpha(s[i]))
		{
			return(s[i:])
		}
	}

	return(s)
}

/************************************
	setInitialLevel

	populate level new level attribute based on contents of
    level indicator attribute (usually object text)
************************************/
void setInitialLevel(Module m)
{
	Object o = null

	for o in m do
	{
		o.ATTR_NEW_LEVEL = getLevel(o)
	}
}

/************************************
	refineLevels

	change new level attribute for objects with new level of zero.

	If new level attribute is zero then set it to level below previous object
	that had a non-zero level.
************************************/
void refineLevels(Module m)
{
	Object o = null
	int    prevLevel = 0
	int    thisLevel = 0

	for o in m do
	{
		thisLevel = o.ATTR_NEW_LEVEL

		if (thisLevel == 0)
		{
			o.ATTR_NEW_LEVEL = prevLevel + 1
		}
		else
		{
			o."Object Heading" = getHeading(o."Object Text" "")
			o."Object Text" = ""

			prevLevel = thisLevel
		}
	}
}

/************************************
	moveObjects
************************************/
void moveObjects(Object oParent)
{

}

/************************************
	moveObjects
************************************/
void moveObjects(Module m)
{
	Object o = null
	Object oParent = null
	int prevLevel = 0
	int thisLevel = 0
	int count = 0
	int l = 0
	int maxLevel = 0
	Skip objects = create

	for o in m do
	{
		put(objects, count++, o)
	}

	for o in objects do
	{
		thisLevel = o.ATTR_NEW_LEVEL

		if (thisLevel > maxLevel)
		{
			maxLevel = thisLevel
		}
	}

	for (l = maxLevel; l > 1; l--)
	{
		for o in objects do
		{
			thisLevel = o.ATTR_NEW_LEVEL

			if (thisLevel == l - 1)
			{
				oParent = o
			}
			else if (thisLevel == l)
			{
				move(o, last below oParent)
			}
		}
	}

	delete(objects)
}

/************************************
	MAIN
************************************/
Module currModule = current Module
string res = ""

if (!isEdit(currModule))
{
	infoBox("This utility can only be run in exclusive edit mode")
	halt
}

// check that the required level indicator attribute exists
createLevelAttr(currModule)

// ensure flat module hierarchy before starting - i.e. all objects at level 1
res = flattenHierarchy(currModule)

if (res != "")
{
	infoBox(res)
	halt
}

setInitialLevel(currModule)

refineLevels(currModule)

moveObjects(currModule)
Posted in Hierarchy, Import, Objects | Comments closed

Smart History Viewer

This script allows you to view object and module history across all baselines. The dialog displays history in listviews on three tabs, showing complete object, module and session history.

The object tab displays history for the current object. You can browse through objects using the Prev and Next buttons or select the object directly by selecting it in the module window. The module tab displays all module history as well as object deletions. The sessions tab displays the history of all editing sessions and baseline creations. All listviews can be sorted by any column.

This script demonstrates some very strange vaguaries of the history system such as the fact that module level attribute changes are recorded as modifyObject events, but with the object absolute number being zero! (see lines 924 and 971). Take a look at line 1009 to see how session numbers are handled.

For those of you that are interested, this script is a good example of how to use the DxlObject type together with skip lists to build practical data structures.

//  Smart History Viewer - Standalone version.

/*
    S M A R T   D X L   L I M I T E D

	Description:

	Provides a dialog to view history accross multiple baselines.

	Save this file, and tophat.bmp to <DOORS ADDINS>/SmartDXL

	Author:	Tony Goodman	 tony@smartdxl.com

    	Change History:

	25-NOV-2008		Tony Goodman	Initial version.

	24-FEB-2011		Tony Goodman	Locate bitmap from addins path.
											Add banner title and website url.
											Use more complex names for global variables.

	17-May-2011		Tony Goodman	Standalone version. Single DXL file for easy installation
											(With the exception of the optional logo bitmap file)
*/

pragma runLim, 0

/****************************
	Smart DXL logo bitmap file - this is loaded once when first dialog is displayed.
****************************/
Bitmap smartDxlLogo = null

string SMART_DXL_DB_TITLE      = "Smart DXL"
string SMART_DXL_BANNER_WEBSITE  = "www.smartdxl.com"
string SMART_DXL_BANNER_BITMAP    = "SmartDXL\\tophat.bmp"

DBE smartDxlBanner = null

bool smartDxlLogoFound = false

string smartDxlBannerText = ""

int   smartDxlLogoWidth   = 0
int   smartDxlLogoHeight  = 0

/****************************
	getAbsolutePath
****************************/
string getAbsolutePath(string sFileName)
{
	string sResult  = ""

	string sAddinsPathList      = (getenv "ADDINS") ";"
	string sDoorsAddinsPathList = (getenv "DOORSADDINS") ";"

	if (sAddinsPathList != sDoorsAddinsPathList)
	{
		sAddinsPathList = sDoorsAddinsPathList ";" sAddinsPathList
	}

	Regexp rPath = regexp "([^;]*);(.*)"

	string sLeft    = ""
	string sRight   = ""

	if (rPath sAddinsPathList)
	{
		// test each path in the semicolon separated list
		while (rPath sAddinsPathList)
		{
			  sLeft  = sAddinsPathList[match 1]
			  sRight = sAddinsPathList[match 2]

			  if (fileExists_ (sLeft "\\" sFileName))
			  {
					sResult  = sLeft "\\" sFileName
					break;
			  }
			  else
			  {
					sAddinsPathList = sRight
			  }
		}
	}
	return(sResult)
}

/*****************************
  smartDrawLogo
*****************************/
void smartDrawLogo(DBE cnvs)
{
	string smartDxlBitmapFile = ""
	Stat   st = null

	if (null smartDxlLogo)
	{
		// bitmap has not been loaded yet, so load it
		smartDxlBitmapFile = getAbsolutePath(SMART_DXL_BANNER_BITMAP)

		// check that the file exists
		st = create(smartDxlBitmapFile)

		if (!null st)
		{
			// file does exist so we can load the bitmap
			delete(st)

			smartDxlLogo = loadBitmap(cnvs, smartDxlBitmapFile, false, smartDxlLogoWidth, smartDxlLogoHeight)
		}
    }

    // draw the bitmap if it was successfully loaded
    if (!null smartDxlLogo)
    {
    	drawBitmap(cnvs, smartDxlLogo, 0, 0)
		smartDxlLogoFound = true
	}
}

/****************************
	doLogoMouse
****************************/
void doLogoMouse(DBE cnv, int but, bool ctrl, int x, int y)
{
	if (x > width(cnv) - width(cnv, SMART_DXL_BANNER_WEBSITE) - 5)
	{
		activateURL(SMART_DXL_BANNER_WEBSITE)
	}
}

/****************************
	doRepaintSmartDxlBanner
****************************/
void doRepaintSmartDxlBanner(DBE cnv)
{
	int canvasWidth  = width(cnv)
	int titleHeight  = 0
	int y = 0

	realBackground(cnv, realColor_White)

	smartDrawLogo(cnv)

	realColor(cnv, realColor_Black)
	font(cnv, 4, HeadingsFont)

	y = (height(cnv) / 2) + (height(cnv, smartDxlBannerText) / 2) - 3

	draw(cnv, smartDxlLogoWidth + 3, y, smartDxlBannerText)

	realColor(cnv, realColor_Blue)
	font(cnv, 9, HeadingsFont)
	draw(cnv, canvasWidth - width(cnv, SMART_DXL_BANNER_WEBSITE) - 5, y - 1, SMART_DXL_BANNER_WEBSITE)
	line(cnv, canvasWidth - width(cnv, SMART_DXL_BANNER_WEBSITE) - 5, y, canvasWidth - 5, y)
}

/****************************
	addSmartDxlBanner
****************************/
void addSmartDxlBanner(DB db)
{
	smartDxlBanner = canvas(db, 0, 32, doRepaintSmartDxlBanner)

	set(smartDxlBanner, doLogoMouse)
}

/****************************
	showHelp
****************************/
void showHelp(string helpFile)
{
	system("\"C:\\Program Files\\Internet Explorer\\iexplore.exe\" -nohome \"" (getAbsolutePath(helpFile)) "\"")
}

/****************************
	smartDialog
****************************/
DB smartDialog(string banner, int options)
{
	DB smartDb = null

	smartDb = create(SMART_DXL_DB_TITLE, options)

	smartDxlBannerText = banner

	addSmartDxlBanner(smartDb)

    return(smartDb)
}

/****************************
	smartDialog
****************************/
DB smartDialog(string dbTitle)
{
	return(smartDialog(dbTitle, styleCentered|styleFloating))
}	

/****************************
	smartDialog
****************************/
DB smartDialog(Module mParent, string banner, int options)
{
	DB smartDb = null

	smartDb = create(mParent, SMART_DXL_DB_TITLE, options)

	smartDxlBannerText = banner

	addSmartDxlBanner(smartDb)

    return(smartDb)
}

/****************************
	smartDialog
****************************/
DB smartDialog(Module mParent, string dbTitle)
{
	return(smartDialog(mParent, dbTitle, styleCentered|styleFloating))
}

/****************************
	smartDialog
****************************/
DB smartDialog(DB dbParent, string banner, int options)
{
	DB  smartDb     = null

	smartDb = create(dbParent, SMART_DXL_DB_TITLE, options)

	smartDxlBannerText = banner

	addSmartDxlBanner(smartDb)

    return(smartDb)
}

/****************************
	smartDialog
****************************/
DB smartDialog(DB dbParent, string banner)
{
	return(smartDialog(dbParent, banner, styleCentered|styleFloating))
}

//  Sort Callbacks for listViews

/****************************
	doSortString
****************************/
int doSortString(string s1, string s2)
{
	return(cistrcmp(s1, s2))
}

/****************************
	doSortDate
****************************/
int doSortDate(string s1, string s2)
{
	Date d1 = "1 January 1970"
	Date d2 = "1 January 1970"

	if (s1 != "")
	{
		d1 = date(s1)
	}

	if (s2 != "")
	{
		d2 = date(s2)
	}

	if (d1 > d2)
	{
		return(-1)
	}
	else if (d2 > d1)
	{
		return(1)
	}

	return(0)
}

/****************************
	doSortInteger
****************************/
int doSortInteger(string s1, string s2)
{
	int i1 = intOf(s1)
	int i2 = intOf(s2)

	if (i1 > i2)
	{
		return(1)
	}
	else if (i2 > i1)
	{
		return(-1)
	}

	return(0)
}

/****************************
	doSortLegal
****************************/
int doSortLegal(string s1, string s2)
{
	int i1 = 0
	int i2 = 0

	i1 = intOf(s1)
	i2 = intOf(s2)

	if (i1 > i2)
	{
		return(1)
	}
	else if (i2 > i1)
	{
		return(-1)
	}
	else
	{
		if (length(s1) > length(i1 ""))
		{
			if (length(s2) > length(i2 ""))
			{
				return(doSortLegal(s1[length(i1 "") + 1:], s2[length(i2 "") + 1:]))
			}

			return(1)
		}
		else if (length(s2) > length(i2 ""))
		{
			return(-1)
		}

		return(0)
	}

	return(0)
}

// columns in listview
const int BS_NAME_COLUMN       = 0
const int BS_CREATED_BY_COLUMN = 1
const int BS_CREATED_ON_COLUMN = 2
const int BS_DELETED_BY_COLUMN = 3
const int BS_DELETED_ON_COLUMN = 4

string bsListItems[] = {}

DB  bsMainDb       = null
DBE bsBaselinesDbe = null

// list of all baselines found in the module
Skip bsSelectedBaselinesSkip = null

// list of baselines selected in the listview by the user
Skip bsAllBaselinesSkip      = null

// true if the user selects one or more baselines.
// false if no baselines are selected or the user clicks cancel
bool bsBaselinesSelected = false

/****************************
	bsUpdateBaselineList

	Update the list view with entries for all baselines found in the given
	module. Also adds each baseline to the list of all baselines.
	This list is used later to get a handle on the selected baselines according
	to their position in the listview.
****************************/
void bsUpdateBaselineList(Module m)
{
    int      i            = 0
    Baseline b            = null
    string   strDate      = ""
    Date     baselineDate = null
    Date     deletedDate  = null

    // clear entries from the list view
    empty(bsBaselinesDbe)

    // loop through al baselines in the module
    for b in all m do
    {
        strDate = ""
        baselineDate = dateOf(b)

        if (!null baselineDate)
        {
            strDate = stringOf(baselineDate)
        }

        // add baseline to list of all baselines
        put(bsAllBaselinesSkip, i, b)

        // add an entry for this baseline to list view
        insert(bsBaselinesDbe, i, major(b) "." minor(b) " " suffix(b), iconNone)

        // set column values
        set(bsBaselinesDbe, i, BS_CREATED_BY_COLUMN, user(b))
        set(bsBaselinesDbe, i, BS_CREATED_ON_COLUMN, strDate)

        if (deleted(b))
        {
            strDate = ""
            deletedDate = deletedOn(b)

            if (!null deletedDate)
            {
                strDate = stringOf(deletedDate)
            }

            set(bsBaselinesDbe, i, BS_DELETED_BY_COLUMN, deletedBy(b))
            set(bsBaselinesDbe, i, BS_DELETED_ON_COLUMN, strDate)
        }
        i++
    }
}

/****************************
	bsDoCancel
	Callback for Cancel button.
****************************/
void bsDoCancel(DB db)
{
	bsBaselinesSelected = false
	release(bsMainDb)
}

/****************************
	bsDoApply
	Callback for OK button.
	Gets selections from the listview and updates the selected baseline list.
****************************/
void bsDoApply(DB db)
{
	int i = 0
	Baseline b = null

	bsBaselinesSelected = false

	// loop through entries in the list view
	for (i = 0; i < noElems(bsBaselinesDbe); i++)
	{
		// if the entry is checked (selected) ...
		if (getCheck(bsBaselinesDbe, i))
		{
			bsBaselinesSelected = true

			// look up the corresponding baseline
			if (find(bsAllBaselinesSkip, i, b))
			{
				// add to selected baseline list
				put(bsSelectedBaselinesSkip, i, b)
			}
		}
	}

	if (!bsBaselinesSelected)
	{
		infoBox("No baselines selected!")
		return
	}

	release(bsMainDb)
}

/****************************
	bsDoSelectAll

	Callback for the Select All button.
	Sets all the entries in the list to checked.
****************************/
void bsDoSelectAll(DB db)
{
	int i = 0

	for (i = 0; i < noElems(bsBaselinesDbe); i++)
	{
		setCheck(bsBaselinesDbe, i, true)
	}
}

/****************************
	bsDoDeselectAll

	Callback for the Deselect All button.
	Sets all the entries in the list to unchecked.
****************************/
void bsDoDeselectAll(DB db)
{
	int i = 0

	for (i = 0; i < noElems(bsBaselinesDbe); i++)
	{
		setCheck(bsBaselinesDbe, i, false)
	}
}

/****************************
	bsGetUserSelectedBaselines

	Launches a dialog displaying a list of baselines for the specified
	module. The user is able to select multiple baselines from the list.

	The selected baselines are returned in the skip list, indexed by an integer
	starting at zero.

	If selectAll is TRUE then the all entries in the list are initially checked.

	Returns TRUE if the user selects on or more baselines, otherwise FALSE.
****************************/
bool bsGetUserSelectedBaselines(Module m, Skip skip, string dbTitle, string msg, bool selectAll)
{
	Baseline b = null

	bsSelectedBaselinesSkip = create
	bsAllBaselinesSkip = create

	bsMainDb = smartDialog(m, dbTitle, styleCentered)

	label(bsMainDb, msg)

	bsBaselinesDbe = listView(bsMainDb, listViewOptionCheckboxes | listViewOptionSortText, 455, 10, bsListItems)

	close(bsMainDb, false, bsDoCancel)

	apply(bsMainDb, "Select All", bsDoSelectAll)
	apply(bsMainDb, "Deselect All", bsDoDeselectAll)
	apply(bsMainDb, bsDoApply)
	ok(bsMainDb, "Cancel", bsDoCancel)

	realize(bsMainDb)

	insertColumn(bsBaselinesDbe, BS_NAME_COLUMN,       "Baseline",    80, iconNone)
    insertColumn(bsBaselinesDbe, BS_CREATED_BY_COLUMN, "Created By",  80, iconNone)
    insertColumn(bsBaselinesDbe, BS_CREATED_ON_COLUMN, "Created On", 105, iconNone)
    insertColumn(bsBaselinesDbe, BS_DELETED_BY_COLUMN, "Deleted By",  80, iconNone)
    insertColumn(bsBaselinesDbe, BS_DELETED_ON_COLUMN, "Deleted On", 105, iconNone)

    set(bsBaselinesDbe, BS_NAME_COLUMN,       doSortLegal)
    set(bsBaselinesDbe, BS_CREATED_BY_COLUMN, doSortString)
    set(bsBaselinesDbe, BS_CREATED_ON_COLUMN, doSortDate)
    set(bsBaselinesDbe, BS_DELETED_BY_COLUMN, doSortString)
    set(bsBaselinesDbe, BS_DELETED_ON_COLUMN, doSortDate)

    bsUpdateBaselineList(m)

    if (selectAll)
    {
	    bsDoSelectAll(bsMainDb)
    }

	block(bsMainDb)

	hide(bsMainDb)
	destroy(bsMainDb)
	bsMainDb = null

	for b in bsSelectedBaselinesSkip do
	{
		put(skip, (int key bsSelectedBaselinesSkip), b)
	}

	delete(bsSelectedBaselinesSkip)
	delete(bsAllBaselinesSkip)

	return(bsBaselinesSelected)
}

/*

    Data structures and utilities for handling history records.

    History types:
	    const HistoryType unknown
		const HistoryType createType
		const HistoryType modifyType
		const HistoryType deleteType
		const HistoryType createAttr
		const HistoryType modifyAttr
		const HistoryType deleteAttr
		const HistoryType createObject
		const HistoryType copyObject
		const HistoryType modifyObject
		const HistoryType deleteObject
		const HistoryType unDeleteObject
		const HistoryType purgeObject
		const HistoryType moveObject
		const HistoryType clipCutObject
		const HistoryType clipMoveObject
		const HistoryType clipCopyObject
		const HistoryType createModule
		const HistoryType baselineModule
		const HistoryType partitionModule
		const HistoryType acceptModule
		const HistoryType returnModule
		const HistoryType rejoinModule
		const HistoryType createLink
		const HistoryType modifyLink
		const HistoryType deleteLink
		const HistoryType insertOLE
		const HistoryType removeOLE
		const HistoryType changeOLE
		const HistoryType pasteOLE
		const HistoryType cutOLE
		const HistoryType readLocked
		const HistoryType commentObject
		const HistoryType commentModule
*/

// indices to DxlObject structure
const string HIST_AUTHOR       = "author"
const string HIST_DATE         = "date"
const string HIST_BASELINE     = "baseline"
const string HIST_SESSION      = "session"
const string HIST_DESCRIPTION  = "description"
const string HIST_OLD_VALUE    = "oldvalue"
const string HIST_NEW_VALUE    = "newvalue"
const string HIST_TYPE         = "type"
const string HIST_LOCKED       = "locked"

// data structure for object history
Skip skipObjects        = create
Skip skipNumRecords     = create

// data structure for module history
Skip skipModuleHistory  = create
int  numModuleRecords   = 0

// data structure for session history
Skip skipSessionHistory = create
int  numSessionRecords  = 0

Module  currMod               = null
Object  currObj               = null
int     currObjNumber         = 0

/****************************
	printObjectHistory

	Use for degug only. Prints all data structures.
****************************/
void printObjectHistory()
{
	Object      o           = null
	Skip        skipHistory = null
	DxlObject   hist        = null
	HistoryType historyType = null
	int         objNo       = 0
	int         rec         = 0

	for skipHistory in skipObjects do
	{
		// get object number
		objNo = (int key skipObjects)

		// lookup number of history records stored for this object
		find(skipNumRecords, objNo, rec)

		print("Object " (int key skipObjects) " has " rec " history records\n")

		for hist in skipHistory do
		{
			print("\tHistory record " (int key skipHistory) "\n")

			if (!(bool hist->HIST_LOCKED))
			{
				print("\t\tDate: " (Date hist->HIST_DATE) "\n")
				print("\t\tAuthor: " (string hist->HIST_AUTHOR) "\n")
				print("\t\tSession: " (int hist->HIST_SESSION) "\n")
				print("\t\tBaseline: " (string hist->HIST_BASELINE) "\n")

				historyType = (HistoryType hist->HIST_TYPE)

				print("\t\tDescription: " (string hist->HIST_DESCRIPTION) "\n")
				print("\t\t\tFrom: " (string hist->HIST_OLD_VALUE) "\n")
				print("\t\t\tTo: " (string hist->HIST_NEW_VALUE) "\n")
			}
			else
			{
				print("Read Locked Data\n")
			}
		}
		return
	}
}

/****************************
	isObjectOperation
****************************/
bool isObjectOperation(HistoryType historyType)
{
    return((historyType == createObject)   ||
           (historyType == copyObject)     ||
           (historyType == deleteObject)   ||
           (historyType == unDeleteObject) ||
           (historyType == purgeObject)    ||
           (historyType == clipCutObject)  ||
           (historyType == clipCopyObject) ||
           (historyType == createLink)     ||
           (historyType == modifyLink)     ||
           (historyType == deleteLink)     ||
           (historyType == insertOLE)      ||
           (historyType == removeOLE)      ||
           (historyType == changeOLE)      ||
           (historyType == pasteOLE))
}

/****************************
	isModuleOperation

	This function deliberately includes the deleteObject and purgeObject
	operations as these do not appear in the object history.

	modifyObject is required for changes to module level attributes.
****************************/
bool isModuleOperation(HistoryType historyType)
{
	return(historyType == createModule ||
	       historyType == baselineModule ||
	       historyType == partitionModule ||
	       historyType == acceptModule ||
	       historyType == rejoinModule ||
	       historyType == returnModule ||
	       historyType == commentModule ||
	       historyType == createAttr ||
	       historyType == modifyAttr ||
	       historyType == deleteAttr ||
	       historyType == createType ||
	       historyType == modifyType ||
	       historyType == deleteType ||
	       historyType == deleteObject ||
	       historyType == purgeObject ||
	       historyType == modifyObject)
}

/****************************
	getObjectHistory
****************************/
void getObjectHistory(Module m, string currBaseline)
{
	Object      o            = null
	int         objNo        = 0
	int         rec          = 0
	bool        locked       = false
	History     h            = null
	HistoryType historyType  = null

	// loop through all objects in the module an extract object history
	for o in entire(m) do
	{
		objNo = o."Absolute Number"

		// skip list to hold history records for the object
		Skip skipHistory = null

		// see if the history list already exists, if not then create it
		if (find(skipObjects, objNo, skipHistory))
		{
			//print("skipHistory found for object " objNo "\n")
			// skip list exists - find next index to use
			if (find(skipNumRecords, objNo, rec))
			{
				//print("numrecords found = " rec "\n")
			}
			//rec++
		}
		else
		{
			skipHistory = create

			// new skip list, so first entry will be indexed at zero
			rec = 0
		}

		// loop through history records for the object
		for h in o do
		{
			// create structure to hold details of the history record
			DxlObject hist = new

			// history record may be read-locked
			locked = h.readlocked
			hist->HIST_LOCKED = locked

			// record the type
			historyType = h.type
			hist->HIST_TYPE = historyType

			// if it is not locked we can read the details
			if (!locked)
			{
				hist->HIST_DATE = h.date
				hist->HIST_AUTHOR = h.author
				hist->HIST_SESSION = h.sessionNo
				hist->HIST_BASELINE = currBaseline
				//hist->HIST_DETAILS = "<no further details>"

				if (historyType == modifyObject)
				{
					// object modification has old and new values
					hist->HIST_DESCRIPTION = "Modify Attribute: " h.attrName

					// TBD use buffers to hold old and new values.
					hist->HIST_OLD_VALUE = h.oldValue
					hist->HIST_NEW_VALUE = h.newValue
				}
				else if (historyType == moveObject or historyType == clipMoveObject)
				{
					// object was moved or copied and pasted
					hist->HIST_DESCRIPTION = "Move object from " h.position " to " h.newPosition ""
				}
				else if (historyType == clipCopyObject)
				{
					// object was created by copying another
					hist->HIST_DESCRIPTION = "Copy object from " h.oldAbsNo " to " h.absNo ""
				}
				else if (historyType == commentObject)
				{
					hist->HIST_DESCRIPTION = "Comment"
					hist->HIST_NEW_VALUE = h.newValue
				}
				else if (isObjectOperation(historyType))
				{
					hist->HIST_DESCRIPTION = goodStringOf(historyType)
				}
				else
				{
					hist->HIST_DESCRIPTION = "<unknown history type>"
				}
			}

			// add history structure to list of history records for this object
			put(skipHistory, rec++, hist)
		}

		// store the number of history records in the list
		delete(skipNumRecords, objNo)
		put(skipNumRecords, objNo, rec)

		// add list of history records for this object to the object list
		delete(skipObjects, objNo)
		put(skipObjects, objNo, skipHistory)
	}
}

/****************************
	getModuleHistory
****************************/
void getModuleHistory(Module m, string currBaseline)
{
	int         rec          = 0
	bool        locked       = false
	string      modification = ""
	History     h            = null
	HistoryType historyType  = null

	// loop through history records (this loops through ALL history
	// because we want the object delete and purge history records
	// in addition to the module specific history records.
	for h in m do
	{
		historyType = h.type

		if (!isModuleOperation(historyType))
		{
			continue
		}

		// filter out modifyObject records that are not for module level attributes
		if (historyType == modifyObject && h.absNo != 0)
		{
			continue
		}

		// create structure to hold details of the history record
		DxlObject hist = new

		// record the type
		hist->HIST_TYPE = historyType

		// history record may be read-locked
		locked = h.readlocked
		hist->HIST_LOCKED = locked

		// if it is not locked we can read the details
		if (!locked)
		{
			hist->HIST_DATE = h.date
			hist->HIST_AUTHOR = h.author
			hist->HIST_SESSION = h.sessionNo
			hist->HIST_BASELINE = currBaseline
			//hist->HIST_DETAILS = "<no further details>"

			modification = goodStringOf(historyType)
			hist->HIST_DESCRIPTION = modification

			if (historyType == createAttr || historyType == modifyAttr || historyType == deleteAttr)
			{
				// attribute modification
				hist->HIST_DESCRIPTION = modification ": " h.attrName
			}
			else if (historyType == createType || historyType == modifyType || historyType == deleteType)
			{
				// attribute type modification
				hist->HIST_DESCRIPTION = modification ": " h.typeName
			}
			else if (historyType == commentModule)
			{
				hist->HIST_DESCRIPTION = "Comment"
				hist->HIST_NEW_VALUE = h.newValue
			}
			else if (historyType == deleteObject || historyType == purgeObject)
			{
				hist->HIST_DESCRIPTION = modification ": " h.absNo ""
			}
			else if (historyType == modifyObject && (h.absNo == 0))
			{
				// it seems that modifications to module level attributes are stored
				// as modifyObject history records with an absolute number of zero.
				hist->HIST_DESCRIPTION = "Modify Attribute: " h.attrName

				// TBD use buffers to hold old and new values.
				hist->HIST_OLD_VALUE = h.oldValue
				hist->HIST_NEW_VALUE = h.newValue
			}
		}

		// store history structure in skip list
		put(skipModuleHistory, numModuleRecords++, hist)
	}
}

/****************************
	getSessionHistory
****************************/
void getSessionHistory(Module m)
{
	HistorySession hs        = null
	string         sBaseline = ""

	for hs in m do
	{
		// create structure to hold details of the history record
		DxlObject hist = new

		hist->HIST_DATE = when(hs)
		hist->HIST_AUTHOR = who(hs) 

		// Sessions are 0-based in the core;  the 1+ here is to account for the fact that
        // number(s) uses this 0-based scheme whereas sessionNo does
        // this 1+ internally.
		hist->HIST_SESSION = number(hs) + 1

		sBaseline = baseline(hs)

		if (!null sBaseline)
		{
			hist->HIST_BASELINE = sBaseline
			hist->HIST_DESCRIPTION = "Created baseline " sBaseline
		}			

		//hist->HIST_DETAILS = "<no details>"

		put(skipSessionHistory, numSessionRecords++, hist)
	}
}

/****************************
	getHistory
****************************/
void getHistory(Module m)
{
	string      currBaseline = ""
	string      s            = ""
	Baseline    b            = null

	// extract baseline information
	if (isBaseline(m))
	{
		b = baselineInfo(m)

		// only display the suffix if is is not empty
		s = (suffix b) ""

		if (s != "")
		{
			s = " (" s ")"
		}

		currBaseline = (major b) "." (minor b) "" s
	}
	else
	{
		currBaseline = "current"
	}

	// get object history records
	getObjectHistory(m, currBaseline)

	// get module history records
	getModuleHistory(m, currBaseline)
}

// listview column indices
const int HIST_AUTHOR_COLUMN        = 0
const int HIST_SESSION_COLUMN       = 1
const int HIST_DATE_COLUMN          = 2
const int HIST_BASELINE_COLUMN      = 3
const int HIST_DESCRIPTION_COLUMN   = 4
const int HIST_RECORD_COLUMN        = 5

// tab indices
const int TAB_OBJECT   = 0
const int TAB_MODULE   = 1
const int TAB_SESSIONS = 2

int selectedTab = TAB_OBJECT

// main dialog
DB      dbMain                = null
DBE     dbeTab                = null

// elements
DBE     dbeLabel        = null
DBE     dbeListView     = null
DBE     dbeDetailsFrame = null
DBE     dbeOldValue     = null
DBE     dbeNewValue       = null
DBE     btnPrev         = null
DBE     btnNext         = null

string  columnTitles[]        = {}

string tabLabels[] = { "Object", "Module", "Sessions" }

string details = ""

//Trigger g_mTrigger            = null
Trigger preSyncTrigger        = null
Trigger postSyncTrigger       = null

Skip skipOldValues = null
Skip skipNewValues = null

/****************************
	clearDetailSkips
****************************/
void clearDetailSkips()
{
	// dynamically store the old and new values in a skip list, referenced by
	// history record number. this allows us to look up the values for display
	// when the user selects an entry in the listview.
	if (!null skipOldValues)
	{
		delete(skipOldValues)
	}
	skipOldValues = create

	if (!null skipNewValues)
	{
		delete(skipNewValues)
	}
	skipNewValues = create
}

/****************************
	showObjectHistory

	Update the dialog with information read from the data structures.
****************************/
void showObjectHistory()
{
	Skip        skipHistory = null
	DxlObject   hist        = null
	HistoryType historyType = null
	int         objNo       = 0
	int         rec         = 0

	objNo = currObj."Absolute Number"

	clearDetailSkips()

	// check that there is an entry for this object in the list
	if (find(skipObjects, objNo, skipHistory) && find(skipNumRecords, objNo, rec))
	{
		if (rec > 0)
		{
			// there are history records for this object
			for hist in skipHistory do
			{
				rec = noElems(dbeListView)

				// insert a row in the list view
				insert(dbeListView, rec, "", iconNone)

				historyType = (HistoryType hist->HIST_TYPE)

				if (!(bool hist->HIST_LOCKED))
				{
					set(dbeListView, rec, HIST_AUTHOR_COLUMN,       (string hist->HIST_AUTHOR))
					set(dbeListView, rec, HIST_SESSION_COLUMN,      (int hist->HIST_SESSION) "")
					set(dbeListView, rec, HIST_DATE_COLUMN,         stringOf(Date hist->HIST_DATE) "")
					set(dbeListView, rec, HIST_BASELINE_COLUMN,     (string hist->HIST_BASELINE))
					set(dbeListView, rec, HIST_DESCRIPTION_COLUMN,  (string hist->HIST_DESCRIPTION))

					// used as an index to the details lists
					set(dbeListView, rec, HIST_RECORD_COLUMN,       rec "")

					// store details in lists - these can then be retrieved when user selects
					// an entry in the listview
					put(skipOldValues, rec, (string hist->HIST_OLD_VALUE))
					put(skipNewValues, rec, (string hist->HIST_NEW_VALUE))
				}
				else
				{
					set(dbeListView, rec, HIST_DESCRIPTION_COLUMN,  goodStringOf(historyType) ": <Read Locked Data>")
					set(dbeListView, rec, HIST_AUTHOR_COLUMN,       "<Read Locked Data>")
					set(dbeListView, rec, HIST_SESSION_COLUMN,      "<Read Locked Data>")
					set(dbeListView, rec, HIST_DATE_COLUMN,         "<Read Locked Data>")
					set(dbeListView, rec, HIST_BASELINE_COLUMN,     "<Read Locked Data>")
					set(dbeListView, rec, HIST_RECORD_COLUMN,       rec "")
				}
			}
		}
		else
		{
			// there are no history records for this object
			insert(dbeListView, 0, "", iconNone)
			set(dbeListView, 0, HIST_DATE_COLUMN, "<No history found>" "")
		}

	}
	else
	{
		// there is no entry for this object
		insert(dbeListView, 0, "", iconNone)
		set(dbeListView, 0, HIST_DESCRIPTION_COLUMN, "<Object not found>")
	}
}

/****************************
	showModuleHistory

	Update the dialog with information read from the data structures.
****************************/
void showModuleHistory()
{
	DxlObject   hist        = null
	HistoryType historyType = null
	int         rec         = 0

	clearDetailSkips()

	if (numModuleRecords > 0)
	{
		// there are history records for this object
		for hist in skipModuleHistory do
		{
			rec = noElems(dbeListView)

			// insert a row in the list view
			insert(dbeListView, rec, "", iconNone)

			historyType = (HistoryType hist->HIST_TYPE)

			if (!(bool hist->HIST_LOCKED))
			{
				set(dbeListView, rec, HIST_AUTHOR_COLUMN,       (string hist->HIST_AUTHOR))
				set(dbeListView, rec, HIST_SESSION_COLUMN,      (int hist->HIST_SESSION) "")
				set(dbeListView, rec, HIST_DATE_COLUMN,         stringOf(Date hist->HIST_DATE) "")
				set(dbeListView, rec, HIST_BASELINE_COLUMN,     (string hist->HIST_BASELINE))
				set(dbeListView, rec, HIST_DESCRIPTION_COLUMN,  (string hist->HIST_DESCRIPTION))
				set(dbeListView, rec, HIST_RECORD_COLUMN,       rec "")

				// store details in lists - these can then be retrieved when user selects
				// an entry in the listview
				put(skipOldValues, rec, (string hist->HIST_OLD_VALUE))
				put(skipNewValues, rec, (string hist->HIST_NEW_VALUE))

			}
			else
			{
				set(dbeListView, rec, HIST_DESCRIPTION_COLUMN,  goodStringOf(historyType) ": <Read Locked Data>")
				set(dbeListView, rec, HIST_AUTHOR_COLUMN,       "<Read Locked Data>")
				set(dbeListView, rec, HIST_SESSION_COLUMN,      "<Read Locked Data>")
				set(dbeListView, rec, HIST_DATE_COLUMN,         "<Read Locked Data>")
				set(dbeListView, rec, HIST_BASELINE_COLUMN,     "<Read Locked Data>")
				set(dbeListView, rec, HIST_RECORD_COLUMN,       rec "")
			}
		}
	}
	else
	{
		// there are no history records for this object
		insert(dbeListView, 0, "", iconNone)
		set(dbeListView, 0, HIST_DATE_COLUMN, "<No history found>")
	}

}

/****************************
	showSessionHistory

	Update the dialog with information read from the data structures.
****************************/
void showSessionHistory()
{
	DxlObject   hist        = null
	int         rec         = 0

	clearDetailSkips()

	if (numSessionRecords > 0)
	{
		// there are history records for this object
		for hist in skipSessionHistory do
		{
			rec = noElems(dbeListView)

			// insert a row in the list view
			insert(dbeListView, rec, "", iconNone)

			set(dbeListView, rec, HIST_AUTHOR_COLUMN,       (string hist->HIST_AUTHOR))
			set(dbeListView, rec, HIST_SESSION_COLUMN,      (int hist->HIST_SESSION) "")
			set(dbeListView, rec, HIST_DATE_COLUMN,         stringOf(Date hist->HIST_DATE) "")
			set(dbeListView, rec, HIST_BASELINE_COLUMN,     (string hist->HIST_BASELINE))
			set(dbeListView, rec, HIST_DESCRIPTION_COLUMN,  (string hist->HIST_DESCRIPTION))
		}
	}
	else
	{
		// there are no history records for this object
		insert(dbeListView, 0, "", iconNone)
		set(dbeListView, 0, HIST_DATE_COLUMN, "<No sessions found>")
	}
}

/****************************
	getBaselineHistory
****************************/
bool getBaselineHistory(Skip skipBaselines)
{
	Baseline thisBasline      = null
	Module   moduleBaseline   = null
	int      bCount           = 0
	int      numBaselines     = 0
	bool     autoShutdown     = false

	// count baselines for progress
	for thisBasline in skipBaselines do
	{
		numBaselines++
		//print("Baseline " (major b) "." (minor b) (suffix b) " " (user b) " " (dateOf b) " " (annotation b) "\n")
	}

	progressStart(dbMain, "Please Wait", "Reading history from baselines", numBaselines)

	// load history from current version
	getHistory(currMod)

	// get session history records
	getSessionHistory(currMod)

	// loop through list of baselines
	for thisBasline in skipBaselines do
	{
		progressStep(++bCount)
		progressMessage("Reading history from baseline " bCount " of " numBaselines "")

		if (progressCancelled)
		{
            if (confirm("Are you sure you want to cancel?"))
            {
	            progressStop
	            //delete(skipBaselines)
                return(false)
            }
        }

		// open the baseline in the background
		moduleBaseline = load(currMod, thisBasline, false)

		if (null moduleBaseline)
		{
			print("ERROR opening baseline " (major thisBasline) "." (minor thisBasline) " (" (suffix thisBaseline) ")\n")
			continue
		}

		// read history from baseline
		getHistory(moduleBaseline)

		close(moduleBaseline)
	}

	progressStop()

	return(true)
}

/****************************
	doCancel

	Callback on Cancel button on details dialog
****************************/
void doCancel(DB db)
{
	//release(dbDetails)
}

/****************************
	doHelp

	Callback on help button.
****************************/
void doHelp(DB db)
{

}

/****************************
	refreshDisplay
****************************/
void refreshDisplay()
{
	// refresh current selected object
	(current ModuleRef__) = currMod
    (current ObjectRef__) = currObj

    refresh(currMod)

    empty(dbeListView)
    set(dbeOldValue, "")
    set(dbeNewValue, "")

    if (selectedTab == TAB_OBJECT)
    {
    	// update dialog to show history records for the selected object
    	set(dbeLabel, "History for Object " identifier(currObj))

    	showObjectHistory()
    	show(btnPrev)
		show(btnNext)
		//show(btnDetails)
		//inactive(btnDetails)
	}
	else if (selectedTab == TAB_MODULE)
	{
		set(dbeLabel, "History for Module " fullName(currMod))

		showModuleHistory()
		hide(btnPrev)
		hide(btnNext)
		//hide(btnDetails)
	}
	else if (selectedTab == TAB_SESSIONS)
	{
		set(dbeLabel, "Session history for Module " fullName(currMod))

		showSessionHistory()
		hide(btnPrev)
		hide(btnNext)
		//hide(btnDetails)
	}
}

/****************************
	preSyncFn
****************************/
bool preSyncFn(Trigger t)
{
	// ensure correct current object and module BEFORE object sync
    (current ModuleRef__) = currMod
    (current ObjectRef__) = currObj

    return(true)
}

/****************************
	postSyncFn
****************************/
void postSyncFn(Trigger t)
{
	// we can safely do this because current module and object were
	// set BEFORE object sync.
	//print("post sync fired\n")

    currObj = current Object
    refreshDisplay()
}

/****************************
	doClose
****************************/
void doClose(DB db)
{
	//if (!null g_mTrigger)
	//{
	//	delete g_mTrigger
	//}

	if (!null preSyncTrigger)
	{
        delete preSyncTrigger
    }

    if (!null postSyncTrigger)
    {
        delete postSyncTrigger
    }

    // TBD delete data astructures

	hide(dbMain)
	destroy(dbMain)
	dbMain = null
}

/****************************
	fnTrigger
****************************/
bool fnTrigger(Trigger xx)
{
    doClose(dbMain)

    return true
}

/****************************
	doPrev
****************************/
void doPrev(DBE dbe)
{
	Object oPrev = previous currObj

    if (oPrev != null)
    {
        currObj = oPrev
        currObjNumber = currObj."Absolute Number"
        refreshDisplay()
    }
}

/****************************
	doNext
****************************/
void doNext(DBE dbe)
{
	Object oNext = next currObj

    if (oNext != null)
    {
        currObj = oNext
        currObjNumber = currObj."Absolute Number"
        refreshDisplay()
    }
}

/****************************
	doSelect

	In shareable edit mode the user may have moved to an object that is not
	locked. Therefore we must check that the current object is locked for edit.
****************************/
void doSelect(DBE dbe, int iSelected)
{
	string sNew = ""
	string sOld = ""

	// get record number from listview - this gives an index to old and new values lists
	int record = intOf(getColumnValue(dbeListView, iSelected, HIST_RECORD_COLUMN))

	if (find(skipNewValues, record, sNew))
	{
		set(dbeNewValue, sNew)

		if (find(skipOldValues, record, sOld))
		{
			set(dbeOldValue, sOld)
		}
	}
}

/****************************
	doUnselect
****************************/
void doUnselect(DBE dbe, int iSelected)
{
	set(dbeOldValue, "")
	set(dbeNewValue, "")
}

/****************************
	doActivate

	Callback when user double-clicks on entry in the list view
****************************/
void doActivate(DBE dbe, int iSelected)
{
    //doDetails(dbeListView)
}

/****************************
	doTabSelect
****************************/
void doTabSelect(DBE dbe)
{
	selectedTab = get(dbeTab)

	refreshDisplay()
}

/****************************
	showHistoryDialog
****************************/
void showHistoryDialog(Skip skip)
{
	string dispString = ""

	currMod = current Module

	// trigger to close dialog when module closes
	//g_mTrigger = trigger(module, close, 10, fnTrigger)

	dbMain = smartDialog(currMod, "Smart History Viewer", styleCentered | styleFloating)

	dbeTab = tab(dbMain, tabLabels, 690, 440, doTabSelect)
	dbeTab->"bottom"->"form"
	dbeTab->"right"->"form"

	dbeLabel = label(dbMain, "History for Object " identifier(currObj))
	dbeLabel->"top"->"inside"->dbeTab
	dbeLabel->"left"->"inside"->dbeTab

	dbeListView  = listView(dbMain, listViewOptionSortText, 650, 10, columnTitles)

	dbeListView->"top"->"spaced"->dbeLabel
    dbeListView->"left"->"inside"->dbeTab
    dbeListView->"right"->"inside"->dbeTab
    dbeListView->"bottom"->"unattached"

    dbeDetailsFrame = frame(dbMain, "Details of selected history record", 650, 100)
    dbeDetailsFrame->"top"->"spaced"->dbeListView
    dbeDetailsFrame->"left"->"inside"->dbeTab
    dbeDetailsFrame->"right"->"inside"->dbeTab
    dbeDetailsFrame->"bottom"->"unattached"

    dbeOldValue = richText(dbMain, "Old value", "", 325, 100, true)
    dbeOldValue->"top"->"inside"->dbeDetailsFrame
    dbeOldValue->"left"->"inside"->dbeDetailsFrame
    dbeOldValue->"right"->"unattached"
    dbeOldValue->"bottom"->"inside"->dbeDetailsFrame

    dbeNewValue = richText(dbMain, "New value", "", 325, 100, true)
    dbeNewValue->"top"->"inside"->dbeDetailsFrame
    dbeNewValue->"left"->"spaced"->dbeOldValue
    dbeNewValue->"right"->"inside"->dbeDetailsFrame
    dbeNewValue->"bottom"->"inside"->dbeDetailsFrame

	btnPrev = button(dbMain, "< Previous", doPrev, styleStandardSize)
    btnPrev->"top"->"spaced"->dbeDetailsFrame
    btnPrev->"left"->"inside"->dbeTab
    btnPrev->"bottom"->"inside"->dbeTab
    btnPrev->"right"->"unattached"

    // Next button
    btnNext = button(dbMain, "Next >", doNext, styleStandardSize)
    btnNext->"top"->"spaced"->dbeDetailsFrame
    btnNext->"left"->"spaced"->btnPrev
    btnNext->"bottom"->"inside"->dbeTab
    btnNext->"right"->"unattached"

	// hide the close button, but associate callback with dialog
	close(dbMain, false, doClose)
    // Help button
    ok(dbMain, "Close", doClose)
    apply(dbMain, "Help", doHelp)

	realize dbMain

	insertColumn(dbeListView, HIST_AUTHOR_COLUMN,        "Author",        120, null)
    insertColumn(dbeListView, HIST_SESSION_COLUMN,       "Session",        50, null)
    insertColumn(dbeListView, HIST_DATE_COLUMN,          "Date",          120, null)
    insertColumn(dbeListView, HIST_BASELINE_COLUMN,      "Baseline",       70, null)
    insertColumn(dbeListView, HIST_DESCRIPTION_COLUMN,   "Description",   250, null)
    insertColumn(dbeListView, HIST_RECORD_COLUMN,        "Record",         50, null)

	// associate sort callbacks with each column
	set(dbeListView, HIST_AUTHOR_COLUMN, doSortString)
	set(dbeListView, HIST_SESSION_COLUMN, doSortInteger)
	set(dbeListView, HIST_DATE_COLUMN, doSortDate)
	set(dbeListView, HIST_BASELINE_COLUMN, doSortString)
	set(dbeListView, HIST_DESCRIPTION_COLUMN, doSortString)
	set(dbeListView, HIST_RECORD_COLUMN, doSortInteger)

	setSortColumn(dbeListView, HIST_DATE_COLUMN)

	// callbacks on listview
	set(dbeListView, doSelect, doUnselect, doActivate)

	// resizing
	setExtraHeightShare(dbeListView, 1.0)
    setExtraWidthShare(dbeListView, 1.0)
    setExtraWidthShare(dbeOldValue, 0.5)
    setExtraWidthShare(dbeNewValue, 0.5)

	// load history from baselines
	if (getBaselineHistory(skip))
	{
		//printObjectHistory()

		// history loaded successfully - refresh display
		refreshDisplay()

		// It is important to reset the current module before creating the triggers
		(current ModuleRef__) = currMod

		// set up triggers for object sync
		if (!null preSyncTrigger)
		{
	        delete preSyncTrigger
	    }
	    preSyncTrigger = trigger(module->object, sync, 10, preSyncFn)

	    if (!null postSyncTrigger)
	    {
	        delete postSyncTrigger
	    }

	    postSyncTrigger = trigger(module->object, sync , 10, postSyncFn)

		show(dbMain)
	}
	else
	{
		// loading of history was cancelled by user
		hide(dbMain)
		destroy(dbMain)
		dbMain = null
	}
}

/****************************
	MAIN
****************************/
currMod = current Module
currObj = current Object

if (null currMod)
{
	infoBox("This utility can only be run from a formal module.")
	halt
}

if (null currObj)
{
	infoBox("No current Object!")
	halt
}

Skip skip = create

// lauch dialog for user to select baselines
if (!bsGetUserSelectedBaselines(currMod,
                                skip,
                                "Smart History Viewer",
                                "Please select the baselines for which you would like to view history.\n" //-
                                "Note that loading numerous baselines may take some time.",
                                true))
{
	halt
}

showHistoryDialog(skip)
Posted in Dialogs, History | Comments closed

Enhanced makeDir Function

Wrapper for mkdir that allows you to create a whole new path, not just a single directory. It also traps and reports errors generated by mkdir.

This is more useful than the builtin mkdir function that will throw a runtime error if the full path to the new directory does not already exist.

Usage:

string makeDir(string dirName)

Example:

string errors = ""
errors = makeDir("C:\\Temp\\tom\\dick\\harry")
print errors "\n"

Source code:

//  makeDir

/*
	Wrapper for mkdir that allows you to create a whole new path, not just
	a single directory.

	Usage:

		string makeDir(string dirName)

	Tony Goodman  23 June 2005
*/

/*************************************
	isDirectory

	Returns TRUE if string parameter is a valid directory.
**************************************/
bool isDirectory(string dn)
{
	Stat s = create dn

	if (null s) return false

	if (directory s)
	{
		delete s
		return true
	}
	delete s
	return false
}

/************************************
	getParentDirectory

	Returns parent directory of the given file or directory name.
*************************************/
string getParentDirectory(string fname)
{
    int i = (length fname) - 1

    while (i > 0)
    {
		if ((fname[i] == '\\') || (fname[i] == '/'))
		{
		    return fname[0:i - 1]
    	}

	    i--
    }

    return ""
}

/*************************************
	makeDir

	Creates a new directory path dPath.

	On success returns an empty string.
	On error returns an error message.
***************************************/
string makeDir(string dPath)
{
	string res     = ""
	string dParent = ""  

	// see if the directory already exists
	if (isDirectory(dPath))
	{
		return("Directory already exists!")
	}

	// get name of parent directory
	dParent = getParentDirectory(dPath)

	// check to see if parent exists
	if ((!isDirectory(dParent)) && (!null dParent))
	{
		// recurse
		res = makeDir(dParent)

	}

	if ((isDirectory(dParent)) || (dParent == null))
	{
		// parent directory exists so we can create dPath using mkdir
		// trap errors
		noError

		mkdir(dPath)

		res = lastError
	}

	return(res)
}
Posted in Files and Directories, System | Comments closed

Directory Selector

There is no built-in mechanism in DXL to create a dialog box element for capturing a directory name. This utility function provides you with functionality similar to the fileName perm, except that the selector window allows the user to select a directory rather than a file.

directoryName creates an element inside the specified dialog box for capturing a directory name. This consists of a field for the directory name and a Browse button to invoke a directory selector window. This function can be used in place of fileName whenever you want to capture a directory rather than a file.

Usage:

DBE directoryName(DB box, string label, string initial, bool readOnly)

Example:

#include "directoryName.inc"

DB db
DBE dbe

db = create("Demonstrate Directory Selector")

dbeDirectory = directoryName(db, "c:/Temp", "", false)

realize(db)
show(db)

Source code:

//  Directory Browser Facility

/*
    Directory Browser Facility.
    Adapted from Telelogic Kitchen.

    provides directoryName() which may be used in place of fileName()
    when browsing for a directory rather than a file.

    smartDXL.com
*/

DB     dnParentDialog = null

const string S_DRIVE_IS_EMPTY  = "This drive has no folders."

const string POSSIBLE_DRIVES[] = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
                                  "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}

/************************************
	Field and Browse button that are created on the parent dialog box.
************************************/
DBE dbeBrowse         = null
DBE dbeDirectory      = null

/************************************
	Directory browser dialog
************************************/
DB  dbDirectorySelect = null
DBE dbeDirectoryTree  = null
DBE dbeDriveChoice    = null
DBE lblUserPrompt     = null
DBE dbeSelectOK       = null
DBE dbeSelectCancel   = null

string actualDrives[] = {"", "", "", "", "", "", "", "", "", "", "", "", "",
                         "", "", "", "", "", "", "", "", "", "", "", "", ""}
int numDrives = 0

string selectedDirectory = null

/************************************
	convertToBackSlashes

	Converts a directory path into a form suitable for the tree view DBE.
************************************/
string convertToBackSlashes(string s)
{
    string sRetval = null

    if (!isVoid_(s))
    {
        int i

        for (i = 0; i < length(s); i++)
        {
            sRetval = sRetval ((s[i] == '/') ? '\\' : s[i]) ""
        }
    }

    return sRetval
}

/************************************
	convertToForwardSlashes

	Converts a tree view entry into a valid directory path.
************************************/
string convertToForwardSlashes(string s)
{
    string sRetval = null

    if (!isVoid_(s))
    {
        int i

        for (i = 0; i < length(s); i++)
        {
            sRetval = sRetval ((s[i] == '\\') ? '/' : s[i]) ""
        }
    }

    return sRetval
}

/************************************
	getAvailableDrives

	Determines the list of available drives.
************************************/
int getAvailableDrives()
{
    int    cDrive  = 0
    int    i       = 0
    string sDrive  = ""
    string sErrMsg = ""

    // turn off error reporting
    noError

    // go through all possible drives
    for (i = 0; i < sizeof(POSSIBLE_DRIVES); i++)
    {
        sDrive = POSSIBLE_DRIVES[i] ":\\"

        if (sDrive == "A:\\")
        {
            // add to array
            actualDrives[numDrives++] = sDrive
        }
        else
        {
            Stat stat = create sDrive

            if (stat != null)
            {
                // this check is probably redundant
                if (directory stat)
                {
                    // add to array
                    actualDrives[numDrives] = sDrive

                    if (sDrive == "C:\\")
                    {
	                    cDrive = numDrives
                    }

                    numDrives++
                }

                delete stat
            }
        }
    }

    // turn error reporting back on
    sErrMsg = lastError

    return(cDrive)
}

/************************************
	doExpandDirectory
************************************/
bool doExpandDirectory(DBE db, string sTreeEntry)
{
	int    numSubDirs      = 0
    string s               = ""
    string sDummyEntry     = ""
    string sDirectory      = ""
    string f               = ""
	string sNewTreeEntry   = ""
	string sNewDummyEntry  = ""
	string sDriveIsEmpty   = ""
	string sErrMsg         = ""
	string sFullPath       = ""
	Stat   fStat           = null

    // convert backslashes
    sDirectory = convertToBackSlashes(sTreeEntry)

    sDummyEntry = sTreeEntry "/" dummyItem

    // turn off error reporting
    noError

    // process contents of directory
    for s in directory sDirectory do
    {
        // exclude
        if ( s == "." || s == ".." || isVoid_(s) )
        {
            continue
        }

        // get absolute location
        sFullPath = sDirectory FileSep_ s

        // it is possible to exceed max windows path if you use drive maps
        if (platform == "WIN32" && length(sFullPath) > 255)
        {
            continue
        }

        Stat stat = create sFullPath

        if (stat != null)
        {
            // only relevant for directories
            if (directory stat)
            {
                // count subdirectorys
                numSubDirs = 0

                for f in directory sFullPath do
                {
                    fStat = create sFullPath FileSep_ f

                    if (null fStat || !directory(fStat) || f == "." || f == "..")
                    {
	                    continue
                    }

                    numSubDirs++
                }

                sNewTreeEntry  = sTreeEntry "/" s
                sNewDummyEntry = sNewTreeEntry "/" dummyItem

                // check for dummy entry
                if (exists(db, sDummyEntry))
                {
                    //This entry was only added so that a '+' was displayed against a new directory (i.e. pre-expansion).
                    delete(db, sDummyEntry)
                }

                sDriveIsEmpty = sTreeEntry "/" S_DRIVE_IS_EMPTY

                // check for dummy entry
                if (exists(db, sDriveIsEmpty))
                {
                    delete(db, sDriveIsEmpty)
                }

                // only relevant if entry does not already exist
                if (!exists(db, sNewTreeEntry))
                {
                    // add to tree view
                    insert(db, sNewTreeEntry,  iconFolder, iconFolderOpen)

                    if (numSubDirs > 0)
                    {
	                    insert(db, sNewDummyEntry, iconFolder, iconFolderOpen)
                    }
                }
            }

            delete(stat)
        }
    }

    // turn error reporting back on
    sErrMsg = lastError

    return(true)
}

/************************************
	doSelectDrive
************************************/
void doSelectDrive(DBE db)
{
    string sDrive = get db

    // remove trailing slash
    if (sDrive[length(sDrive) - 1] == '\\')
    {
        sDrive = sDrive[0 : length(sDrive) - 2]
    }

    // clear existing structure
    empty(dbeDirectoryTree)

    // add drive and dummy entry
    insert(dbeDirectoryTree, sDrive, iconFolder, iconFolderOpen)
    insert(dbeDirectoryTree, (sDrive "/" S_DRIVE_IS_EMPTY), iconNone, iconNone)
}

/************************************
	doSelectOK

	Callback for dbeSelectOk
************************************/
void doSelectOK(DB db)
{
    selectedDirectory = convertToBackSlashes(get dbeDirectoryTree)

    // only relevant for root directories
    if (length(selectedDirectory) == 2)
    {
        // add trailing slash
        selectedDirectory = selectedDirectory "\\"
    }

    // update directory field on parent dialog
    set(dbeDirectory, selectedDirectory)	

    release dbDirectorySelect

}

/************************************
	doSelectCancel
************************************/
void doSelectCancel(DB db)
{
    selectedDirectory = null

    release dbDirectorySelect
}

/************************************
	doBrowse
************************************/
void doBrowse(DBE dbe)
{
	int cDriveIdx = 0

	// initialise available drives array and return index to C: drive
    cDriveIdx = getAvailableDrives()

    // Create Directory Browser dialog
    dbDirectorySelect = create(dnParentDialog, "Directory Selector", styleSubWindow)

  	// drop-down of available drives - set to drive C:\ by default
	dbeDriveChoice = choice(dbDirectorySelect, "Drive: ", actualDrives, numDrives, cDriveIdx)

    // directory tree
    dbeDirectoryTree = treeView(dbDirectorySelect, treeViewOptionSorted, 400, 15)

    close(dbDirectorySelect, true, doSelectCancel)
	apply(dbDirectorySelect, "OK", doSelectOK)

    realize dbDirectorySelect

    set(dbeDirectoryTree, doExpandDirectory)
    set(dbeDriveChoice, doSelectDrive)

    // initialise directory tree according to default drive setting
    doSelectDrive(dbeDriveChoice)

	// show the directory browser dialog
	block dbDirectorySelect
	hide dbDirectorySelect
	destroy dbDirectorySelect
}

/************************************
	directoryName
************************************/
DBE directoryName(DB     dbParent,
                  string sLabel,
                  string initDirectory,
                  bool   readOnly)
{
    // use default if parameter is null
    if (dbParent == null)
    {
        dnParentDialog = dbExplorer
    }
    else
    {
	    dnParentDialog = dbParent
	}

	// Create Field and Browse button on parent dialog
    dbeDirectory = field(dbParent, sLabel, initDirectory, 40, readOnly)
    beside(dbParent)
   	dbeBrowse = button(dbParent, "Browse...", doBrowse)
   	left(dbParent)

    // return handle on directory field dbe
    return(dbeDirectory)
}
Posted in Dialogs, Files and Directories | Comments closed

Export Attribute Values to Spreadsheet

exporters10This utility allows you to easily and quickly export the values of selected attributes for all objects to a spreadsheet file (CSV format).

//  Export Attributes to CSV File

/*
    Export Selected Attributes to CSV File.

    smartDXL.com
*/

DB      dbMain        = null
DBE     dbeAttrList   = null
DBE     dbeExport     = null
DBE     dbeExportPath = null

Module  currModule    = null
Skip    skipAttrs     = null
int     numVisible    = 0  

const  int    MAX_VISIBLE = 40    // maximal number of attributes visible in multilist
const  string dummy[]     = {}

string tempDir = getenv("TEMP")

/************************************
	getAttrs

	Fill a skip list with names of visible attributes.
*************************************/
void getAttrs()
{
	AttrDef ad = null

	skipAttrs = createString

	for ad in currModule do
	{
		if (isVisibleAttribute(ad))
		{
			put(skipAttrs, ad.name, ad)
			numVisible++
		}
	}

	// restrict the number of visible entries in the multilist
	if (numVisible > MAX_VISIBLE)
	{
		numVisible = MAX_VISIBLE
	}
}

/***************************************
	fillAttrList

	Populates a multilist with the attribute names from the skip list.

****************************************/
void fillAttrList()
{
	AttrDef ad = null
	int     i  = 0

	for ad in skipAttrs do
	{
		insert(dbeAttrList, i++, ad.name)
	}
}

/************************************
	doList
*************************************/
void doExport(DB db)
{
	int     i           = 0
	string  attr        = ""
	int     numSelected = 0
	Object  o           = null
	Stream  outFile     = null
	AttrDef      ad     = null
	AttrType     at     = null
	AttrBaseType abt    = null

	// count the number of attributes selected
    numSelected = 0
    for attr in dbeAttrList do numSelected++

    if (numSelected == 0)
    {
		infoBox("Please select some Attributes!")
		return
    }

	outFile = write(get(dbeExportPath))

	// write header row
	for attr in dbeAttrList do
	{
		outFile << "\"" attr "\","
	}

	outFile << "\n"

	// export attribute values
	for o in currModule do
	{
		for attr in dbeAttrList do
		{
			if (o.attr "" == "")
			{
				outFile << ","
			}
			else if (attr == "Object Heading")
			{
				outFile << "\"" << number(o) " " o.attr "\","
			}
			else
			{
				ad  = find(currModule, attr)
				at  = ad.type
				abt = at.type

				if (abt == attrInteger || abt == attrReal)
				{
					outFile << o.attr ","
				}
				else
				{
					outFile << "\"" << o.attr "\","
				}
			}
		}

		outFile << "\n"
	}	

	close(outFile)

	release db
}

/*********************************
	MAIN
*********************************/
currModule = current Module

if (null currModule)
{
	infoBox("This utility can only be run from a formal module.")
	halt
}

getAttrs()

dbMain = create(currModule, "Export Attributes to CSV File")

label(dbMain, "This utility exports selected attributes from the current module\n" //-
              "Output is to a CSV file.")

dbeAttrList = multiList(dbMain, "Attributes to Display:", numVisible, dummy)              

dbeExport     = field(dbMain, "Module", fullName(currModule), 50, true)
dbeExportPath = fileName(dbMain, "Report File", tempDir "\\" name(currModule) " Attributes.csv")

ok(dbMain, "Export", doExport)

realize dbMain

fillAttrList()

block(dbMain)

destroy(dbMain)
dbMain = null
Posted in Attributes, Export | Comments closed

Mini Explorer

Mini database explorer based on Telelogic’s global function fnMiniExplorer. Includes a fix for the fnMiniExplorer problem where it allows the user to select a folder even when the item filter is set to return a module. The OK button is now inactive until an item matching the filter is selected.

miniExplorer is intended to be used as a replacement for fnMiniExplorer where you want more control over what the user can select. The function is overloaded to give you a fexible range of parameter options.

Usage:

string miniExplorer([[DB dbParent,] [Folder fStart,] [int itemMask]])

Example:

DB  db
DBE dbeBrowse
DBE dbeModule

void doBrowse(DBE dbe)
{
	string mName = miniExplorer(db,
	                            current Folder,
	                            MINI_EXP_FORMAL_MODS)

	set(dbeModule, mName)
}

db = create("Demonstrate Mini Explorer")

dbeModule = field(db, "Module", "", 50, true)
beside db
dbeBrowse = button(db, "Browse...", doBrowse)

realize db
show db

Source code:

//  Enhanced Mini Database Explorer

/*
    Mini database explorer based on Telelogic builtin miniExplorer.

    Includes a fix for the builtin miniExplorer problem where it returns
    a folder name even when the item filter is set to return a formal
    module. The OK button is now inactive until an item matching the
    filter is selected.

    Usage:

    	string miniExplorer(DB dbParent, Folder fStart, int itemMask)

		string miniExplorer(DB dbParent, Folder fStart)

		string miniExplorer(Folder fStart, int itemMask)

		string miniExplorer(DB dbParent, int itemMask)   

		string miniExplorer()  

    Where:

	    	dbParent		Parent dialog box. Returns focus to this dialog
							on closing.

			fStart			Folder to open in the tree when the dialog is first
			                displayed.	

			itemMask		Mask used to filter items that are displayed.

								MINI_EXP_FP               - show folders and projects
								MINI_EXP_LINK_MODS        - show link modules
								MINI_EXP_FORMAL_MODS      - show formal modules
								MINI_EXP_DESCRIPTIVE_MODS - show descriptive modules
								MINI_EXP_SHOW_DELETED     - show deleted items

    smartDXL.com
*/

pragma runLim, 0

// item filters
const int MINI_EXP_FP                       = 1
const int MINI_EXP_LINK_MODS                = 2
const int MINI_EXP_FORMAL_MODS              = 4
const int MINI_EXP_DESCRIPTIVE_MODS         = 8
const int MINI_EXP_SHOW_DELETED             = 16

// item filters
const int MINI_EXP_SHOW_ALL_NO_DELETED      = 15
const int MINI_EXP_SHOW_ALL                 = 31

DB     dbMiniExplorer                       = null
DBE    dbeMiniExplorerTree                  = null
DBE    dbeMiniExplorerOkButton              = null

int    miniExplorerItemMask                 = MINI_EXP_FP
string miniExplorerSelectedItem             = ""

/**********************************
	itemMatchesMask
***********************************/
bool itemMatchesMask(Item itm)
{
	if (type(itm) == "Formal" && ((miniExplorerItemMask & MINI_EXP_FORMAL_MODS) == MINI_EXP_FORMAL_MODS))
	{
		return(true)
	}

	if (type(itm) == "Link" && ((miniExplorerItemMask & MINI_EXP_LINK_MODS) == MINI_EXP_LINK_MODS))
	{
		return(true)
	}

	if (type(itm) == "Descriptive" && ((miniExplorerItemMask & MINI_EXP_DESCRIPTIVE_MODS) == MINI_EXP_DESCRIPTIVE_MODS))
	{
		return(true)
	}

	return(false)
}

/************************************
	doOk
************************************/
void doOk(DB db)
{
    miniExplorerSelectedItem = getRealPath(dbSep get(dbeMiniExplorerTree))

    release(dbMiniExplorer)
    hide(dbMiniExplorer)
}

/***********************************
	doActivate
************************************/
void doActivate(DBE dbe)
{
	string sel = getRealPath(dbSep get(dbeMiniExplorerTree))

	if (itemMatchesMask(item(sel)))
	{
		miniExplorerSelectedItem = sel

		release(dbMiniExplorer)
    	hide(dbMiniExplorer)
	}
}

/**********************************
	doCancel
***********************************/
void doCancel(DB db)
{
    miniExplorerSelectedItem = ""

    release(dbMiniExplorer)
    hide(dbMiniExplorer)
}

/************************************
	doNothing
*************************************/
void doNothing(DB db)
{
	;
}

/**********************************
	addItem
************************************/
void addItem(DBE dbe, Item i)
{
    string displayPath = getDisplayPath(i)

    // check status
    if (exists(dbeMiniExplorerTree, displayPath) == false)
    {
        // check status
        if (isDeleted(i) == false || (isDeleted(i) == true && ((miniExplorerItemMask & MINI_EXP_SHOW_DELETED) == MINI_EXP_SHOW_DELETED)))
        {
            // check item type
            if (type(i) == "Folder" || type(i) == "Project")
            {
                Icon iconOpen, iconClosed
                string sDummyEntry = displayPath dbSep dummyItem

                // assign project or folder specific icons
                assignIcons(i, iconOpen, iconClosed)

                // add entry (plus dummy)
                insert(dbeMiniExplorerTree, displayPath, iconClosed, iconOpen)
                insert(dbeMiniExplorerTree, sDummyEntry, iconClosed, iconOpen)
            }
            else
            {
                if (itemMatchesMask(i))
                {
                	insert(dbeMiniExplorerTree, displayPath, getIcon(i), getIcon(i))
                }
            }
        }
    }
}

/**********************************
	displayBranch
************************************/
void displayBranch(DBE dbe, string sItemPath)
{
    Folder fStart = folder(getRealPath(sItemPath))

    if (fStart != null)
    {
        Item i

        for i in all fStart do
        {
            addItem(dbeMiniExplorerTree, i)
        }
    }
}

/**********************************
	doSelect
***********************************/
void doSelect(DBE dbe)
{
	string sel = getRealPath(dbSep get(dbeMiniExplorerTree))

	if (itemMatchesMask(item(sel)))
	{
		active(dbeMiniExplorerOkButton)
	}
	else
	{
		inactive(dbeMiniExplorerOkButton)
	}
}

/**********************************
	doExpand
************************************/
bool doExpand(DBE dbe, string sItem)
{
    string sItemPath = dbSep sItem
    string sDummyItem = sItemPath dbSep dummyItem

    // check status
    if (exists(dbeMiniExplorerTree, sDummyItem) == true)
    {
        // remove dummy
        delete(dbeMiniExplorerTree, sDummyItem)
    }

    // check status
    if (theCurrentView == DATABASE_VIEW)
    {
        // adjust view accordingly
        displayBranch(dbeMiniExplorerTree, sItemPath)
    }
    else
    {
        Project prjOldRoot = getRootProject_()

        setRootProject_(project(getRootOfPath sItemPath))

        // adjust view accordingly
        displayBranch(dbeMiniExplorerTree, sItemPath)

        setRootProject_(prjOldRoot)
    }

    return true
}

/*************************************
	fnChangeToStartFolder
**************************************/
void fnChangeToStartFolder(Folder fStart)
{
    if (!null fStart)
    {
        int iIndex

        // calculate max bound for loop
        int iFinish = length(rootName_(fStart))

        // prepare initial path
        string sFolderPath = ((theCurrentView == DATABASE_VIEW) ? dbDisplayRoot() : "")

        // process string
        for iIndex in 0 : iFinish by 1 do
        {
            // obtain character
            string sCharacter = (rootName_(fStart))[iIndex:iIndex]

            // check status
            if (sCharacter == dbSep || iIndex == iFinish)
            {
                // check status
                if (theCurrentView == PROJECT_VIEW && sFolderPath == dbSep)
                    continue

                // update explorer
                displayBranch(dbeMiniExplorerTree, sFolderPath)
            }

            sFolderPath = sFolderPath sCharacter
        }   

        // update explorer
        set(dbeMiniExplorerTree, sFolderPath)
    }
    else
    {
        // default to database level
        displayBranch(dbeMiniExplorerTree, dbDisplayRoot())
        set(dbeMiniExplorerTree, dbDisplayRoot())
    }
}

/************************************
	miniExplorer

	Provides a tree view for user to select an item from the database.

	Parameters:

		dbParent		Parent dialog box. Returns focus to this dialog
						on closing.

		fStart			Folder to open in the tree when the dialog is first
		                displayed.	

		itemMask		Mask used to filter items that are displayed. 	

	Returns fullname of selected item or an empty string.
*************************************/
string miniExplorer(DB     dbParent,
                         Folder fStart,
                         int    itemMask)
{
	string  pName = ""
    Project pRoot = null

	miniExplorerItemMask   = itemMask 

    if (null dbParent)
    {
	    dbMiniExplorer = create("smart Mini Explorer")
    }
    else
    {
    	dbMiniExplorer = create(dbParent, "smart Mini Explorer")
	}

    label(dbMiniExplorer, "Please select an item...")

    dbeMiniExplorerTree = treeView(dbMiniExplorer, treeViewOptionSorted, 400, 15)

    dbeMiniExplorerOkButton = apply(dbMiniExplorer, "OK", doOk)

	// create our own close and ok buttons
	close(dbMiniExplorer, false, doNothing)
	ok(dbMiniExplorer, "Cancel", doCancel)

    realize dbMiniExplorer

    set(dbeMiniExplorerTree, doSelect, doActivate)
    set(dbeMiniExplorerTree, doExpand)

    inactive(dbeMiniExplorerOkButton)
    // check status
    if (theCurrentView == DATABASE_VIEW)
    {
        // adjust accordingly
        insert(dbeMiniExplorerTree, dbDisplayRoot(), iconDatabase, iconDatabase)
    }
    else
    {
        pRoot = getRootProject_()
        setRootProject_(null)

        // process database
        for pName in database do
        {
            addItem(dbeMiniExplorerTree, item(dbSep pName))
        }

        setRootProject_(pRoot)
    }

    fnChangeToStartFolder(fStart)

    block(dbMiniExplorer)

    destroy(dbMiniExplorer)

    return(miniExplorerSelectedItem)
}

/*************************************
	miniExplorer

	Filter not specified
**************************************/
string miniExplorer(DB dbParent, Folder fStart)
{
	return miniExplorer(dbParent, fStart, MINI_EXP_SHOW_ALL)
}

/**************************************
	miniExplorer

	Parent dialog not specified
*************************************/
string miniExplorer(Folder fStart, int itemMask)
{
	return miniExplorer(null, fStart, itemMask)
}

/*************************************
	miniExplorer

	Current folder not specified
**************************************/
string miniExplorer(DB dbParent, int itemMask)
{
	return miniExplorer(dbParent, null, itemMask)
}

/************************************
	miniExplorer

	No parameters
************************************/
string miniExplorer()
{
	return miniExplorer(null, null, MINI_EXP_SHOW_ALL)
}           
Posted in Dialogs, Explorers | Comments closed