/*
** Copyright (c) 1990 David E. Smyth
**
** This file was derived from work performed by Martin Brunecky at
** Auto-trol Technology Corporation, Denver, Colorado, under the
** following copyright:
**
*******************************************************************************
* Copyright 1990 by Auto-trol Technology Corporation, Denver, Colorado.
*
*                        All Rights Reserved
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted, provided
* that the above copyright notice appears on all copies and that both the
* copyright and this permission notice appear in supporting documentation
* and that the name of Auto-trol not be used in advertising or publicity
* pertaining to distribution of the software without specific, prior written
* permission.
*
* Auto-trol disclaims all warranties with regard to this software, including
* all implied warranties of merchantability and fitness, in no event shall
* Auto-trol be liable for any special, indirect or consequential damages or
* any damages whatsoever resulting from loss of use, data or profits, whether
* in an action of contract, negligence or other tortious action, arising out
* of or in connection with the use or performance of this software.
*******************************************************************************
**
** Redistribution and use in source and binary forms are permitted
** provided that the above copyright notice and this paragraph are
** duplicated in all such forms and that any documentation, advertising
** materials, and other materials related to such distribution and use
** acknowledge that the software was developed by David E. Smyth.  The
** name of David E. Smyth may not be used to endorse or promote products
** derived from this software without specific prior written permission.
** THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
** WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
*/

/*
* SCCS_data: @(#)WcCreate.c 1.03 ( 11 July 1990 )
*
* Subsystem_group:
*
*     Widget Creation Library
*
* Module_description:
*
*     This module contains the functions used to create and manage 
*     a widget tree using the Xrm database.

*     The Xrm database format used to prescribe widget's children
*     is as follows:
*
*     ...parent.wcChildren:         childName1, childName2, ...
*
*     The type of each child must be specified.  There are three
*     ways of doing this: (1) by widget class (class pointer name),
*     (2) by widget class name, and (3) by widget constructor 
*     function name.  The resource value given is case insensitive,
*     although one is encouraged to use the capitalization given
*     in the reference manuals simply on stylistic grounds.  The 
*     three examples below are effectively equivalent:
*
*     ...drawing.wcClass:	xmDrawingAreaWidgetClass
*     ...drawing.wcClassName:	XmDrawingArea
*     ...drawing.wcConstructor: XmCreateDrawingArea
*
*     Since there are multiple ways of specifying the widget type,
*     a precedence is defined: wcClass is the highest precedence,
*     and wcClass > wcClassName > wcConstructor.
*
*     Currently, the Motif widget set defines many constructors, such 
*     as XmCreateForm or XmCreateFileSelectionBox.  Constructors are 
*     somtimes called Convenience Functions or even Confusion Functions.  
*     It is highly recommended that you use the WcTrace resource when
*     first using Constructors in order to see the resulting widget
*     heirarchy more clearly.  The WcTrace resource is specified as:
*
*     ...drawing.wcTrace:	True

*     By default, all widgets created from the Xrm resource database
*     are managed.  In some cases, this is not what is desired: for
*     example, pop-up menus are generally not managed when they are
*     created, rather they are managed due to some mouse event.  In
*     such cases, the WcManaged resource should be set False, as below:
*
*     *fileMenu.wcManaged:	False

*     It is possible to bind one or more callback functions to the
*     creation of a widget by using the WcCallback resource.  For 
*     example, using the Motif widget set, when a menu bar is created 
*     as a child of an XmMainWindow, the menuBar resource of the 
*     XmMainWindow needs to be set to refer to the menu bar.  For 
*     example:
*
*     App.main.wcClassName:	XmMainWindow
*     App.main.wcChildren:	mbar
*     *mbar.wcConstructor:	XmCreateMenuBar
*     *mbar.wcCallback:		WcSetResourceCB( *main.menuBar: this)

*     Sometimes widget heirarchies are dynamic: many of the widgets
*     are created at initialization time, but others need to be created
*     at run time.  Simply do not include the names of the dynamic
*     created widgets in any wcChildren resource.  They will then still
*     be defined in the Xrm database, but they will not be created
*     during the initial widget tree creation.

*     The WcDeferred resource is an obsolete mechanism which should no
*     longer be used.

*     For example, let's say your interface includes a box world, where
*     the user can create an arbitrary number of various types of boxes:

*     *box_world.wcClass:	MyBoxWorldWidgetClass
*
*     *box_type1.wcClass:	MyBoxType1WidgetClass
*     *box_type2.wcClass:	MyBoxType2WidgetClass
*     *box_type3.wcClass:	MyBoxType3WidgetClass
*     *box_type3.wcChildren:	child1, child2, child3, child4
*
*     *button1.callback:	WcCreateChildrenCB( *box_world, box_type1 )
*     *button2.callback:	WcCreateChildrenCB( *box_world, box_type2 )
*     *button3.callback:	WcCreateChildrenCB( *box_world, box_type3 )
*
*     Initially, when box_world is created, it will have no children.
*     Each time button1 is pressed, another instance of box_type1 will
*     be created as a child of box_world, and managed (since wcManaged
*     is by default True).  Similarly, everytime button2 is pressed
*     another box_type2 will be created, and everytime button3 is
*     pressed another box_type3 will be created, along with children
*     named child1, child2, child3, and child4, and their children as 
*     applicable.

*     User interfaces are often rather complex.  Since the Widget
*     Creation Library allows much more of the interface to be
*     specified in resource files than in prehistoric days of
*     Widget application programming, it becomes necessary to allow
*     interfaces to be specified in a collection of resource files.
*     
*     It is very desirable that anything in the interface can be
*     tailored by the user.  Therefore, each resource filename
*     loaded to describe an interface must be searched for in 
*     all the locations defined by the Xt Intrinsics.  This is
*     exactly the same as the initial resource file.
*
*     Since a resource file may override any of the resources on
*     a given widget, including other creation resources, the
*     resource file name is first fetched before any of the other
*     wc* resources.
*
*     A resource file name is specified as follows:
*
*     *menuBar.wcResFile:	MenuBar
*
*     Each of the standard directories will be searched in order
*     for a file named MenuBar, and each merged into the resource database.

*     The following is the complete set of resources which are 
*     interpreted by the Widget Creation Library:
*
*     ...widget.wcChildren:	childName1, childName2, ...
*     ...widget.wcClass:	classPointerName
*     ...widget.wcClassName:	className
*     ...widget.wcConstructor:  constructorFunctionName
*     ...widget.wcTrace:	true/false (default = false)
*     ...widget.wcManaged:	true/false (default = true)
*     ...widget.wcCallback:	callback1(args), callback2(args), ...
*     ...widget.wcResFile:	filename
*
*     In all cases, the Widget Creation resource names can be
*     specified as Wc<name> or wc<name>, with the capitalized
*     form having looser binding (representing the resource class).

*     Example:
*
*		HelloWorld.wcChildren:  push
*
*		*push.wcClass:          xmPushButtonWidgetClass
*		*push.labelString:      Hello World
*		*push.activateCallback: WcExitCB(1)

*     Since (for portability reasons) we can not assume runtime binding,
*     all widget classes or creation routines (constructors) must be 
*     "registered"  by the application BEFORE widget tree creation.
*
*     The widget tree creation is performed by the WcCreateDatabaseChild()
*     function, which descends the widget tree recursively until no more
*     children are found, or a non-composite widget/object is found.
*
*     Several convenience callbacks are provided with the package, allowing 
*     deferred widget creation, control (manage/unmanage) and other utility
*     functions.  These are found in WcCallb.c
*
* Module_history:
                                                  
*   mm/dd/yy  initials  function  action
*   --------  --------  --------  ---------------------------------------------
*   13Aug90   D.Smyth	Got rid of WcDefered stuff
*   11Jul90   D.Smyth   Added wcResFile resource
*   30Jun90   R.Whitby	WcWidgetCreation added call to WcRegisterWcActions
*   19Jun90   D.Smyth	Version 1.0
*   04/18/90  MarBru    many..    Changed w->core.name to XrmQuarkToString...
*   03/27/90  MarBru    Creation  Converted to a completely new syntax
*   02/16/90  MarBru    Create..  Limited creation to composite widgets/objects
*
* Design_notes:
*
*******************************************************************************
*/
/*
*******************************************************************************
* Include_files.
*******************************************************************************
*/

/*  -- Operating system includes */
#include <stdio.h>

/*  -- X Window System includes */
#include <X11/StringDefs.h>

/*  -- Widget Creation Includes */
#include "WcCreate.h"
#include "WcCreateP.h"

/*
*******************************************************************************
* Private_data_definitions.
*******************************************************************************
*/

static char     msg[MAX_ERRMSG];

/*  -- Creation resources 
	NOTE: All resource values are fetched at once, but if WcResFile
	is specified, then the resource files must be loaded and then
	the resources re-fetched.  This is an optimization: usually
	WcResFile is not specified, so two fetches from the Xrm database
	do not need to be done in the normal case.  If resource files are
	loaded and the resources are re-fetched, WcResFile is ignored.
	NOTE: The WcClass and WcClassName resources are both converted to
        a class pointer, as we can construct useful error messages using:
		class->core_class.class_name
	However, the Constructor must be the entire constructor cache
	record so we have the name of the constructor for the error
	messages.
	NOTE: WcClass and WcClassName write to different ResourceRec
	members, so we can provide better error messages.
*/

XtResource wc_resources[] =
  {
    { WcNwcResFile,	WcCWcResFile,		XtRString,	sizeof(String),
      XtOffset(ResourceRecPtr, resFile ),	XtRImmediate,	(caddr_t) NULL
    },
    { WcNwcChildren,	WcCWcChildren,		XtRString,	sizeof(String),
      XtOffset(ResourceRecPtr, children ),	XtRImmediate,	(caddr_t) NULL
    },
    { WcNwcClass,	WcCWcClass,		WcRClassPtr,	sizeof(caddr_t),
      XtOffset(ResourceRecPtr, class ),		XtRImmediate,	(caddr_t) NULL
    },
    { WcNwcClassName,	WcCWcClassName,		WcRClassName,	sizeof(caddr_t),
      XtOffset(ResourceRecPtr, classFromName ),	XtRImmediate,	(caddr_t) NULL
    },
    { WcNwcConstructor,	WcCWcConstructor, 	WcRConstructor,	sizeof(caddr_t),
      XtOffset(ResourceRecPtr, constructor ),	XtRImmediate,	(caddr_t) NULL
    },
    { WcNwcManaged,	WcCWcManaged,		XtRBoolean,	sizeof(Boolean),
      XtOffset(ResourceRecPtr, managed),	XtRImmediate,	(caddr_t) TRUE
    },
    { WcNwcTrace,	WcCWcTrace,		XtRBoolean,	sizeof(Boolean),
      XtOffset(ResourceRecPtr, trace),		XtRImmediate,	(caddr_t) FALSE
    },
    { WcNwcCallback,	WcCWcCallback,	XtRCallback,	sizeof(XtCallbackList),
      XtOffset(ResourceRecPtr, callback ),	XtRImmediate,	(caddr_t) NULL
    }
  };

/*
*******************************************************************************
* Private_function_declarations.
*******************************************************************************
*/

/*
*******************************************************************************
* Public_function_declarations.
*******************************************************************************
*/

/*
    -- Create Database Child
*******************************************************************************
    This function checks the resource database for creation resources
    of the named widget. If found, the child is created using the specified
    class, and the creation callbacks are called.

    Note that whenever this function generates a warning message, it
    uses the parent's name.  In order to improve run-time efficientcy,
    the parent name is only determined when a warning message is to be
    generated.  This adds a few lines of code, but it speeds the
    nominal case where there are no warnings.
*/

Widget WcCreateDatabaseChild ( pw, name, managed  )
    Widget      pw;         /* child's parent */
    char*       name;       /* child name to create */
    int        *managed;    /* returned T/F: this child to be managed ? */
{
    ResourceRec res;			   /* child's creation resources */
    Widget	child = NULL;		   /* what we create */

    /* Get creation resources for the child to be created.
    ** After this XtGetSubresources() call, the resource structure `res'
    ** contains resources specified in the Xrm database or the defaults.
    */
    XtGetSubresources ( pw, &res, name, name, 
       wc_resources, XtNumber(wc_resources), NULL, 0 );

    /* if a resource file is specified for this widget, first
    ** load the resource file, then re-fetch the resources.
    ** Notice that we don't check for resFile again.
    */
    if ( res.resFile )
    {
	WcLoadResourceFileCB ( pw, res.resFile, NULL );
	XtGetSubresources ( pw, &res, name, name,
	   wc_resources, XtNumber(wc_resources), NULL, 0 );
    }

    if ( !res.class && !res.classFromName && !res.constructor )
    {
	char* parentName = WcWidgetToFullName( pw );
        sprintf( msg,
            "WcCreateDatabaseChild (%s.%s) - Failed \n\
             Problem: No %s, %s, nor %s specified,  \n\
                      Child `%s' could not be created.",
             parentName, name, 
	     WcCWcClass, WcCWcClassName, WcCWcConstructor,
	     name );
        XtWarning( msg );
	child = (Widget)NULL;
	XtFree( parentName );
    }

    else if ( res.class || res.classFromName )
    {
	if ( res.class && res.classFromName && res.constructor )
	{
	    char* parentName = WcWidgetToFullName( pw );
	    sprintf( msg,
            "WcCreateDatabaseChild (%s.%s) \n\
             Problem: %s, %s, and %s resources specified, \n\
		      `%s.%s: %s' and \n\
		      `%s.%s: %s' ignored.",
             parentName, name, 
	     WcCWcClass, WcCWcClassName, WcCWcConstructor,
	     name, WcCWcClassName, res.classFromName->core_class.class_name,
	     name, WcCWcConstructor, res.constructor->name );
            XtWarning( msg );
	    XtFree( parentName );
	}
	else if ( res.class && res.classFromName )
	{
	    char* parentName = WcWidgetToFullName( pw );
            sprintf( msg,
            "WcCreateDatabaseChild (%s.%s) \n\
             Problem: %s and %s resources specified, \n\
                      `%s.%s: %s' ignored.",
             parentName, name,
             WcCWcClass, WcCWcClassName, 
             name, WcCWcClassName, res.classFromName->core_class.class_name);
            XtWarning( msg );
	    XtFree( parentName );
        }
        else if ( res.class && res.constructor )
        {
	    char* parentName = WcWidgetToFullName( pw );
            sprintf( msg,
            "WcCreateDatabaseChild (%s.%s) \n\
             Problem: %s and %s resources specified, \n\
                      `%s.%s: %s' ignored.",
             parentName, name,
             WcCWcClass, WcCWcConstructor,
             name, WcCWcConstructor, res.constructor->name );
            XtWarning( msg );
	    XtFree( parentName );
        }
        else if ( res.classFromName && res.constructor )
        {
	    char* parentName = WcWidgetToFullName( pw );
            sprintf( msg,
            "WcCreateDatabaseChild (%s.%s) \n\
             Problem: %s and %s resources specified, \n\
                      `%s.%s: %s' ignored.",
             parentName, name,
             WcCWcClassName, WcCWcConstructor,
             name, WcCWcConstructor, res.constructor->name );
            XtWarning( msg );
	    XtFree( parentName );
        }

	if ( res.class )
	    child = XtCreateWidget ( name, res.class, pw, NULL, 0 );
	else
	    child = XtCreateWidget ( name, res.classFromName, pw, NULL, 0 );

	if ( !child )
	{
	    char* parentName = WcWidgetToFullName( pw );
            sprintf( msg,
            "WcCreateDatabaseChild (%s.%s) - Failed \n\
             Problem: XtCreateWidget ( %s, %s ) failed.",
             parentName, name, name, res.class->core_class.class_name );
            XtWarning( msg );
	    XtFree( parentName );
	}
    }

    else if ( res.constructor )
    {
	child = res.constructor->constructor( pw, name, NULL, 0 );

	if ( !child )
        {
	    char* parentName = WcWidgetToFullName( pw );
            sprintf( msg,
            "WcCreateDatabaseChild (%s.%s) - Failed \n\
             Problem: %s ( %s ) failed.",
             parentName, name, res.constructor->name, name );
            XtWarning( msg );
	    XtFree( parentName );
        }
    }

    if ( child )
    {
	/* A child widget was created.
	** print out creation trace, if required 
	*/
	if ( res.trace )
	{
	    char* childName = WcWidgetToFullName( child );
	    fprintf(stderr,"Wc %s: %s of class %s\n",
		((res.managed) ? "  managed" : "unmanaged"), childName, 
		child->core.widget_class->core_class.class_name);
	    XtFree( childName  );
	}

	/* call creation callbacks */
	if ( res.callback )
	{
	    XtCallbackRec *cb = res.callback;
	    for ( ; cb->callback; cb++ )
		(*cb->callback)( child, cb->closure, NULL );
	}

	if ( res.children )
	{
	    if  ( XtIsSubclass( child, compositeWidgetClass ) )
	    {
		/* child is a manager widget, create its children */
	        WcCreateNamedChildren ( child, res.children );		
	    }
	    else
	    {
		char* parentName = WcWidgetToFullName( pw );
                sprintf( msg,
            "WcCreateDatabaseChild (%s.%s) - children ignored \n\
             Problem: %s is not a composite, cannot have children.",
             parentName, name, name );
                XtWarning( msg );
		XtFree( parentName );
	    }
	}
    }

    *managed = res.managed;
    return (child);
}

/*
    -- Create And Manage Named Children from Xrm Database
*******************************************************************************
    This function creates widget's children specified by names list,
    by calling WcCreateDatabaseChild() for each of the names provided.

    All the children are then managed, unless WcManaged resource is FALSE.

    Note that widgets created by WcCreateDatabaseChild may or may not
    be children of `pw' due to the use of constructors.  Only children
    of `pw' may be managed via a call to XtManageChildren().  Other
    widgets must be managed individually.  Usually, these widgets
    are created by the XmCreateScrolled*() or XmCreate*Dialog confusion 
    functions.
*/

void WcCreateNamedChildren ( pw, names )
    Widget      pw;         /* children's parent                            */
    char*       names;      /* (list of) widget names to create             */
{
    Widget	child;
    int		children = 0;
    Widget	widget_children[MAX_CHILDREN];
    int		other = 0;
    Widget	widget_other[MAX_CHILDREN];
    char	cleanName[MAX_XRMSTRING];
    char*	next;
    int		managed;
    int		i;

    if  ( !names ) return;

    next = WcCleanName( names, cleanName );

    while ( cleanName[0] )
    {
	child = WcCreateDatabaseChild ( pw, cleanName, &managed );
	if ( child )
	{
	    if ( managed && (XtParent( child ) == pw ) )
		widget_children[children++] = child;
	    else if ( managed )
		widget_other[other++] = child;
	}
	next = WcSkipWhitespace_Comma( next );
	next = WcCleanName( next, cleanName );
    }

    if ( children ) 
	XtManageChildren( widget_children, children );

    for (i = 0 ; i < other ; i++)
	XtManageChild( widget_other[i] );
}

/*
    -- Create Widget Tree from Xrm Database
*******************************************************************************
    This routine creates widget children as defined in Xrm database.
    It checks the widget resource "WcChildren", which is a list of
    names of children to create. Each child must then be further defined
    in the databse.

    This function is frequently called from an application's main()
    procedure after the application shell is created via XtInitialize().

    Note that this function registers the converters for StringToWidget,
    StringToCallback, and so forth.
*/

void WcWidgetCreation ( root )
    Widget       root;
{
    XtAppContext app = XtWidgetToApplicationContext( root );
    char*	 fullName = WcWidgetToFullName( root );	/* must be XtFree'd */
    ResourceRec  res;

    /* register the root of this widget */
    (void)WcRootWidget(root);

    /* register the Xt standard widgets */
    WcRegisterIntrinsic ( app );

    /* register the Wc converters */
    WcAddConverters( app );

    /* register the Wc callbacks */
    WcRegisterWcCallbacks ( app );

    /* register the Wc actions */
    WcRegisterWcActions ( app );

    if ( XtIsSubclass( root, compositeWidgetClass ) )
    {
        XtGetApplicationResources ( root, &res,
              wc_resources, XtNumber(wc_resources), NULL, 0 );
    
        if ( res.children )
           WcCreateNamedChildren ( root, res.children );
        else
        {
            sprintf( msg,
            "WcWidgetCreation (%s) - Failed \n\
             Problem: No children defined in Xrm database.\n\
	     Possible: resource file not found (XENVIRONEMENT not set?), \n\
	     Possible: top level widget in resource file not named %s",
             fullName, root->core.name );
            XtWarning( msg );
	}
    }

     else
    {
        sprintf( msg,
            "WcWidgetCreation (%s) - Failed \n\
             Problem: %s is not a composite widget, cannot have children.",
             fullName, fullName );
        XtWarning( msg );
    }

    XtFree ( fullName );
}
