/*
 *
 *  (c) COPYRIGHT INRIA, 1996-2003
 *  Please first read the full copyright statement in file COPYRIGHT.
 *
 */

/*
 * User interface for attributes
 *
 * Authors: I. Vatton (INRIA)
 *          R. Guetari (W3C/INRIA) - Windows version
 *
 */

#include "thot_gui.h"
#include "thot_sys.h"
#include "dialog.h"
#include "constmedia.h"
#include "typemedia.h"
#include "libmsg.h"
#include "message.h"
#include "constmenu.h"
#include "appdialogue.h"
#ifdef _WINDOWS
#include "winsys.h"
#include "wininclude.h"
#endif /* _WINDOWS */

#undef THOT_EXPORT
#define THOT_EXPORT extern
#include "select_tv.h"
#include "edit_tv.h"
#include "frame_tv.h"
#include "appdialogue_tv.h"

/* flags to show the existence of the TtAttribute forms*/
static ThotBool     AttrFormExists = FALSE;
static ThotBool     MandatoryAttrFormExists = FALSE;

#define LgMaxAttrText 500
static PtrSSchema   SchCurrentAttr = NULL;
static PtrDocument  DocCurrentAttr = NULL;
static char         TextAttrValue[LgMaxAttrText];
static int          NumCurrentAttr = 0;
static int          CurrentAttr;
/* return value of the input form */
static int          NumAttrValue;
/* main menu of attributes */
static PtrSSchema   AttrStruct[MAX_MENU * 2];
static int          AttrNumber[MAX_MENU * 2];
static int          ActiveAttr[MAX_MENU * 2];
static ThotBool     AttrOblig[MAX_MENU * 2];
static ThotBool     AttrEvent[MAX_MENU * 2];
/* submenu of event attributes */
static int          AttrEventNumber[MAX_MENU];
static int          ActiveEventAttr[MAX_MENU];
static int          EventMenu[MAX_FRAME];

/* required attributs context */
static PtrAttribute PtrReqAttr;
static PtrDocument  PtrDocOfReqAttr;

#ifdef _WINDOWS
#define ID_CONFIRM   1000
#define ID_DONE      1001
#define ID_APPLY     1002
#define ID_DELETE    1003
#define ID_EDITVALUE 1004


extern WNDPROC      lpfnTextZoneWndProc ;
static char         WIN_buffMenu[MAX_TXT_LEN];
static char         WIN_Lab[200];
static char         formRange[100];
static char        *szAppName;
static ThotWindow   hwnEdit;
static TtAttribute *WIN_pAttr1;
static ThotBool     wndRegistered;
static ThotBool     wndSheetRegistered;
static ThotBool     wndNumAttrRegistered;
static ThotBool     WIN_AtNumAttr  = FALSE;
static ThotBool     WIN_AtTextAttr = FALSE;
static ThotBool     WIN_AtEnumAttr = FALSE;
static ThotBool     isForm         = FALSE;
static int          WIN_nbItem;
static int          formValue;
static int          nbDlgItems;
static int          WIN_Language;

extern HINSTANCE hInstance;
extern LPCTSTR   iconID;
extern UINT      subMenuID[MAX_FRAME];
#endif /* _WINDOWS */

#include "appdialogue_f.h"
#include "appli_f.h"
#include "applicationapi_f.h"
#include "attributes_f.h"
#include "attrpresent_f.h"
#include "actions_f.h"
#include "callback_f.h"
#include "changeabsbox_f.h"
#include "content_f.h"
#include "displayview_f.h"
#include "exceptions_f.h"
#include "language_f.h"
#include "memory_f.h"
#include "schemas_f.h"
#include "selectmenu_f.h"
#include "structcommands_f.h"
#include "structcreation_f.h"
#include "structmodif_f.h"
#include "structselect_f.h"
#include "structschema_f.h"
#include "tree_f.h"
#include "uconvert_f.h"

/*----------------------------------------------------------------------
  InitFormLangue
  initializes a form for capturing the values of the Language attribute.
  ----------------------------------------------------------------------*/
static void InitFormLanguage (Document doc, View view,
			      PtrElement firstSel,
			      PtrAttribute currAttr)
{
   Language            language;
   PtrAttribute        pHeritAttr;
   PtrElement          pElAttr;
   char               *ptr;
   char                languageCode[MAX_TXT_LEN];
   char                label[200];
   int                 defItem, nbItem;
#ifndef _WINDOWS
   char                bufMenu[MAX_TXT_LEN];
   int                 i;
#endif /* _WINDOWS */

   /* Initialize the language selector */
   languageCode[0] = EOS;
   if (currAttr && currAttr->AeAttrText)
     CopyBuffer2MBs (currAttr->AeAttrText, 0, languageCode, MAX_TXT_LEN);
#ifdef _WINDOWS
   ptr = GetListOfLanguages (WIN_buffMenu, MAX_TXT_LEN, languageCode, &nbItem, &defItem);
#else  /* _WINDOWS */
   /* generate the form with two buttons Apply Cancel */
   strcpy (bufMenu, TtaGetMessage (LIB, TMSG_APPLY));
   i = strlen (bufMenu) + 1;
   strcpy (&bufMenu[i], TtaGetMessage (LIB, TMSG_DEL_ATTR));
   TtaNewSheet (NumFormLanguage, TtaGetViewFrame (doc, view),
		TtaGetMessage (LIB, TMSG_LANGUAGE), 2, 
		bufMenu, FALSE, 2, 'L', D_DONE);
   /* construit le selecteur des Langues */
   ptr = GetListOfLanguages (bufMenu, MAX_TXT_LEN, languageCode, &nbItem, &defItem);
   if (nbItem > 0)
     {
       /* on cree un selecteur */
       if (nbItem >= 6)
	 i = 6;
       else
	 i = nbItem;
       TtaNewSelector (NumSelectLanguage, NumFormLanguage,
		       TtaGetMessage (LIB, TMSG_LANG_OF_EL), nbItem, bufMenu,
		       i, NULL, TRUE, TRUE);
     }
   if (defItem >= 0)
     TtaSetSelector (NumSelectLanguage, defItem, NULL);
   else if (ptr)
     TtaSetSelector (NumSelectLanguage, -1, ptr);
   else
     TtaSetSelector (NumSelectLanguage, -1, NULL);
#endif /* _WINDOWS */
   if (languageCode[0] == EOS)
     {
       /* look for the inherited attribute value Language */
       strcpy (label, TtaGetMessage (LIB, TMSG_INHERITED_LANG));
       pHeritAttr = GetTypedAttrAncestor (firstSel, 1, NULL, &pElAttr);
       if (pHeritAttr && pHeritAttr->AeAttrText)
	 {
	   /* the attribute value is a RFC-1766 code. Convert it into */
	   /* a language name */
	   CopyBuffer2MBs (pHeritAttr->AeAttrText, 0, languageCode, MAX_TXT_LEN);
	   language = TtaGetLanguageIdFromName (languageCode);
	   strcat (label, TtaGetLanguageName(language));
	 }
     }
   else
     label[0] = EOS;

#ifndef _WINDOWS 
   TtaNewLabel (NumLabelHeritedLanguage, NumFormLanguage, label);
   /* affiche le formulaire */
   TtaShowDialogue (NumFormLanguage, TRUE);
#else  /* _WINDOWS */
   sprintf (WIN_Lab, "%s", label);
   WIN_nbItem = nbItem; 
   WIN_Language = defItem;
#endif /* _WINDOWS */
}

#ifdef _WINDOWS
/*----------------------------------------------------------------------
  ----------------------------------------------------------------------*/
LRESULT CALLBACK TextAttrProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch (msg)
    {
    case WM_KEYDOWN:
      switch (wParam)
        {
        case VK_RETURN:
          SendMessage(GetParent (hwnd), WM_ENTER, 0, 0);
          return 0;
        }
      break;

    case WM_KEYUP:
    case WM_CHAR:
      switch (wParam)
        {
        case VK_RETURN:
          return 0;
        }
    }

  /* Call the original window procedure for default processing */
  return CallWindowProc (lpfnTextZoneWndProc, hwnd, msg, wParam, lParam);
}

/*----------------------------------------------------------------------
 InitFormDialogWndProc
  ----------------------------------------------------------------------*/
LRESULT CALLBACK InitFormDialogWndProc (ThotWindow hwnd, UINT iMsg,
					WPARAM wParam, LPARAM lParam)
{
  HFONT               newFont;
  ThotWindow          hwnTitle;
  ThotWindow          confirmButton;
  ThotWindow          doneButton;
  int                 i;
  int                 txtLength;
  
  switch (iMsg)
    {
    case WM_CREATE:
      /* get the default GUI font */
      newFont = GetStockObject (DEFAULT_GUI_FONT); 
      /* Create static window for the title */
      hwnTitle = CreateWindow ("STATIC", WIN_pAttr1->AttrName, 
			       WS_CHILD | WS_VISIBLE | SS_LEFT,
			       10, 5, 100, 15, hwnd, (HMENU) 99, 
			       ((LPCREATESTRUCT) lParam)->hInstance, NULL); 
      /* set the font of the window */
      if(newFont)
         SendMessage (hwnTitle, WM_SETFONT, (WPARAM) newFont, MAKELPARAM(FALSE, 0));
       
      /* Create Edit Window autoscrolled */
      hwnEdit = CreateWindow ("EDIT", TextAttrValue, 
			      WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT |
			         ES_AUTOHSCROLL,
			      10, 25, 320, 20, hwnd, (HMENU) 1,
			      ((LPCREATESTRUCT) lParam)->hInstance, NULL);
      /* set the font of the window */
      if(newFont)
         SendMessage (hwnEdit, WM_SETFONT, (WPARAM) newFont, MAKELPARAM(FALSE, 0));
 
      if (lpfnTextZoneWndProc == (WNDPROC) 0)
	lpfnTextZoneWndProc = (WNDPROC) SetWindowLong (hwnEdit, GWL_WNDPROC,
						       (DWORD) TextAttrProc);
      else
	SetWindowLong (hwnEdit, GWL_WNDPROC, (DWORD) TextAttrProc);
      
      /* Create Confirm button */
      confirmButton = CreateWindow ("BUTTON",
				    TtaGetMessage (LIB, TMSG_LIB_CONFIRM), 
				    WS_CHILD | BS_DEFPUSHBUTTON | WS_VISIBLE,
				    65, 50, 100, 20, hwnd, 
				    (HMENU) ID_CONFIRM,
 				    ((LPCREATESTRUCT)lParam)->hInstance, NULL);
      /* set the font of the window */
      if(newFont)
         SendMessage (confirmButton, WM_SETFONT, (WPARAM) newFont, MAKELPARAM(FALSE, 0));
      
      /* Create Done Button */
      doneButton = CreateWindow ("BUTTON", TtaGetMessage(LIB, TMSG_DONE),
				 WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE,
				 185, 50, 100, 20, hwnd, 
				 (HMENU) ID_DONE,
				 ((LPCREATESTRUCT) lParam)->hInstance, NULL);
      /* set the font of the window */
      if(newFont)
         SendMessage (doneButton, WM_SETFONT, (WPARAM) newFont, MAKELPARAM(FALSE, 0));
       
      SetFocus (hwnEdit);
      break;
      
    case WM_DESTROY :
      PostQuitMessage (0);
      break;
      
    case WM_ENTER:
      txtLength = GetWindowTextLength (hwnEdit);
      if (txtLength >= LgMaxAttrText)
	txtLength = LgMaxAttrText - 1;
      GetWindowText (hwnEdit, TextAttrValue, txtLength + 1);
      i = 0;
      while (i < txtLength && TextAttrValue[i] != __CR__)
	i++;
      if (i < txtLength)
	TextAttrValue[i] = EOS;
      ThotCallback (NumMenuAttrTextNeeded, STRING_DATA, TextAttrValue);
      ThotCallback (NumMenuAttrRequired, INTEGER_DATA, (char *) 1);
      DestroyWindow (hwnd);
      break;
      
    case WM_COMMAND:
      switch (LOWORD (wParam))
	{
	case ID_CONFIRM:
	  txtLength = GetWindowTextLength (hwnEdit);
	  if (txtLength >= LgMaxAttrText)
	    txtLength = LgMaxAttrText - 1;
	  GetWindowText (hwnEdit, TextAttrValue, txtLength + 1);
	  i = 0;
	  while (i < txtLength && TextAttrValue[i] != __CR__)
	    i++;
	  if (i < txtLength)
	    TextAttrValue[i] = EOS;
	  ThotCallback (NumMenuAttrTextNeeded, STRING_DATA, TextAttrValue);
	  ThotCallback (NumMenuAttrRequired, INTEGER_DATA, (char *) 1);
	  DestroyWindow (hwnd);
	  break;
	  
	case ID_DONE:
	  ThotCallback (NumMenuAttrRequired, INTEGER_DATA, (char *) 0);
	  DestroyWindow (hwnd);
	  /* Traitement ID_DONE */
	  break;
	}
      break;
    }
  return DefWindowProc (hwnd, iMsg, wParam, lParam);
}

/*----------------------------------------------------------------------
  WIN_InitFormDialog
  ----------------------------------------------------------------------*/
static ThotBool WIN_InitFormDialog (ThotWindow parent, char *title)
{
  HFONT           newFont;
  WNDCLASS        wndFormClass;
  char           *szAppName; 
  ThotWindow      hwnFromDialog;
  MSG             msg;
  int             frame;

  szAppName = "FormClass";
  if (!wndRegistered)
    {
      wndRegistered = TRUE;
      wndFormClass.style         = CS_HREDRAW | CS_VREDRAW;
      wndFormClass.lpfnWndProc   = InitFormDialogWndProc;
      wndFormClass.cbClsExtra    = 0;
      wndFormClass.cbWndExtra    = 0;
      wndFormClass.hInstance     = hInstance;
      wndFormClass.hIcon         = LoadIcon (NULL, IDI_APPLICATION);
      wndFormClass.hCursor       = LoadCursor (NULL, IDC_ARROW);
      wndFormClass.hbrBackground = (HBRUSH) GetStockObject (LTGRAY_BRUSH);
      wndFormClass.lpszClassName = szAppName;
      wndFormClass.lpszMenuName  = NULL;
      
      if (IS_WIN95)
	{
	  if (!RegisterWin95 (&wndFormClass))
	    return (FALSE);
	}
      else if (!RegisterClass (&wndFormClass))
	return (FALSE);
    }
  
  /* get the default GUI font */
  newFont = GetStockObject (DEFAULT_GUI_FONT); 
  hwnFromDialog = CreateWindow (szAppName, title,
				DS_MODALFRAME | WS_POPUP | 
				WS_VISIBLE | WS_CAPTION | WS_SYSMENU,
				ClickX, ClickY,
				340, 100,
				parent, NULL, hInstance, NULL);
  /* set the font of the window */
  if(newFont)
    SendMessage (hwnFromDialog, WM_SETFONT, (WPARAM) newFont, MAKELPARAM(FALSE, 0));
  
  ShowWindow (hwnFromDialog, SW_SHOWNORMAL);
  UpdateWindow (hwnFromDialog);
  
  while (GetMessage (&msg, NULL, 0, 0))
    {
      frame = GetFrameNumber (msg.hwnd);
      TranslateMessage (&msg);
      DispatchMessage (&msg);
    }
  return TRUE;
}

/*----------------------------------------------------------------------
  InitSheetDialogWndProc
  ----------------------------------------------------------------------*/
LRESULT CALLBACK InitSheetDialogWndProc (ThotWindow hwnd, UINT iMsg,
					 WPARAM wParam, LPARAM lParam)
{
  HFONT           newFont;
  ThotWindow      hwnTitle;
  ThotWindow      applyButton;
  ThotWindow      deleteButton;
  ThotWindow      doneButton;
  int             i;
  int             txtLength;

  switch (iMsg)
    {
    case WM_CREATE:
      /* get the default GUI font */
      newFont = GetStockObject (DEFAULT_GUI_FONT); 
     /* Create static window for the title */
      hwnTitle = CreateWindow ("STATIC", WIN_pAttr1->AttrName, 
			       WS_CHILD | WS_VISIBLE | SS_LEFT,
			       10, 5, 100, 15, hwnd, (HMENU) 99, 
			       ((LPCREATESTRUCT) lParam)->hInstance, NULL); 
      /* set the font of the window */
      if(newFont)
         SendMessage (hwnTitle, WM_SETFONT, (WPARAM) newFont, MAKELPARAM(FALSE, 0));
      
      /* Create Edit Window autoscrolled */
      hwnEdit = CreateWindow ("EDIT", TextAttrValue, 
			      WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT |
			        ES_AUTOHSCROLL | WS_TABSTOP,
			      10, 25, 310, 20, hwnd, (HMENU) 1,
			      ((LPCREATESTRUCT) lParam)->hInstance, NULL);
      /* set the font of the window */
      if(newFont)
         SendMessage (hwnEdit, WM_SETFONT, (WPARAM) newFont, MAKELPARAM(FALSE, 0));
				
      if (lpfnTextZoneWndProc == (WNDPROC) 0)
	lpfnTextZoneWndProc = (WNDPROC) SetWindowLong (hwnEdit, GWL_WNDPROC,
						       (DWORD) TextAttrProc);
      else
	SetWindowLong (hwnEdit, GWL_WNDPROC, (DWORD) TextAttrProc);
      
      /* Create Apply button */
      applyButton = CreateWindow ("BUTTON",
				  TtaGetMessage (LIB, TMSG_APPLY), 
				  WS_CHILD | BS_DEFPUSHBUTTON | WS_VISIBLE,
				  10, 50, 80, 20, hwnd, (HMENU) ID_APPLY,
				  ((LPCREATESTRUCT) lParam)->hInstance, NULL);
      /* set the font of the window */
      if(newFont)
         SendMessage (applyButton, WM_SETFONT, (WPARAM) newFont, MAKELPARAM(FALSE, 0));
      
      /* Create Delete Button */
      deleteButton = CreateWindow ("BUTTON",
				   TtaGetMessage (LIB, TMSG_DEL_ATTR), 
				   WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE,
				   100, 50, 120, 20, hwnd, (HMENU) ID_DELETE,
				   ((LPCREATESTRUCT) lParam)->hInstance, NULL);
      /* set the font of the window */
      if(newFont)
         SendMessage (deleteButton, WM_SETFONT, (WPARAM) newFont, MAKELPARAM(FALSE, 0));
      
      /* Create Done Button */
      doneButton = CreateWindow ("BUTTON",
				 TtaGetMessage (LIB, TMSG_DONE), 
				 WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE,
				 230, 50, 80, 20, hwnd, (HMENU) ID_DONE,
				 ((LPCREATESTRUCT) lParam)->hInstance, NULL);
      /* set the font of the window */
      if(newFont)
         SendMessage (doneButton, WM_SETFONT, (WPARAM) newFont, MAKELPARAM(FALSE, 0));
      SetFocus (hwnEdit);
      break;

    case WM_ENTER:
      txtLength = GetWindowTextLength (hwnEdit);
      if (txtLength >= LgMaxAttrText)
	txtLength = LgMaxAttrText - 1;
      GetWindowText (hwnEdit, TextAttrValue, txtLength + 1);
      i = 0;
      while (i < txtLength && TextAttrValue[i] != __CR__)
	i++;
      if (i < txtLength)
	TextAttrValue[i] = EOS;
      ThotCallback (NumMenuAttrText, STRING_DATA, TextAttrValue);
      ThotCallback (NumMenuAttr, INTEGER_DATA, (char *) 1);
      ThotCallback (NumMenuAttr, INTEGER_DATA, (char *) 0);
      DestroyWindow (hwnd);
      break;
      
    case WM_DESTROY :
      PostQuitMessage (0);
      break;
      
    case WM_COMMAND:
      switch (LOWORD (wParam))
	{
	case ID_APPLY:
	  txtLength = GetWindowTextLength (hwnEdit);
	  if (txtLength >= LgMaxAttrText)
	    txtLength = LgMaxAttrText - 1;
	  GetWindowText (hwnEdit, TextAttrValue, txtLength + 1);
	  i = 0;
	  while (i < txtLength && TextAttrValue[i] != __CR__)
	    i++;
	  if (i < txtLength)
	    TextAttrValue[i] = EOS;
	  ThotCallback (NumMenuAttrText, STRING_DATA, TextAttrValue);
	  ThotCallback (NumMenuAttr, INTEGER_DATA, (char *) 1);
	  break;
	  
	case ID_DELETE:
	  ThotCallback (NumMenuAttr, INTEGER_DATA, (char *) 2);
	  DestroyWindow (hwnd);
	  break;
	  
	case ID_DONE:
	  ThotCallback (NumMenuAttr, INTEGER_DATA, (char *) 0);
	  DestroyWindow (hwnd);
	  break;
	}
      break;
    }
  return DefWindowProc (hwnd, iMsg, wParam, lParam);
}

/*----------------------------------------------------------------------
  WIN_InitSheetDialog
  ----------------------------------------------------------------------*/
static ThotBool WIN_InitSheetDialog (ThotWindow parent)
{
  HFONT         newFont;
  WNDCLASSEX    wndSheetClass;
  char         *szAppName;
  ThotWindow    hwnSheetDialog;
  MSG           msg;
  int           frame;

  szAppName = "SheetClass";
  if (!wndSheetRegistered)
    {
      wndSheetRegistered = TRUE;
      wndSheetClass.style         = CS_HREDRAW | CS_VREDRAW;
      wndSheetClass.lpfnWndProc   = InitSheetDialogWndProc;
      wndSheetClass.cbClsExtra    = 0;
      wndSheetClass.cbWndExtra    = 0;
      wndSheetClass.hInstance     = hInstance;
      wndSheetClass.hIcon         = LoadIcon (NULL, IDI_APPLICATION);
      wndSheetClass.hCursor       = LoadCursor (NULL, IDC_ARROW);
      wndSheetClass.hbrBackground = (HBRUSH) GetStockObject (LTGRAY_BRUSH);
      wndSheetClass.lpszMenuName  = NULL;
      wndSheetClass.lpszClassName = szAppName;
      wndSheetClass.cbSize        = sizeof(WNDCLASSEX);
      wndSheetClass.hIconSm       = LoadIcon (hInstance, iconID);
      if (!RegisterClassEx (&wndSheetClass))
	return (FALSE);
    }
   
  /* get the default GUI font */
  newFont = GetStockObject (DEFAULT_GUI_FONT); 
  hwnSheetDialog = CreateWindow (szAppName, TtaGetMessage (LIB, TMSG_ATTR),
				 DS_MODALFRAME | WS_POPUP | WS_VISIBLE |
				 WS_CAPTION | WS_SYSMENU | WS_TABSTOP,
				 ClickX, ClickY,
				 340, 100,
				 parent, NULL, hInstance, NULL);
  /* set the font of the window */
  if(newFont)
    SendMessage (hwnSheetDialog, WM_SETFONT, (WPARAM) newFont, MAKELPARAM(FALSE, 0));
  ShowWindow (hwnSheetDialog, SW_SHOWNORMAL);
  UpdateWindow (hwnSheetDialog);
  while (GetMessage (&msg, NULL, 0, 0))
    {
      frame = GetFrameNumber (msg.hwnd);
      TranslateMessage (&msg);
      DispatchMessage (&msg);
    }
  return TRUE;
}

/*----------------------------------------------------------------------
  InitNumAttrDialogWndProc
  ----------------------------------------------------------------------*/
LRESULT CALLBACK InitNumAttrDialogWndProc (ThotWindow hwnd, UINT iMsg,
					   WPARAM wParam, LPARAM lParam)
{
  HFONT           newFont;
  ThotWindow      hwnTitle;
  ThotWindow      applyButton;
  ThotWindow      deleteButton;
  ThotWindow      doneButton;
  ThotBool        ok;
  int             val;

  switch (iMsg)
    {
    case WM_CREATE:
      /* get the default GUI font */
      newFont = GetStockObject (DEFAULT_GUI_FONT); 
      /* Create static window for the title */
      hwnTitle = CreateWindow ("STATIC", WIN_pAttr1->AttrName, 
			       WS_CHILD | WS_VISIBLE | SS_LEFT,
			       10, 5, 160, 240, hwnd, (HMENU) 1,
			       ((LPCREATESTRUCT) lParam)->hInstance, NULL); 
      /* set the font of the window */
      if(newFont)
         SendMessage (hwnTitle, WM_SETFONT, (WPARAM) newFont, MAKELPARAM(FALSE, 0));
      
      /* Create Edit Window autoscrolled */
      hwnEdit = CreateWindow ("EDIT", NULL, 
			      WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT |
			         ES_AUTOHSCROLL,
			      10, 20, 120, 20, hwnd, (HMENU) ID_EDITVALUE,
			      ((LPCREATESTRUCT) lParam)->hInstance, NULL);
      /* set the font of the window */
      if(newFont)
         SendMessage (hwnEdit, WM_SETFONT, (WPARAM) newFont, MAKELPARAM(FALSE, 0));
      SetDlgItemInt (hwnd, ID_EDITVALUE, formValue, TRUE);
      if (lpfnTextZoneWndProc == (WNDPROC) 0)
	lpfnTextZoneWndProc = (WNDPROC) SetWindowLong (hwnEdit, GWL_WNDPROC,
						       (DWORD) TextAttrProc);
      else
	SetWindowLong (hwnEdit, GWL_WNDPROC, (DWORD) TextAttrProc);
      
      /* Create Apply button */
      applyButton = CreateWindow ("BUTTON",
				  TtaGetMessage (LIB, TMSG_APPLY), 
				  WS_CHILD | BS_DEFPUSHBUTTON | WS_VISIBLE,
				  10, 55, 65, 25, hwnd, (HMENU) ID_APPLY,
				  ((LPCREATESTRUCT) lParam)->hInstance, NULL);
      /* set the font of the window */
      if(newFont)
         SendMessage (applyButton, WM_SETFONT, (WPARAM) newFont, MAKELPARAM(FALSE, 0));
      
      /* Create Delete Button */
      deleteButton = CreateWindow ("BUTTON",
				   TtaGetMessage (LIB, TMSG_DEL_ATTR), 
				   WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE,
				   80, 55, 120, 25, hwnd, (HMENU) ID_DELETE,
				   ((LPCREATESTRUCT) lParam)->hInstance, NULL);
      /* set the font of the window */
      if(newFont)
         SendMessage (deleteButton, WM_SETFONT, (WPARAM) newFont, MAKELPARAM(FALSE, 0));
      
      /* Create Done Button */
      doneButton = CreateWindow ("BUTTON",
				 TtaGetMessage (LIB, TMSG_DONE), 
				 WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE,
				 205, 55, 65, 25, hwnd, (HMENU) ID_DONE,
				 ((LPCREATESTRUCT) lParam)->hInstance, NULL);
      /* set the font of the window */
      if(newFont)
         SendMessage (doneButton, WM_SETFONT, (WPARAM) newFont, MAKELPARAM(FALSE, 0));
      SetFocus (hwnEdit);
      break;
      
    case WM_DESTROY :
      PostQuitMessage (0);
      break;

    case WM_ENTER:
      val = GetDlgItemInt (hwnd, ID_EDITVALUE, &ok, TRUE);
      if (ok)
	{
	  ThotCallback (NumMenuAttrNumber, INTEGER_DATA, (char *) val);
	  ThotCallback (NumMenuAttr, INTEGER_DATA, (char *) 1);
	}
      break;
      
    case WM_COMMAND:
      switch (LOWORD (wParam))
	{
	case ID_APPLY:
	  val = GetDlgItemInt (hwnd, ID_EDITVALUE, &ok, TRUE);
	  if (ok)
	    {
	      ThotCallback (NumMenuAttrNumber, INTEGER_DATA, (char *) val);
	      ThotCallback (NumMenuAttr, INTEGER_DATA, (char *) 1);
	    }
	  break;
	  
	case ID_DELETE:
	  val = GetDlgItemInt (hwnd, ID_EDITVALUE, &ok, TRUE);
	  if (ok)
	    {
	      ThotCallback (NumMenuAttrNumber, INTEGER_DATA, (char *) val);
	      ThotCallback (NumMenuAttr, INTEGER_DATA, (char *) 2);
	      DestroyWindow (hwnd);
	    }
	  break;
	  
	case ID_DONE:
	  ThotCallback (NumMenuAttr, INTEGER_DATA, (char *) 0);
	  DestroyWindow (hwnd);
	  break;
	}
      break;
    }
  return DefWindowProc (hwnd, iMsg, wParam, lParam);
}

/*----------------------------------------------------------------------
  WIN_InitNumAttrDialog
  ----------------------------------------------------------------------*/
static ThotBool WIN_InitNumAttrDialog (ThotWindow parent)
{
  HFONT         newFont;
  WNDCLASSEX    wndNumAttrClass;
  ThotWindow    hwnNumAttrDialog;
  MSG           msg;
  int           frame;

  szAppName = "NumAttrClass";
  if (!wndNumAttrRegistered)
    {
      wndNumAttrRegistered = TRUE;
      wndNumAttrClass.style         = CS_HREDRAW | CS_VREDRAW;
      wndNumAttrClass.lpfnWndProc   = InitNumAttrDialogWndProc;
      wndNumAttrClass.cbClsExtra    = 0;
      wndNumAttrClass.cbWndExtra    = 0;
      wndNumAttrClass.hInstance     = hInstance;
      wndNumAttrClass.hIcon         = LoadIcon (NULL, IDI_APPLICATION);
      wndNumAttrClass.hCursor       = LoadCursor (NULL, IDC_ARROW);
      wndNumAttrClass.hbrBackground = (HBRUSH) GetStockObject (LTGRAY_BRUSH);
      wndNumAttrClass.lpszMenuName  = NULL;
      wndNumAttrClass.lpszClassName = szAppName;
      wndNumAttrClass.cbSize        = sizeof(WNDCLASSEX);
      wndNumAttrClass.hIconSm       = LoadIcon (hInstance, iconID);
      if (!RegisterClassEx (&wndNumAttrClass))
	return (FALSE);
    }
   
  /* get the default GUI font */
  newFont = GetStockObject (DEFAULT_GUI_FONT); 
  hwnNumAttrDialog = CreateWindow (szAppName, TtaGetMessage (LIB, TMSG_ATTR),
				   DS_MODALFRAME | WS_POPUP | 
				   WS_VISIBLE | WS_CAPTION | WS_SYSMENU,
				   ClickX, ClickY,
				   285, 110,
				   parent, NULL, hInstance, NULL);
  /* set the font of the window */
  if(newFont)
    SendMessage (hwnNumAttrDialog, WM_SETFONT, (WPARAM) newFont, MAKELPARAM(FALSE, 0));
  ShowWindow (hwnNumAttrDialog, SW_SHOWNORMAL);
  UpdateWindow (hwnNumAttrDialog);
  while (GetMessage (&msg, NULL, 0, 0))
    {
      frame = GetFrameNumber (msg.hwnd);
      TranslateMessage (&msg);
      DispatchMessage (&msg);
    }
  return TRUE;
}
#endif /* _WINDOWS */

/*----------------------------------------------------------------------
   MenuValues
   builds the dialog box for capturing the values of the attribute defined
   by the pAttr1 rule.
   required specifies if it's a required attribute
   currAttr gives the current value of the attribute
  ----------------------------------------------------------------------*/
static void MenuValues (TtAttribute * pAttr1, ThotBool required,
			PtrAttribute currAttr, PtrDocument pDoc, int view)
{
   Document          doc;
#ifdef _I18N_
   char             *tmp;
#endif /* _I18N_ */
   char              bufMenu[MAX_TXT_LEN];
   char              title[MAX_NAME_LENGTH + 2];
   int               i, lgmenu, val, buttons;
   int               form, subform;

#ifdef _WINDOWS
   WIN_pAttr1 = pAttr1;
#endif /* _WINDOWS */
   doc = (Document) IdentDocument (pDoc);
   buttons = 0;
   strcpy (bufMenu, TtaGetMessage (LIB, TMSG_APPLY));
   buttons++;
   i = strlen (bufMenu) + 1;
   strcpy (&bufMenu[i], TtaGetMessage (LIB, TMSG_DEL_ATTR));
   buttons++;
   /* detruit la feuille de dialogue et la recree */
   if (required)
     {
       form = NumMenuAttrRequired;
       if (MandatoryAttrFormExists)
	 {
	   TtaUnmapDialogue (NumMenuAttrRequired);
	   TtaDestroyDialogue (NumMenuAttrRequired);
	 } 
#ifndef _WINDOWS 
       TtaNewForm (NumMenuAttrRequired, TtaGetViewFrame (doc, view),
		   TtaGetMessage (LIB, TMSG_ATTR), FALSE, 2, 'L', D_DONE);
#else  /* _WINDOWS */
       isForm = TRUE;
       WIN_InitFormDialog (TtaGetViewFrame (doc, view),
			   TtaGetMessage (LIB, TMSG_ATTR));
#endif /* _WINDOWS */
       MandatoryAttrFormExists = TRUE;
     }
   else
     {
       form = NumMenuAttr;
       if (AttrFormExists)
	 {
	 TtaUnmapDialogue (NumMenuAttr);
	 TtaDestroyDialogue (NumMenuAttr);
	 } 
#ifndef _WINDOWS
       TtaNewSheet (NumMenuAttr, TtaGetViewFrame (doc, view),
		    TtaGetMessage (LIB, TMSG_ATTR), buttons, bufMenu, FALSE, 2,
		    'L', D_DONE);
#else  /* _WINDOWS */
       isForm = FALSE;
#endif /* _WINDOWS */
       AttrFormExists = TRUE;
     }  

   strncpy (title, pAttr1->AttrName, MAX_NAME_LENGTH);
   switch (pAttr1->AttrType)
     {
     case AtNumAttr: /* attribut a valeur numerique */
       subform = form + 1;
#ifndef _WINDOWS
       TtaNewNumberForm (subform, form, title, -MAX_INT_ATTR_VAL,
			 MAX_INT_ATTR_VAL, TRUE);
       TtaAttachForm (subform);
#endif /* !_WINDOWS */
       if (currAttr == NULL)
	 i = 0;
       else
	 i = currAttr->AeAttrValue;
       
#ifndef _WINDOWS
       TtaSetNumberForm (subform, i);
#else /* !_WINDOWS */
       WIN_AtNumAttr  = TRUE;
       WIN_AtTextAttr = FALSE;
       WIN_AtEnumAttr = FALSE;
       sprintf (formRange, "%d .. %d", -MAX_INT_ATTR_VAL,
		 MAX_INT_ATTR_VAL); 
       formValue = i;
#endif /* _WINDOWS */
       break;
       
     case AtTextAttr: /* attribut a valeur textuelle */
       subform = form + 2;
       if (currAttr && currAttr->AeAttrText)
	 {
	   i = LgMaxAttrText - 2;
	   i = CopyBuffer2MBs (currAttr->AeAttrText, 0, TextAttrValue, i);
#ifdef _I18N_
	   /* convert to the dialogue encoding */
	   tmp = TtaConvertMbsToByte (TextAttrValue, TtaGetDefaultCharset ());
	   strcpy (TextAttrValue, tmp);
	   TtaFreeMemory (tmp);
#endif /* _I18N_ */
	 }
       else
	   TextAttrValue[0] = EOS;
#ifndef _WINDOWS
       TtaNewTextForm (subform, form, title, 40, 1, FALSE);
       TtaAttachForm (subform);
       TtaSetTextForm (subform, TextAttrValue);       
#else  /* _WINDOWS */
       WIN_AtNumAttr  = FALSE;
       WIN_AtTextAttr = TRUE;
       WIN_AtEnumAttr = FALSE;
#endif /* _WINDOWS */
       break;
       
     case AtEnumAttr: /* attribut a valeurs enumerees */
       subform = form + 3;
       /* cree un menu de toutes les valeurs possibles de l'attribut */
       lgmenu = 0;
       val = 0;
       /* boucle sur les valeurs possibles de l'attribut */
       while (val < pAttr1->AttrNEnumValues)
	 {
#ifdef _WINDOWS 
	   i = strlen (pAttr1->AttrEnumValue[val]) + 1; /* for EOS */
	   if (lgmenu + i < MAX_TXT_LEN)
	     {
	       strcpy (&WIN_buffMenu[lgmenu], pAttr1->AttrEnumValue[val]);
#else  /* !_WINDOWS */
	   i = strlen (pAttr1->AttrEnumValue[val]) + 2; /* for 'B' and EOS */
	   if (lgmenu + i < MAX_TXT_LEN)
	     {
	       bufMenu[lgmenu] = 'B';
	       strcpy (&bufMenu[lgmenu + 1], pAttr1->AttrEnumValue[val]);
#endif /* _WINDOWS */
	       val++;
	     } 
	   lgmenu += i;
	 }

#ifndef _WINDOWS
       /* cree le menu des valeurs de l'attribut */
       TtaNewSubmenu (subform, form, 0, title, val, bufMenu, NULL, TRUE);
       TtaAttachForm (subform);
       /* initialise le menu avec la valeur courante */
       val = -1;
       if (currAttr != NULL)
	 val = currAttr->AeAttrValue - 1;
       TtaSetMenuForm (subform, val);
#else  /* _WINDOWS */
       nbDlgItems = val;
       WIN_AtNumAttr  = FALSE;
       WIN_AtTextAttr = FALSE;
       WIN_AtEnumAttr = TRUE;
#endif /* _WINDOWS */
       break;

     case AtReferenceAttr: /* attribut reference, on ne fait rien */
       break;
       
     default: break;
     } 
} 

/*----------------------------------------------------------------------
   CallbackReqAttrMenu
   handles the callback of the menu which captures the required attributes.
  ----------------------------------------------------------------------*/
void CallbackReqAttrMenu (int ref, int val, char *txt)
{
  int      length;

  switch (ref)
    {
    case NumMenuAttrRequired:
      /* retour de la feuille de dialogue elle-meme */
      /* on detruit cette feuille de dialogue sauf si c'est */
      /* un abandon */
      if (val != 0)
	{
	  TtaDestroyDialogue (NumMenuAttrRequired);
	  MandatoryAttrFormExists = FALSE;
	}
      /* on ne fait rien d'autre : tout a ete fait par les cas */
      /* suivants */
      break;
    case NumMenuAttrNumNeeded:
      /* zone de saisie de la valeur numerique de l'attribut */
      if (val >= -MAX_INT_ATTR_VAL || val <= MAX_INT_ATTR_VAL)
	PtrReqAttr->AeAttrValue = val;
      break;
    case NumMenuAttrTextNeeded:
      /* zonee de saisie du texte de l'attribut */
      if (PtrReqAttr->AeAttrText == NULL)
	GetTextBuffer (&PtrReqAttr->AeAttrText);
      else
	ClearText (PtrReqAttr->AeAttrText);
      CopyStringToBuffer (txt, PtrReqAttr->AeAttrText, &length);
      break;
    case NumMenuAttrEnumNeeded:
      /* menu des valeurs d'un attribut a valeurs enumerees */
      val++;
      PtrReqAttr->AeAttrValue = val;
      break;
    default:
      break;
    }
}

/*----------------------------------------------------------------------
   BuildReqAttrMenu
   builds the form for capturing the value of the required
   attribute as defined by the pRuleAttr rule.
  ----------------------------------------------------------------------*/
void BuildReqAttrMenu (PtrAttribute pAttr, PtrDocument pDoc)
{
   PtrTtAttribute        pRuleAttr;

   PtrReqAttr = pAttr;
   PtrDocOfReqAttr = pDoc;
   pRuleAttr = pAttr->AeAttrSSchema->SsAttribute->TtAttr[pAttr->AeAttrNum - 1];
   /* toujours lie a la vue 1 du document */
   MenuValues (pRuleAttr, TRUE, NULL, pDoc, 1);
#ifndef _WINDOWS
   TtaShowDialogue (NumMenuAttrRequired, FALSE);
   TtaWaitShowDialogue ();
#endif /* !_WINDOWS */
}

/*----------------------------------------------------------------------
   TteItemMenuAttr 
   sends the AttrMenu.Pre message which indicates that the editor
   is going to add to the Attributes menu an item for the creation
   of an attribute of type (pSS, att) for the pEl element. It 
   returns the answer from the application.
  ----------------------------------------------------------------------*/
static ThotBool TteItemMenuAttr (PtrSSchema pSS, int att, PtrElement pEl,
				 PtrDocument pDoc)
{
   NotifyAttribute     notifyAttr;
   ThotBool            OK;

   notifyAttr.event = TteAttrMenu;
   notifyAttr.document = (Document) IdentDocument (pDoc);
   notifyAttr.element = (Element) pEl;
   notifyAttr.attribute = NULL;
   notifyAttr.attributeType.AttrSSchema = (SSchema) pSS;
   notifyAttr.attributeType.AttrTypeNum = att;
   OK = !CallEventAttribute (&notifyAttr, TRUE);
   return OK;
}


/*----------------------------------------------------------------------
   BuildAttrMenu
   builds the Attributes menu and returns the number of attributes added
   to the composite menu.
   Returns also the number of events attibutes and updates the corresponding
   buffer.
  ----------------------------------------------------------------------*/
static int BuildAttrMenu (char *bufMenu, PtrDocument pDoc, int *nbEvent,
			  char *bufEventAttr)
{
  PtrDocument         SelDoc;
  PtrElement          firstSel, lastSel;
  PtrSSchema          pSS;
  PtrSSchema          pSchExt;
  PtrAttribute        pAttr;
  PtrSRule            pRe1;
  PtrTtAttribute      pAt;
  char                tempBuffer[MAX_NAME_LENGTH + 1];
  int                 i, j, k;
  int                 firstChar, lastChar;
  int                 lgmenu = 0, lgsubmenu;
  int                 att, nbOfEntries;
  ThotBool            selectionOK, isNew;

  nbOfEntries = 0;
  *nbEvent = 0;
  /* demande quelle est la selection courante */
  selectionOK = GetCurrentSelection (&SelDoc, &firstSel, &lastSel, &firstChar,
				     &lastChar);
  if (selectionOK && firstSel == lastSel && firstSel->ElParent == NULL)
    /* the Document element is selected. It can't accept any attribute */
    selectionOK = FALSE;
  if (selectionOK && ElementIsReadOnly (firstSel))
    /* the selected element is read-only. Don't change its attributes */
    selectionOK = FALSE;
  if (selectionOK && SelDoc == pDoc && firstSel)
    /* il y a une selection et elle est dans le document traite' */
    {
      /* cherche les attributs globaux definis dans le schema de structure */
      /* du premier element selectionne' et dans les extrensions de ce schema*/
      pSS = firstSel->ElStructSchema;/* schema de struct de l'element courant*/
      if (pSS)
	{
	  /* on parcourt toutes les extensions de ce schema de structure */
	  do
	    {
	      /* on a deja traite' ce schema de structure ? */
	      isNew = TRUE;
	      for (i = 0; i < nbOfEntries; i++)
		if (pSS == AttrStruct[i])
		  /* already known */
		  isNew = FALSE;
	      
	      if (isNew)
		/* the element uses a new structure schema */
		/* add all global attributes of this schema in the table */
		{
		  att = 0;
		  while (att < pSS->SsNAttributes &&
			 nbOfEntries - *nbEvent < MAX_MENU)
		    {
		      att++;
		      /* skip local attributes */
		      if (pSS->SsAttribute->TtAttr[att - 1]->AttrGlobal &&
			  /* and invisible attributes */
			  !AttrHasException (ExcInvisible, att, pSS))
			/* skip the attribute Language execpt the first time */
			if (nbOfEntries == 0 || att != 1)
			  if (TteItemMenuAttr (pSS, att, firstSel, SelDoc))
			    {
			      /* keep in mind the structure schema and */
			      /* the attribute number of this new entry */
			      AttrStruct[nbOfEntries] = pSS;
			      AttrNumber[nbOfEntries] = att;
			      AttrOblig[nbOfEntries] = FALSE;
			      /* is it an event attribute */
			      AttrEvent[nbOfEntries] =
				AttrHasException (ExcEventAttr, att, pSS);
			      if (AttrEvent[nbOfEntries])
				(*nbEvent)++;
			      nbOfEntries++;
			    }
		    }
		}
	      /* next extension schema */
	      pSS = pSS->SsNextExtens;
	    }
	  while (pSS != NULL);
	}

      /* cherche les attributs locaux du premier element selectionne' */
      pSS = firstSel->ElStructSchema;
      if (pSS != NULL)
	{
	  pRe1 = pSS->SsRule->SrElem[firstSel->ElTypeNumber - 1];
	  pSchExt = SelDoc->DocSSchema;
	  do
	    {
	      if (pRe1 != NULL)
		/* prend les attributs locaux definis dans cette regle */
		for (att = 0; att < pRe1->SrNLocalAttrs; att++)
		  if (nbOfEntries - *nbEvent < MAX_MENU &&
		      !AttrHasException (ExcInvisible,
					 pRe1->SrLocalAttr->Num[att], pSS) &&
		      TteItemMenuAttr (pSS, pRe1->SrLocalAttr->Num[att],
				       firstSel, SelDoc))
		    {
		      /* conserve le schema de structure et le numero */
		      /* d'attribut de cette nouvelle entree du menu */
		      AttrStruct[nbOfEntries] = pSS;
		      AttrNumber[nbOfEntries] = pRe1->SrLocalAttr->Num[att];
		      AttrOblig[nbOfEntries] = pRe1->SrRequiredAttr->Bln[att];
		      /* is it an event attribute */
		      AttrEvent[nbOfEntries] = AttrHasException (ExcEventAttr,
					     pRe1->SrLocalAttr->Num[att], pSS);
		      if (AttrEvent[nbOfEntries])
			(*nbEvent)++;
		      nbOfEntries++;
		    }
	      /* passe a l'extension suivante du schema du document */
	      pSchExt = pSchExt->SsNextExtens;
	      /* cherche dans cette extension de schema la regle d'extension */
	      /* pour le premier element selectionne' */
	      if (pSchExt != NULL)
		{
		  pSS = pSchExt;
		  pRe1 = ExtensionRule (firstSel->ElStructSchema,
					firstSel->ElTypeNumber, pSchExt);
		}
	    }
	  while (pSchExt != NULL);
	}
      /* la table contient tous les attributs applicables aux elements */
      /* selectionnes */
      /* add attributes attached to the element that are not yet in
	 the table */
      pAttr = firstSel->ElFirstAttr;
      while (pAttr)
	{
	  isNew = TRUE;
	  for (att = 0; att < nbOfEntries && isNew; att++)
	    {
	      if (pAttr->AeAttrNum == AttrNumber[att] &&
		  pAttr->AeAttrSSchema == AttrStruct[att])
		isNew = FALSE;
            }
	  if (isNew)
	    {
	      if (nbOfEntries - *nbEvent < MAX_MENU &&
		  !AttrHasException (ExcInvisible, pAttr->AeAttrNum,
				     pAttr->AeAttrSSchema) &&
		  TteItemMenuAttr (pAttr->AeAttrSSchema, pAttr->AeAttrNum,
				   firstSel, SelDoc))
		{
		  /* conserve le schema de structure et le numero */
		  /* d'attribut de cette nouvelle entree du menu */
		  AttrStruct[nbOfEntries] = pAttr->AeAttrSSchema;
		  AttrNumber[nbOfEntries] = pAttr->AeAttrNum;
		  AttrOblig[nbOfEntries] = FALSE;
		  /* is it an event attribute? */
		  AttrEvent[nbOfEntries] = AttrHasException (ExcEventAttr,
				       pAttr->AeAttrNum, pAttr->AeAttrSSchema);
		  if (AttrEvent[nbOfEntries])
		    (*nbEvent)++;
		  nbOfEntries++;
		}
	    }
          pAttr = pAttr->AeNext;
	}

      /* build the menu according to the table */
      GetAttribute (&pAttr);
      if (nbOfEntries > 0)
	{
	  lgmenu = 0;
	  lgsubmenu = 0;
	  k = 0;
	  j = 0;
	  /* met les noms des attributs de la table dans le menu */
	  for (att = 0; att < nbOfEntries; att++)
	    {
	      pAt = AttrStruct[att]->SsAttribute->TtAttr[AttrNumber[att]-1];
	      pAttr->AeAttrSSchema = AttrStruct[att];
	      pAttr->AeAttrNum = AttrNumber[att];
	      pAttr->AeDefAttr = FALSE;
	      if (pAt->AttrType == AtEnumAttr && pAt->AttrNEnumValues == 1)
		/* attribut enumere' a une seule valeur (attribut booleen) */
		sprintf (tempBuffer, "T%s", pAt->AttrName);
	      else
		sprintf (tempBuffer, "T%s...", pAt->AttrName);
	      i = strlen (tempBuffer) + 1;
	      if (AttrEvent[att])
		{
		  if (lgsubmenu + i < MAX_TXT_LEN)
		    strcpy (&bufEventAttr[lgsubmenu], tempBuffer);
		  lgsubmenu += i;
		  /* mark all active enties*/
		  if (AttributeValue (firstSel, pAttr) != NULL)
		    ActiveEventAttr[k] = 1;
		  else
		    ActiveEventAttr[k] = 0;
		  AttrEventNumber[k] = att;
		  k++;
		}
	      else
		{
		  if (lgmenu + i < MAX_TXT_LEN)
		    strcpy (&bufMenu[lgmenu], tempBuffer);
		  lgmenu += i;
		  /* mark all active enties*/
		  if (AttributeValue (firstSel, pAttr) != NULL)
		    ActiveAttr[j] = 1;
		  else
		    ActiveAttr[j] = 0;
		  j++;
		}
	    }
	}
      DeleteAttribute (NULL, pAttr);

      if (*nbEvent > 0)
	{
	  /* add the event entry if needed */
	  sprintf (tempBuffer, "M%s", TtaGetMessage (LIB, TMSG_EVENTS));
	  i = strlen (tempBuffer) + 1;
	  if (lgmenu + i < MAX_TXT_LEN)
	    {
	      strcpy (&bufMenu[lgmenu], tempBuffer);
	      nbOfEntries++;
	    }
	}
    }

  return (nbOfEntries - *nbEvent);
}


/*----------------------------------------------------------------------
   UpdateAttrMenu                                                       
   Updates the Attributes menu of all open frames belonging to document
   pDoc.
  ----------------------------------------------------------------------*/
void UpdateAttrMenu (PtrDocument pDoc)
{
  Document            document;
  Menu_Ctl           *pMenu;
  char                bufMenuAttr[MAX_TXT_LEN];
  char                bufEventAttr[MAX_TXT_LEN];
  int                 view, menu, menuID;
  int                 frame, ref, nbEvent;
  int                 nbItemAttr, i, max;
#ifdef _WINDOWS
  int                 nbOldItems;
#endif /* _WINDOWS */

  /* Compose le menu des attributs */
  if (pDoc == SelectedDocument && !pDoc->DocReadOnly)
    nbItemAttr = BuildAttrMenu (bufMenuAttr, pDoc, &nbEvent, bufEventAttr);
  else
    {
      nbItemAttr = 0;
      nbEvent = 0;
    }

  document = (Document) IdentDocument (pDoc);
  /* Traite toutes les vues de l'arbre principal */
  for (view = 1; view <= MAX_VIEW_DOC; view++)
    {
      frame = pDoc->DocViewFrame[view - 1];
      if (frame != 0 && FrameTable[frame].MenuAttr != -1)
	{
#ifdef _WINDOWS 
	  currentFrame = frame;
#endif /* _WINDOWS */
	  menuID = FrameTable[frame].MenuAttr;
	  menu = FindMenu (frame, menuID, &pMenu) - 1;
	  ref = (menu * MAX_ITEM) + frame + MAX_LocalMenu;
	  if (pDoc != SelectedDocument || nbItemAttr == 0)
	    {
	      /* le menu Attributs contient au moins un attribut */
	      TtaSetMenuOff (document, view, menuID);
	      TtaDestroyDialogue (ref);
#ifdef _WINDOWS
		  /* Remove that reference in the window list of catalogues */
		  CleanFrameCatList (frame, ref);
#endif /* _WINDOWS */
	    }
	  else
	    {
#ifdef _WINDOWS
	      nbOldItems = GetMenuItemCount (FrameTable[frame].WdMenus[menu]);
	      for (i = 0; i < nbOldItems; i ++)
		{
		  if (!DeleteMenu (FrameTable[frame].WdMenus[menu], ref + i,
				   MF_BYCOMMAND))
		    DeleteMenu (FrameTable[frame].WdMenus[menu], i,
				MF_BYPOSITION);
		}
#endif /* _WINDOWS */
	      if (EventMenu[frame - 1] != 0)
		{
		  /* destroy the submenu event */
		  TtaDestroyDialogue (EventMenu[frame - 1]);
#ifdef _WINDOWS
		  /* Remove that reference in the window list of catalogues */
		  CleanFrameCatList (frame, EventMenu[frame - 1]);
		  if (subMenuID[frame])
		    DeleteMenu (FrameTable[frame].WdMenus[menu], subMenuID[frame],
			            MF_BYCOMMAND);
		  else 
		    subMenuID[frame] = 0;
#endif /* _WINDOWS */
		  EventMenu[frame - 1] = 0;
		}
	      TtaNewPulldown (ref, FrameTable[frame].WdMenus[menu], NULL,
			      nbItemAttr, bufMenuAttr, NULL);
	      if (nbEvent != 0)
		{
		  /* there is a submenu of event attributes */
		  EventMenu[frame - 1] = (nbItemAttr * MAX_MENU * MAX_ITEM) + ref;
		  TtaNewSubmenu (EventMenu[frame - 1], ref, nbItemAttr - 1,
				 NULL, nbEvent, bufEventAttr, NULL, FALSE);
		  /* post active attributes */
		  for (i = 0; i < nbEvent; i++)
#ifdef _WINDOWS
		    WIN_TtaSetToggleMenu (EventMenu[frame - 1], i,
		       (ThotBool) (ActiveEventAttr[i] == 1), FrMainRef[frame]);
#else  /* !_WINDOWS */
		  TtaSetToggleMenu (EventMenu[frame - 1], i, (ActiveEventAttr[i] == 1));
#endif /* _WINDOWS */
		}

	      /* post active attributes */
	      max = nbItemAttr;
	      if (nbEvent != 0)
		/* except the submenu entry */
		max--;
	      for (i = 0; i < max; i++)
#ifdef _WINDOWS
		WIN_TtaSetToggleMenu (ref, i, (ThotBool) (ActiveAttr[i] == 1), FrMainRef[frame]);
#else  /* !_WINDOWS */
	      TtaSetToggleMenu (ref, i, (ActiveAttr[i] == 1));
#endif /* _WINDOWS */
	      TtaSetMenuOn (document, view, menuID);
	    }
	}
    }
}

/*----------------------------------------------------------------------
   AttachAttrToElem attachs the attribute to the element
  ----------------------------------------------------------------------*/
static void AttachAttrToElem (PtrAttribute pAttr, PtrElement pEl, PtrDocument pDoc)
{
  Language            lang;
  PtrAttribute        pAttrAsc;
  PtrElement          pElAttr;
  char                text[100];

  /* On ne traite pas les marques de page */
  if (!pEl->ElTerminal || pEl->ElLeafType != LtPageColBreak)
    {
      if (pAttr->AeAttrNum == 1)
   	/* c'est l'attribut langue */
	{
   	  /* change la langue de toutes les feuilles de texte du sous-arbre */
   	  /* de l'element */
   	  if (pAttr->AeAttrText)
	    {
	      CopyBuffer2MBs (pAttr->AeAttrText, 0, text, 99);
	      lang = TtaGetLanguageIdFromName (text);
	    }
   	  else
	    /* c'est une suppression de l'attribut Langue */
   	    {
	      lang = TtaGetDefaultLanguage ();		/* langue par defaut */
	      /* on cherche si un ascendant porte l'attribut Langue */
	      if (pEl->ElParent != NULL)
		pAttrAsc = GetTypedAttrAncestor (pEl->ElParent, 1, NULL,
						 &pElAttr);
	      else
		pAttrAsc = GetTypedAttrAncestor (pEl->ElParent, 1, NULL,
						 &pElAttr);
	      
	      if (pAttrAsc && pAttrAsc->AeAttrText)
		{
		  /* un ascendant definit la langue, on prend cette langue */
		  CopyBuffer2MBs (pAttrAsc->AeAttrText, 0, text, 99);
		  lang = TtaGetLanguageIdFromName (text);
		}
   	    }
   	  ChangeLanguage (pDoc, pEl, lang, FALSE);
	}
      
      /* met la nouvelle valeur de l'attribut dans l'element et */
      /* applique les regles de presentation de l'attribut a l'element */
      AttachAttrWithValue (pEl, pDoc, pAttr);
      /* special attributes */
      if (ThotLocalActions[T_attrtable])
   	(*ThotLocalActions[T_attrtable]) (pEl, pAttr, pDoc);
    }
}


/*----------------------------------------------------------------------
   AttachAttrToRange applique l'attribut pAttr a une partie de document
  ----------------------------------------------------------------------*/
static void AttachAttrToRange (PtrAttribute pAttr, int lastChar, 
			       int firstChar, PtrElement pLastSel, 
			       PtrElement pFirstSel, PtrDocument pDoc,
			       ThotBool reDisplay)
{
   PtrElement          pEl;

   /* eteint d'abord la selection */
   TtaClearViewSelections ();
   /* Coupe les elements du debut et de la fin de la selection s'ils */
   /* sont partiellement selectionnes */
   IsolateSelection (pDoc, &pFirstSel, &pLastSel, &firstChar, &lastChar, TRUE);
   /* start an operation sequence in editing history */
   if (ThotLocalActions[T_openhistory] != NULL)
     (*ThotLocalActions[T_openhistory]) (pDoc, pFirstSel, pLastSel, NULL,
					 firstChar, lastChar);
   /* parcourt les elements selectionnes */
   pEl = pFirstSel;
   while (pEl != NULL)
     {
	AttachAttrToElem (pAttr, pEl, pDoc);
	/* cherche l'element a traiter ensuite */
	pEl = NextInSelection (pEl, pLastSel);
     }
   /* close the editing sequence */
   if (ThotLocalActions[T_closehistory] != NULL)
	(*ThotLocalActions[T_closehistory]) (pDoc);
   /* parcourt a nouveau les elements selectionnes pour fusionner les */
   /* elements voisins de meme type ayant les memes attributs, reaffiche */
   /* toutes les vues et retablit la selection */
   if (reDisplay)
     SelectRange (pDoc, pFirstSel, pLastSel, firstChar, lastChar);
}

/*----------------------------------------------------------------------
   CallbackValAttrMenu
   handles the callback of the form which captures the attribute values.
   Applies to the selected elements the attributes chosen by the user.
   ref: reference to the dialogue element who called back this function
   valmenu: selected or captured value in this dialogue element
   valtexte: pointer to the captured text in this dialogue element
  ----------------------------------------------------------------------*/
void CallbackValAttrMenu (int ref, int valmenu, char *valtext)
{
  PtrDocument         SelDoc;
  PtrElement          firstSel, lastSel;
  PtrAttribute        pAttrNew;
  DisplayMode         dispMode = DeferredDisplay;
  Document            doc = 0;
#ifdef _I18N_
   char              *tmp;
#endif /* _I18N_ */
  int                 firstChar, lastChar;
  int                 act;
  ThotBool            lock = TRUE;

  act = 1; /* apply by default */
  switch (ref)
    {
    case NumMenuAttrNumber:
      /* valeur d'un attribut numerique */
      NumAttrValue = valmenu;
      act = 0;
      break;
    case NumMenuAttrText:
      /* valeur d'un attribut textuel */
      strncpy (TextAttrValue, valtext, LgMaxAttrText);
      act = 0;
      break;
    case NumMenuAttrEnum:
      /* numero de la valeur d'un attribut enumere' */
      NumAttrValue = valmenu + 1;
      break;
    case NumMenuAttr:
      /* retour de la feuille de dialogue elle-meme */
      if (valmenu == 0 || valmenu == 2)
	/* fermeture de la feuille de dialogue */
	{
	  /* on detruit la feuille de dialogue */
	  TtaDestroyDialogue (NumMenuAttr);
	  AttrFormExists = FALSE;
	}
      act = valmenu;
      break;
    }

  if (act > 0)
    {
      /* ce n'est pas une simple fermeture de la feuille de dialogue */
      /* demande quelle est la selection courante */
      if (GetCurrentSelection (&SelDoc, &firstSel, &lastSel, &firstChar,
			       &lastChar))
	/* il y a bien une selection */
	{
	  /* on ne fait rien si le document ou` se trouve la selection
	     n'utilise pas le schema de structure qui definit l'attribut */
	  if (SchCurrentAttr &&
	      GetSSchemaForDoc (SchCurrentAttr->SsName, SelDoc))
	    {
	      /* lock tables formatting */
	      if (ThotLocalActions[T_islock])
		{
		  (*ThotLocalActions[T_islock]) (&lock);
		  if (!lock)
		    {
		      doc = IdentDocument (SelDoc);
		      dispMode = TtaGetDisplayMode (doc);
		      if (dispMode == DisplayImmediately)
			TtaSetDisplayMode (doc, DeferredDisplay);
		      /* table formatting is not locked, lock it now */
		      (*ThotLocalActions[T_lock]) ();
		    }
		}
	      GetAttribute (&pAttrNew);
	      pAttrNew->AeAttrSSchema = SchCurrentAttr;
	      pAttrNew->AeAttrNum = NumCurrentAttr;
	      pAttrNew->AeDefAttr = FALSE;
	      pAttrNew->AeAttrType = SchCurrentAttr->SsAttribute->TtAttr[NumCurrentAttr - 1]->AttrType;
	      
	      switch (pAttrNew->AeAttrType)
		{
		case AtNumAttr:
		  if (act == 2)
		    /* Supprimer l'attribut */
		    pAttrNew->AeAttrValue = MAX_INT_ATTR_VAL + 1;
		  else
		    /* la valeur saisie devient la valeur courante */
		    pAttrNew->AeAttrValue = NumAttrValue;
		  /* applique les attributs a la partie selectionnee */
		  AttachAttrToRange (pAttrNew, lastChar, firstChar, lastSel,
				     firstSel, SelDoc, TRUE);
		  break;
		    
		case AtTextAttr:
		  if (act == 2)
		    /* suppression de l'attribut */
		    pAttrNew->AeAttrText = NULL;
		  else
		    {
		      /* la valeur saisie devient la valeur courante */
		      if (pAttrNew->AeAttrText == NULL)
			GetTextBuffer (&(pAttrNew->AeAttrText));
		      else
			ClearText (pAttrNew->AeAttrText);
#ifdef _I18N_
		      tmp = TtaConvertByteToMbs (TextAttrValue, TtaGetDefaultCharset ());
		      CopyMBs2Buffer (tmp, pAttrNew->AeAttrText, 0, strlen (tmp));
		      TtaFreeMemory (tmp);
#else /* _I18N_ */
		      CopyMBs2Buffer (TextAttrValue, pAttrNew->AeAttrText, 0,
				      strlen (TextAttrValue));
#endif /* _I18N_ */
		    }
		  /* applique les attributs a la partie selectionnee */
		  AttachAttrToRange (pAttrNew, lastChar, firstChar, lastSel,
				     firstSel, SelDoc, TRUE);
		  break;
		  
		case AtReferenceAttr:		    
		  break;
		  
		case AtEnumAttr:
		  if (act == 2)
		    /* suppression de l'attribut */
		    pAttrNew->AeAttrValue = 0;
		  else
		    /* la valeur choisie devient la valeur courante */
		    pAttrNew->AeAttrValue = NumAttrValue;
		  /* applique les attributs a la partie selectionnee */
		  AttachAttrToRange (pAttrNew, lastChar, firstChar, lastSel,
				     firstSel, SelDoc, TRUE);
		  break;
		  
		default:
		  break;
		}
	      if (!lock)
		{
		  /* unlock table formatting */
		  (*ThotLocalActions[T_unlock]) ();
		  if (dispMode == DisplayImmediately)
		    TtaSetDisplayMode (doc, DisplayImmediately);
		}
	      UpdateAttrMenu (SelDoc);
	      DeleteAttribute (NULL, pAttrNew);
	    }
	  DocCurrentAttr = NULL;
	}
    }
}

/*----------------------------------------------------------------------
   CallbackAttrMenu 
   handles the callbacks of the "Attributes" menu: creates a
   form to capture the value of the chosen attribute.
  ----------------------------------------------------------------------*/
void CallbackAttrMenu (int refmenu, int att, int frame)
{
  PtrTtAttribute      pAttr;
  PtrAttribute        pAttrNew, currAttr;
  PtrDocument         SelDoc;
  PtrElement          firstSel, lastSel;
  PtrReference        Ref;
  Document            doc;
  View                view;
  int                 item, i;
  int                 firstChar, lastChar;
#ifdef _WINDOWS
  int                 currAttrVal = -1;
#endif /* _WINDOWS */

  FrameToView (frame, &doc, &view);
  item = att;
  /* get the right entry in the attributes list */
  if (refmenu == EventMenu[frame - 1])
    att = AttrEventNumber[att];
  else
    {
      i = 0;
      while (i <= att)
	{
	  if (AttrEvent[i])
	    att++;
	  i++;
	}
    }
  if (att >= 0)
    if (GetCurrentSelection (&SelDoc, &firstSel, &lastSel, &firstChar,
			     &lastChar))
      {
	GetAttribute (&pAttrNew);
	pAttrNew->AeAttrSSchema = AttrStruct[att];
	pAttrNew->AeAttrNum = AttrNumber[att];
	pAttrNew->AeDefAttr = FALSE;
	pAttr = AttrStruct[att]->SsAttribute->TtAttr[AttrNumber[att] - 1];
	pAttrNew->AeAttrType = pAttr->AttrType;
	if (pAttr->AttrType == AtReferenceAttr)
	  {
	    /* attache un bloc reference a l'attribut */
	    GetReference (&Ref);
	    pAttrNew->AeAttrReference = Ref;
	    pAttrNew->AeAttrReference->RdElement = NULL;
	    pAttrNew->AeAttrReference->RdAttribute = pAttrNew;
	    /* demande a l'utilisateur l'element reference' */
	    if (LinkReference (firstSel, pAttrNew, SelDoc))
	      if (ThotLocalActions[T_checkextens] != NULL)
		(*ThotLocalActions[T_checkextens])
		  (pAttrNew, firstSel, lastSel, FALSE);
	    /* applique l'attribut a la partie selectionnee */
	    AttachAttrToRange (pAttrNew, lastChar, firstChar, lastSel,
			       firstSel, SelDoc, TRUE);
	    UpdateAttrMenu (SelDoc);
	  }
	else
	  {
	    /* cherche la valeur de cet attribut pour le premier element */
	    /* selectionne' */
	    currAttr = AttributeValue (firstSel, pAttrNew);
#ifdef _WINDOWS
	    if (currAttr == NULL)
	      currAttrVal = -1;
	    else 
	      currAttrVal = currAttr->AeAttrValue;
#endif /* _WINDOWS */
	    if (pAttrNew->AeAttrNum == 1)
	      /* that's the language attribute */
	      {
		InitFormLanguage (doc, view, firstSel, currAttr);
		/* memorise l'attribut concerne' par le formulaire */
		SchCurrentAttr = pAttrNew->AeAttrSSchema;
		NumCurrentAttr = 1;
		DocCurrentAttr = LoadedDocument[doc - 1];
		/* restaure l'etat courant du toggle */
#ifdef _WINDOWS
		CreateLanguageDlgWindow (TtaGetViewFrame (doc, view), 
					 TtaGetMessage (LIB, TMSG_LANGUAGE),
					 TtaGetMessage (LIB, TMSG_LANG_OF_EL), 
					 WIN_nbItem, WIN_buffMenu, WIN_Lab, 
					 (int)WIN_Language);
#else /* _WINDOWS */
		if (ActiveAttr[item] == 0)
		  TtaSetToggleMenu (refmenu, item, FALSE);
		else
		  TtaSetToggleMenu (refmenu, item, TRUE);
#endif /* _WINDOWS */
	      }
	    else if (pAttr->AttrType == AtEnumAttr &&
		     pAttr->AttrNEnumValues == 1)
	      /* attribut enumere' a une seule valeur(attribut booleen) */
	      {
		if (currAttr == NULL)
		  /* le premier element selectionne' n'a pas cet */
		  /* attribut. On le lui met */
		  pAttrNew->AeAttrValue = 1;
		else
		  /* suppression de l'attribut */
		  pAttrNew->AeAttrValue = 0;
		/* applique l'operation a la partie selectionnee */
		AttachAttrToRange (pAttrNew, lastChar, firstChar, lastSel,
				   firstSel, SelDoc, TRUE);
	      }
	    else
	      {
		/* construit le formulaire de saisie de la valeur de */
		/* l'attribut */
		MenuValues (pAttr, FALSE, currAttr, SelDoc, view);
		/* memorise l'attribut concerne' par le formulaire */
		SchCurrentAttr = AttrStruct[att];
		NumCurrentAttr = AttrNumber[att];
		DocCurrentAttr = LoadedDocument[doc - 1];
		/* register the current attribut */
		CurrentAttr = att;
		/* restore the toggle state */
#ifndef _WINDOWS 
		/*#ifndef _GTK*/
		if (ActiveAttr[item] == 0)
		  TtaSetToggleMenu (refmenu, item, FALSE);
		else
		  TtaSetToggleMenu (refmenu, item, TRUE);
		/*#endif*/
		/* display the form */
		TtaShowDialogue (NumMenuAttr, TRUE);
#else /* _WINDOWS */
		if (WIN_AtNumAttr) 
		  WIN_InitNumAttrDialog (TtaGetViewFrame (doc, view));
		else if (WIN_AtTextAttr && !isForm) 
		  WIN_InitSheetDialog (TtaGetViewFrame (doc, view));
		else if (WIN_AtEnumAttr) 
                  CreateAttributeDlgWindow (pAttr->AttrName, currAttrVal,
					    nbDlgItems, WIN_buffMenu);
#endif /* _WINDOWS */
	      }
	    DeleteAttribute (NULL, pAttrNew);
	  }
      }
}

/*----------------------------------------------------------------------
   CallbackLanguageMenu
   handles the callbacks of the Language form.
  ----------------------------------------------------------------------*/
void CallbackLanguageMenu (int ref, int val, char *txt)
{
  switch (ref)
    {
    case NumSelectLanguage:
      /* retour de la langue choisie par l'utilisateur */
      if (txt == NULL)
	TextAttrValue[0] = EOS;
      else
	  strncpy (TextAttrValue, TtaGetLanguageCodeFromName (txt), LgMaxAttrText);
#ifndef _WINDOWS 
      TtaNewLabel (NumLabelHeritedLanguage, NumFormLanguage, "");
#endif /* _WINDOWS */
      CallbackValAttrMenu (NumMenuAttr, 1, NULL);
      break;
    case NumFormLanguage:
      /* retour du formulaire lui-meme */
      switch (val)
	{
	case 0:
	  /* abandon du formulaire */
	  break;
	case 1:
	case 2:
	  /* appliquer la nouvelle valeur */
	  CallbackValAttrMenu (NumMenuAttr, val, NULL);
	  break;
	}
      break;
    }
}

/*----------------------------------------------------------------------
   CloseAttributeDialogues
   Closes all dialogue boxes related with attribute input that are
   associated with document pDoc.
  ----------------------------------------------------------------------*/
void CloseAttributeDialogues (PtrDocument pDoc)
{
 if (PtrDocOfReqAttr == pDoc)
   {
     TtaUnmapDialogue (NumMenuAttrRequired);
     TtaDestroyDialogue (NumMenuAttrRequired);
     PtrReqAttr = NULL;
     PtrDocOfReqAttr = NULL;
   }
 if (DocCurrentAttr == pDoc)
   {
     if (NumCurrentAttr == 1)
       {
	 TtaUnmapDialogue (NumFormLanguage);
	 TtaDestroyDialogue (NumFormLanguage);
       }
     else
       {
	 TtaUnmapDialogue (NumMenuAttr);
	 TtaDestroyDialogue (NumMenuAttr);
       }
     SchCurrentAttr = NULL;
     DocCurrentAttr = NULL;
   }
}

/*----------------------------------------------------------------------
   AttributeMenuLoadResources
   connects the local actions.
  ----------------------------------------------------------------------*/
void                AttributeMenuLoadResources ()
{
  int i;

  if (ThotLocalActions[T_chattr] == NULL)
    {
      /* Connecte les actions de selection */
      TteConnectAction (T_chattr, (Proc) UpdateAttrMenu);
      TteConnectAction (T_rattr, (Proc) CallbackAttrMenu);
      TteConnectAction (T_rattrval, (Proc) CallbackValAttrMenu);
      TteConnectAction (T_rattrlang, (Proc) CallbackLanguageMenu);
      TteConnectAction (T_attrreq, (Proc) BuildReqAttrMenu);
      TteConnectAction (T_rattrreq, (Proc) CallbackReqAttrMenu);
      for (i = 0; i < MAX_FRAME; i++)
	EventMenu[i] = 0;
    }
}
