/* * * COPYRIGHT INRIA and W3C, 1996-2008 * Please first read the full copyright statement in file COPYRIGHT. * */ /* * Authors: Francesc Campoy Flores * Emilien Kia * */ #define THOT_EXPORT extern #include "amaya.h" #include "document.h" #include "undo.h" #include "containers.h" #include "Elemlist.h" #include "templates.h" #ifdef TEMPLATES #include "Template.h" #include "templateDeclarations.h" #include "html2thot_f.h" #include "HTMLedit_f.h" #include "templates_f.h" #include "templateUtils_f.h" #include "templateLoad_f.h" #include "templateDeclarations_f.h" #include "templateInstantiate_f.h" #include "Templatebuilder_f.h" #include "appdialogue_wx.h" #include "init_f.h" #include "wxdialogapi_f.h" #include "AHTURLTools_f.h" #endif /* TEMPLATES */ #include "fetchXMLname_f.h" #include "MENUconf.h" /* Paths from which looking for templates.*/ static Prop_Templates_Path *TemplateRepositoryPaths; // register the parent repeat of the new created use static Element Creating_repeat = NULL; /*---------------------------------------------------------------------- IsTemplateInstanceDocument: Test if a document is a template instance doc : Document to test return : TRUE if the document is a template instance ----------------------------------------------------------------------*/ ThotBool IsTemplateInstanceDocument(Document doc) { #ifdef TEMPLATES XTigerTemplate t = GetXTigerDocTemplate(doc); if (t) return ((t->state & templInstance) != 0); else return FALSE; #else /* TEMPLATES */ return FALSE; #endif /* TEMPLATES */ } /*---------------------------------------------------------------------- IsTemplateDocument: Test if a document is a template (not an instance) doc : Document to test return : TRUE if the document is a template ----------------------------------------------------------------------*/ ThotBool IsTemplateDocument (Document doc) { #ifdef TEMPLATES XTigerTemplate t = GetXTigerDocTemplate(doc); if (t) return ((t->state & templTemplate) != 0); else return FALSE; #endif /* TEMPLATES */ return FALSE; } /*---------------------------------------------------------------------- GetUsedTypeName returns the name of the current used type or the first name of the types attribute The returned string must be freed ----------------------------------------------------------------------*/ char *GetUsedTypeName (Element el) { char *name = NULL; #ifdef TEMPLATES char *ptr; if (IsTemplateElement (el)) { name = GetAttributeStringValueFromNum (el, Template_ATTR_currentType, NULL); if (name == NULL) { // use the first type name = GetAttributeStringValueFromNum (el, Template_ATTR_types, NULL); if (name) { ptr = strstr (name, " "); if (ptr) *ptr = EOS; } return name; } } #endif /* TEMPLATES */ return name; } /*---------------------------------------------------------------------- Test if a document is an internal template. (no instance is opened and it is not edited) ----------------------------------------------------------------------*/ ThotBool IsInternalTemplateDocument(Document doc) { #ifdef TEMPLATES XTigerTemplate t = GetXTigerDocTemplate(doc); if (t) return (t->state&templInternal)!=0; else return FALSE; #else /* TEMPLATES */ return FALSE; #endif /* TEMPLATES */ } /*---------------------------------------------------------------------- Return the URL of an instance template. ----------------------------------------------------------------------*/ char* GetDocumentInstanceTemplateUrl(Document doc) { #ifdef TEMPLATES XTigerTemplate t = GetXTigerDocTemplate(doc); if (t) return t->base_uri; else return FALSE; #else /* TEMPLATES */ return NULL; #endif /* TEMPLATES */ } /*---------------------------------------------------------------------- CheckPromptIndicator checks if the element is a prompt text unit ----------------------------------------------------------------------*/ ThotBool CheckPromptIndicator (Element el, Document doc) { #ifdef TEMPLATES ElementType elType; Element parent; AttributeType attrType; Attribute att; SSchema templateSSchema; if (!IsTemplateInstanceDocument(doc)) /* let Thot perform normal operation */ return FALSE; elType = TtaGetElementType (el); templateSSchema = TtaGetSSchema ("Template", doc); if (elType.ElTypeNum == HTML_EL_TEXT_UNIT) { parent = TtaGetParent (el); elType = TtaGetElementType (parent); while (parent && elType.ElSSchema != templateSSchema) { parent = TtaGetParent (parent); elType = TtaGetElementType (parent); } if (parent && (elType.ElTypeNum == Template_EL_useEl || elType.ElTypeNum == Template_EL_useSimple)) { // there is a parent template use attrType.AttrSSchema = elType.ElSSchema; attrType.AttrTypeNum = Template_ATTR_prompt; att = TtaGetAttribute (parent, attrType); if (att) { TtaSelectElement (doc, el); return TRUE; } } } #endif /* TEMPLATES */ /* let Thot perform normal operation */ return FALSE; } /*---------------------------------------------------------------------- RemovePromptIndicator removes the enclosing prompt indicator ----------------------------------------------------------------------*/ ThotBool RemovePromptIndicator (NotifyOnTarget *event) { #ifdef TEMPLATES ElementType elType; Element parent, el; AttributeType attrType; Attribute att; Document doc; SSchema templateSSchema; el = event->element; doc = event->document; elType = TtaGetElementType (el); templateSSchema = TtaGetSSchema ("Template", doc); parent = TtaGetParent (el); elType = TtaGetElementType (parent); while (parent && elType.ElSSchema != templateSSchema) { parent = TtaGetParent (parent); elType = TtaGetElementType (parent); } if (parent && (elType.ElTypeNum == Template_EL_useEl || elType.ElTypeNum == Template_EL_useSimple)) { // there is a parent template use attrType.AttrSSchema = elType.ElSSchema; attrType.AttrTypeNum = Template_ATTR_prompt; att = TtaGetAttribute (parent, attrType); if (att) { TtaRegisterAttributeDelete (att, parent, doc); TtaRemoveAttribute (parent, att, doc); } } #endif /* TEMPLATES */ return FALSE; /* let Thot perform normal operation */ } /*---------------------------------------------------------------------- AllocTemplateRepositoryListElement: allocates an element for the list of template repositories. path : path of the new element return : address of the new element ----------------------------------------------------------------------*/ void* AllocTemplateRepositoryListElement (const char* path, void* prevElement) { Prop_Templates_Path *element; element = (Prop_Templates_Path*)TtaGetMemory (sizeof(Prop_Templates_Path)); memset (element, 0, sizeof(Prop_Templates_Path)); strncpy (element->Path, path, MAX_LENGTH - 1); if (prevElement) { element->NextPath = ((Prop_Templates_Path*)prevElement)->NextPath; ((Prop_Templates_Path*)prevElement)->NextPath = element; } return element; } /*---------------------------------------------------------------------- FreeTemplateRepositoryList: Free the list of template repositories. list : address of the list (address of the first element). ----------------------------------------------------------------------*/ void FreeTemplateRepositoryList (void* list) { Prop_Templates_Path **l = (Prop_Templates_Path**) list; Prop_Templates_Path *element = *l; l = NULL; while (element) { Prop_Templates_Path* next = element->NextPath; TtaFreeMemory (element); element = next; } } /*---------------------------------------------------------------------- CopyTemplateRepositoryList: Copy a list of template repositories. src : address of the list (address of the first element). dst : address where copy the list ----------------------------------------------------------------------*/ static void CopyTemplateRepositoryList (const Prop_Templates_Path** src, Prop_Templates_Path** dst) { Prop_Templates_Path *element=NULL, *current=NULL; if (*src) { *dst = (Prop_Templates_Path*) TtaGetMemory (sizeof(Prop_Templates_Path)); (*dst)->NextPath = NULL; strcpy((*dst)->Path, (*src)->Path); element = (*src)->NextPath; current = *dst; } while (element) { current->NextPath = (Prop_Templates_Path*) TtaGetMemory (sizeof(Prop_Templates_Path)); current = current->NextPath; current->NextPath = NULL; strcpy(current->Path, element->Path); element = element->NextPath; } } /*---------------------------------------------------------------------- LoadTemplateRepositoryList: Load the list of template repositories. list : address of the list (address of the first element). return : the number of readed repository paths. ----------------------------------------------------------------------*/ static int LoadTemplateRepositoryList (Prop_Templates_Path** list) { Prop_Templates_Path *element, *current = NULL; char *path, *homePath; unsigned char *c; int nb = 0; FILE *file; //clean up the curent list FreeTemplateRepositoryList (list); // open the file path = (char *) TtaGetMemory (MAX_LENGTH); homePath = TtaGetEnvString ("APP_HOME"); sprintf (path, "%s%ctemplates.dat", homePath, DIR_SEP); file = TtaReadOpen ((char *)path); if (!file) { /* The config file dont exist, create it. */ file = TtaWriteOpen ((char *)path); fprintf (file, "http://www.w3.org/Amaya/Templates/cv.xtd\n"); fprintf (file, "http://www.w3.org/Amaya/Templates/slides.xtd\n"); fprintf (file, "http://www.w3.org/Amaya/Templates/ACM-Proc-Article.xtd\n"); TtaWriteClose (file); /* Retry to open it.*/ file = TtaReadOpen ((char *)path); } if (file) { // read the file c = (unsigned char*)path; *c = EOS; while (TtaReadByte (file, c)) { if (*c == 13 || *c == EOL) *c = EOS; if (*c == EOS && c != (unsigned char*)path ) { element = (Prop_Templates_Path*) TtaGetMemory (sizeof(Prop_Templates_Path)); element->NextPath = NULL; strcpy (element->Path, path); if (*list == NULL) *list = element; else current->NextPath = element; current = element; nb++; c = (unsigned char*) path; *c = EOS; } else c++; } if (c != (unsigned char*)path && *path != EOS) { element = (Prop_Templates_Path*) TtaGetMemory (sizeof(Prop_Templates_Path)); *(c+1) = EOS; strcpy (element->Path, path); element->NextPath = NULL; if (*list == NULL) *list = element; else current->NextPath = element; nb++; } TtaReadClose (file); } TtaFreeMemory(path); return nb; } /*---------------------------------------------------------------------- SaveTemplateRepositoryList: Save the list of template repositories. list : address of the list (address of the first element). ----------------------------------------------------------------------*/ static void SaveTemplateRepositoryList (const Prop_Templates_Path** list) { const Prop_Templates_Path *element; char *path, *homePath; unsigned char *c; FILE *file; path = (char *) TtaGetMemory (MAX_LENGTH); homePath = TtaGetEnvString ("APP_HOME"); sprintf (path, "%s%ctemplates.dat", homePath, DIR_SEP); file = TtaWriteOpen ((char *)path); c = (unsigned char*)path; *c = EOS; if (file) { element = *list; while (element) { fprintf(file, "%s\n", element->Path); element = element->NextPath; } TtaWriteClose (file); } } /*---------------------------------------------------------------------- GetTemplateRepositoryList: Get the list of template repositories from template environment. list : address of the list (address of the first element). ----------------------------------------------------------------------*/ void GetTemplateRepositoryList (void* list) { Prop_Templates_Path** l = (Prop_Templates_Path**) list; CopyTemplateRepositoryList((const Prop_Templates_Path**)&TemplateRepositoryPaths, l); } /*---------------------------------------------------------------------- SetTemplateRepositoryList: Set the list of template repositories environment. list : address of the list (address of the first element). ----------------------------------------------------------------------*/ void SetTemplateRepositoryList (const void* list) { const Prop_Templates_Path** l = (const Prop_Templates_Path**) list; CopyTemplateRepositoryList((const Prop_Templates_Path**)l, &TemplateRepositoryPaths); SaveTemplateRepositoryList((const Prop_Templates_Path**)&TemplateRepositoryPaths); } /*----------------------------------------------------------------------- InitTemplates Initializes the annotation library -----------------------------------------------------------------------*/ void InitTemplates () { TtaSetEnvBoolean ("SHOW_TEMPLATES", TRUE, FALSE); LoadTemplateRepositoryList (&TemplateRepositoryPaths); } /*---------------------------------------------------------------------- Load a template and create the instance file - update images and stylesheets related to the template. ----------------------------------------------------------------------*/ void CreateInstanceOfTemplate (Document doc, char *templatename, char *docname) { #ifdef TEMPLATES DocumentType docType; int len, i; char *s; char suffix[6]; ThotBool dontReplace = DontReplaceOldDoc; if (!IsW3Path (docname) && TtaFileExist (docname)) { s = (char *)TtaGetMemory (strlen (docname) + strlen (TtaGetMessage (AMAYA, AM_OVERWRITE_CHECK)) + 2); sprintf (s, TtaGetMessage (AMAYA, AM_OVERWRITE_CHECK), docname); InitConfirm (0, 0, s); TtaFreeMemory (s); if (!UserAnswer) return; } docType = LoadTemplate (0, templatename); if (docType != docFree) { /* check if the file suffix is conform to the document type */ s = (char *)TtaGetMemory (strlen (docname) + 10); strcpy (s, docname); if (!IsXMLName (docname)) { // by default no suffix is added suffix[0] = EOS; if (IsMathMLName (docname) && docType != docMath) strcpy (suffix, "mml"); else if (IsSVGName (docname) && docType != docSVG) strcpy (suffix, "svg"); else if (IsHTMLName (docname) && docType != docHTML) strcpy (suffix, "xml"); if (suffix[0] != EOS) { // change or update the suffix len = strlen (s); for (i = len-1; i > 0 && s[i] != '.'; i--); if (s[i] != '.') { /* there is no suffix */ s[i++] = '.'; strcpy (&s[i], suffix); } else { /* there is a suffix */ i++; strcpy (&s[i], suffix); } } } // now create the instance DontReplaceOldDoc = dontReplace; CreateInstance (templatename, s, docname, docType, doc); TtaFreeMemory (s); } #endif /* TEMPLATES */ } /*---------------------------------------------------------------------- PreventReloadingTemplate Prevent reloading a template. You must call AllowReloadingTemplate when finish. Usefull for reload an instance without reloading the template. ----------------------------------------------------------------------*/ void PreventReloadingTemplate(char* template_url) { #ifdef TEMPLATES Template_AddReference(GetXTigerTemplate (template_url)); #endif /* TEMPLATES */ } /*---------------------------------------------------------------------- AllowReloadingTemplate Allow reloading a template. You must call it after each PreventReloadingTemplate call. ----------------------------------------------------------------------*/ void AllowReloadingTemplate(char* template_url) { #ifdef TEMPLATES Template_RemoveReference(GetXTigerTemplate (template_url)); #endif /* TEMPLATES */ } /*---------------------------------------------------------------------- ----------------------------------------------------------------------*/ ThotBool isEOSorWhiteSpace (const char c) { return c == SPACE || c == TAB || c == EOL || c ==__CR__ || c == EOS; } ThotBool isWhiteSpace (const char c) { return c == SPACE || c == TAB || c == EOL || c ==__CR__; } /*---------------------------------------------------------------------- giveItems : Lists type items from string example : "one two three" is extracted to {one, two, three} note : item type are setted to SimpleTypeNat text : text from which list items size : size of text in characters items : address of exctracted item list nbitems : items number in items list ----------------------------------------------------------------------*/ void giveItems (char *text, int size, struct menuType **items, int *nbitems) { #ifdef TEMPLATES ThotBool inElement = TRUE; struct menuType *menu; char *iter; char temp[128]; int i; int labelSize; *nbitems = 1; for (i = 0; i < size; i++) { if (isEOSorWhiteSpace (text[i])) { if (inElement) inElement = FALSE; } else if (!inElement) { inElement = TRUE; (*nbitems)++; } } menu = (struct menuType*) TtaGetMemory (sizeof (struct menuType)* *nbitems); iter = text; for (i = 0; i < *nbitems; i++) { labelSize = 0; while (isWhiteSpace (*iter)) iter++; if (*iter != EOS) { while (!isEOSorWhiteSpace (*iter)) { temp[labelSize++] = *iter; iter++; } temp[labelSize] = EOS; menu[i].label = (char *) TtaStrdup (temp); menu[i].type = SimpleTypeNat; /* @@@@@ ???? @@@@@ */ } } *items = menu; #endif /* TEMPLATES */ } #ifdef TEMPLATES /*---------------------------------------------------------------------- ----------------------------------------------------------------------*/ static char *createMenuString (const struct menuType* items, const int nbItems) { char *result, *iter; int size = 0; int i; for (i=0; i < nbItems; i++) size += 2 + strlen (items[i].label); result = (char *) TtaGetMemory (size); iter = result; for (i=0; i < nbItems; i++) { *iter = 'B'; ++iter; strcpy (iter, items[i].label); iter += strlen (items[i].label)+1; } return result; } #endif /* TEMPLATES */ /*---------------------------------------------------------------------- UseToBeCreated An new use element will be created by the user through some generic editing command -----------------------------------------------------------------------*/ ThotBool UseToBeCreated (NotifyElement *event) { #ifdef TEMPLATES ElementType parentType; SSchema templateSSchema = TtaGetSSchema ("Template", event->document); if (templateSSchema) { parentType = TtaGetElementType (event->element); if (parentType.ElSSchema == templateSSchema && parentType.ElTypeNum == Template_EL_repeat) { if (Template_CanInsertRepeatChild (event->element)) return TemplateElementWillBeCreated (event); else return TRUE; //don't let Thot do the job } else return TemplateElementWillBeCreated (event); } #endif /* TEMPLATES */ return FALSE; /* let Thot perform normal operation */ } /*---------------------------------------------------------------------- UseCreated A new "use" element has just been created by the user with a generic editing command. -----------------------------------------------------------------------*/ void UseCreated (NotifyElement *event) { #ifdef TEMPLATES Document doc = event->document; Element el = event->element; Element parent; Element first; ElementType parentType; XTigerTemplate t; SSchema templateSSchema; char* types, *text = NULL; if (!TtaGetDocumentAccessMode(doc)) return; if (TtaGetFirstChild (el)) /* this Use element has already some content. It has already been instanciated */ return; t = GetXTigerDocTemplate (doc); if (!t) return; // no template ?!?! templateSSchema = TtaGetSSchema ("Template", doc); parent = TtaGetParent(el); parentType = TtaGetElementType(parent); if (parentType.ElSSchema == templateSSchema && parentType.ElTypeNum == Template_EL_repeat) { first = TtaGetFirstChild (parent); if (first == el) TtaNextSibling (&first); if (first) { types = GetAttributeStringValueFromNum (first, Template_ATTR_types, NULL); if (types) { SetAttributeStringValueWithUndo (el, Template_ATTR_types, types); text = GetAttributeStringValueFromNum (el, Template_ATTR_title, NULL); SetAttributeStringValueWithUndo (first, Template_ATTR_title, text); TtaFreeMemory (text); text = GetAttributeStringValueFromNum (el, Template_ATTR_currentType, NULL); SetAttributeStringValueWithUndo (first, Template_ATTR_currentType, text); TtaFreeMemory (text); TtaFreeMemory (types); } } } InstantiateUse (t, el, doc, TRUE); #endif /* TEMPLATES */ } /*---------------------------------------------------------------------- Template_CanInsertRepeatChild Test if a xt:repeat child can be inserted (number between params min and max). @param el element (xt:repeat) to test @return True if an element can be inserted. ----------------------------------------------------------------------*/ ThotBool Template_CanInsertRepeatChild(Element el) { #ifdef TEMPLATES char *max; int maxVal, curVal; Element child; max = GetAttributeStringValueFromNum(el, Template_ATTR_maxOccurs, NULL); if (max && max[0]!=EOS) { if (!strcmp(max, "*")) { TtaFreeMemory(max); return TRUE; } maxVal = atoi (max); TtaFreeMemory (max); curVal = 0; for (child = TtaGetFirstChild(el); child; TtaNextSibling(&child)) curVal++; return (curVal < maxVal); } else return TRUE; #endif /* TEMPLATES */ return FALSE; } #ifdef TEMPLATES /*---------------------------------------------------------------------- QueryStringFromMenu Show a context menu to query a choice. @param items space-separated choice list string. @return The choosed item string or NULL if none. ----------------------------------------------------------------------*/ static char* QueryStringFromMenu (Document doc, char* items) { int nbitems, size; struct menuType *itemlist; char *menuString; char *result = NULL; if (!TtaGetDocumentAccessMode (doc)) return NULL; if (items == NULL) return NULL; size = strlen (items); if (size == 0) return NULL; giveItems (items, size, &itemlist, &nbitems); menuString = createMenuString (itemlist, nbitems); TtaNewScrollPopup (BaseDialog + OptionMenu, TtaGetViewFrame (doc, 1), NULL, nbitems, menuString , NULL, false, 'L'); TtaFreeMemory (menuString); ReturnOption = -1; TtaShowDialogue (BaseDialog + OptionMenu, FALSE); TtaWaitShowProcDialogue (); TtaDestroyDialogue (BaseDialog + OptionMenu); if (ReturnOption!=-1) { result = TtaStrdup(itemlist[ReturnOption].label); } TtaFreeMemory (itemlist); return result; } #endif /* TEMPLATES */ #ifdef AMAYA_DEBUG void FillInsertableElemList (Document doc, Element elem, DLList list); #endif /* AMAYA_DEBUG */ /*---------------------------------------------------------------------- ----------------------------------------------------------------------*/ char *Template_GetListTypes (XTigerTemplate t, Element el) { #ifdef TEMPLATES char *listtypes = NULL, *types; types = GetAttributeStringValueFromNum (el, Template_ATTR_types, NULL); if (types) listtypes = Template_ExpandTypes (t, types, el, FALSE); return listtypes; #endif /* TEMPLATES */ } /*---------------------------------------------------------------------- BagButtonClicked Called when a bag button is clicked. Can be called for useEl, useSimple or bag. If called for useEl or useSimple, the new element must be added after. If called for bag, the element must be added before all. Shows a menu with all the types that can be used in the bag. ----------------------------------------------------------------------*/ ThotBool BagButtonClicked (NotifyElement *event) { #ifdef TEMPLATES Document doc = event->document; Element el = event->element; ElementType elType; XTigerTemplate t; Declaration decl; Element bagEl = el; Element firstEl; Element newEl = NULL; View view; SSchema templateSSchema; char *listtypes = NULL; char *result = NULL; ThotBool oldStructureChecking; DisplayMode dispMode; if (!TtaGetDocumentAccessMode (doc)) return TRUE; if (!IsTemplateInstanceDocument (doc)) return FALSE; /* let Thot perform normal operation */ TtaGetActiveView (&doc, &view); if (view != 1) return FALSE; /* let Thot perform normal operation */ TtaCancelSelection (doc); templateSSchema = TtaGetSSchema ("Template", doc); t = GetXTigerDocTemplate(doc); elType = TtaGetElementType (el); while (bagEl && (elType.ElSSchema != templateSSchema || elType.ElTypeNum != Template_EL_bag)) { bagEl = TtaGetParent (bagEl); elType = TtaGetElementType (bagEl); } if (bagEl) { listtypes = Template_GetListTypes (t, bagEl); if (listtypes) { #ifdef AMAYA_DEBUG printf("BagButtonClicked : \n > %s\n", listtypes); // { // DLList list = DLList_Create(); // FillInsertableElemList (doc, TtaGetFirstChild(bagEl), list); // DLListNode node; // ForwardIterator iter = DLList_GetForwardIterator(list); // ITERATOR_FOREACH(iter, DLListNode, node) // { // ElemListElement elem = (ElemListElement)node->elem; // printf(" + %s\n", ElemListElement_GetName(elem)); // } // DLList_Destroy(list); // } #endif /* AMAYA_DEBUG */ result = QueryStringFromMenu (doc, listtypes); TtaFreeMemory (listtypes); if (result) { decl = Template_GetDeclaration (t, result); if (decl) { dispMode = TtaGetDisplayMode (doc); if (dispMode == DisplayImmediately) /* don't set NoComputedDisplay -> it breaks down views formatting when Enter generates new elements */ TtaSetDisplayMode (doc, DeferredDisplay); /* Prepare insertion.*/ oldStructureChecking = TtaGetStructureChecking (doc); TtaSetStructureChecking (FALSE, doc); TtaOpenUndoSequence (doc, NULL, NULL, 0, 0); /* Insert */ if (el == bagEl) { el = TtaGetFirstChild (el); TtaSelectElement (doc, el); TtaInsertAnyElement (doc, TRUE); } else { TtaSelectElement (doc, el); TtaInsertAnyElement (doc, FALSE); } newEl = Template_InsertBagChild (doc, bagEl, decl, FALSE); /* Finish insertion.*/ TtaCloseUndoSequence (doc); TtaSetDocumentModified (doc); TtaSetStructureChecking (oldStructureChecking, doc); // restore the display TtaSetDisplayMode (doc, dispMode); firstEl = GetFirstEditableElement (newEl); if (firstEl) { TtaSelectElement (doc, firstEl); TtaSetStatusSelectedElement (doc, view, firstEl); } else { TtaSelectElement (doc, newEl); TtaSetStatusSelectedElement (doc, view, newEl); } } } } TtaFreeMemory (result); } #endif /* TEMPLATES */ return TRUE; /* don't let Thot perform normal operation */ } /*---------------------------------------------------------------------- DoReplicateUseElement insert a new element after the el child of repeatEl or as the first child of repeatEl. ----------------------------------------------------------------------*/ void DoReplicateUseElement (XTigerTemplate t, Document doc, int view, Element el, Element repeatEl, char *name) { Declaration decl; Element newEl, firstEl, prevRepeat; ThotBool oldStructureChecking; DisplayMode dispMode; if (repeatEl == Creating_repeat) return; prevRepeat = Creating_repeat; Creating_repeat = repeatEl; decl = Template_GetDeclaration (t, name); if (decl) { dispMode = TtaGetDisplayMode (doc); if (dispMode == DisplayImmediately) /* don't set NoComputedDisplay -> it breaks down views formatting when Enter generates new elements */ TtaSetDisplayMode (doc, DeferredDisplay); /* Prepare insertion.*/ oldStructureChecking = TtaGetStructureChecking (doc); TtaSetStructureChecking (FALSE, doc); TtaOpenUndoSequence (doc, NULL, NULL, 0, 0); /* Insert. */ if (el == repeatEl) newEl = Template_InsertRepeatChildAfter (doc, repeatEl, decl, NULL); else newEl = Template_InsertRepeatChildAfter (doc, repeatEl, decl, el); /* Finish insertion.*/ TtaCloseUndoSequence(doc); TtaSetDocumentModified (doc); TtaSetStructureChecking (oldStructureChecking, doc); // restore the display TtaSetDisplayMode (doc, dispMode); firstEl = GetFirstEditableElement (newEl); if (firstEl) { TtaSelectElement (doc, firstEl); TtaSetStatusSelectedElement (doc, view, firstEl); } else { TtaSelectElement (doc, newEl); TtaSetStatusSelectedElement (doc, view, newEl); } } Creating_repeat = prevRepeat; } /*---------------------------------------------------------------------- RepeatButtonClicked Called when a repeat button is clicked. Can be called for useEl, useSimple or repeat. If called for useEl or useSimple, the new element must be added after. If called for repeat, the element must be added before all. Shows a menu with all the types that can be used in a use element. ----------------------------------------------------------------------*/ ThotBool RepeatButtonClicked (NotifyElement *event) { #ifdef TEMPLATES Document doc = event->document; Element el = event->element; ElementType elType; XTigerTemplate t; Element repeatEl = el; Element firstEl; View view; char *listtypes = NULL; char *result = NULL; if (!TtaGetDocumentAccessMode(doc)) return TRUE; if (!IsTemplateInstanceDocument (doc)) return FALSE; /* let Thot perform normal operation */ TtaGetActiveView (&doc, &view); if (view != 1) return FALSE; /* let Thot perform normal operation */ TtaCancelSelection(doc); t = GetXTigerDocTemplate(doc); elType = TtaGetElementType(el); while (elType.ElTypeNum != Template_EL_repeat) { repeatEl = TtaGetParent(repeatEl); if (repeatEl == NULL) break; elType = TtaGetElementType(repeatEl); } if (repeatEl) { if (Template_CanInsertRepeatChild (repeatEl)) { firstEl = TtaGetFirstChild (repeatEl); listtypes = Template_GetListTypes (t, firstEl); if (listtypes) { #ifdef AMAYA_DEBUG printf("RepeatButtonClicked : \n > %s\n", listtypes); #endif /* AMAYA_DEBUG */ result = QueryStringFromMenu (doc, listtypes); TtaFreeMemory (listtypes); if (result) DoReplicateUseElement (t, doc, view, el, repeatEl, result); } TtaFreeMemory(result); DumpSubtree(repeatEl, doc, 0); } else /* if (Template_CanInsertRepeatChild(repeatEl)) */ { TtaSetStatus(doc, view, TtaGetMessage (AMAYA, AM_NUMBER_OCCUR_HAVE_MAX), NULL); } } return TRUE; /* don't let Thot perform normal operation */ #endif /* TEMPLATES */ return TRUE; } /*---------------------------------------------------------------------- UseButtonClicked Shows a menu with all the types that can be used in a use element. ----------------------------------------------------------------------*/ ThotBool UseButtonClicked (NotifyElement *event) { #ifdef TEMPLATES Document doc = event->document; Element el = event->element; Element child; ElementType elType; XTigerTemplate t; Declaration decl; Element firstEl; Element newEl = NULL; char* types; ThotBool oldStructureChecking; View view; char* listtypes = NULL; char* result = NULL; if (!TtaGetDocumentAccessMode (doc)) return TRUE; if (!IsTemplateInstanceDocument (doc)) return FALSE; /* let Thot perform normal operation */ TtaGetActiveView (&doc, &view); if (view != 1) return FALSE; /* let Thot perform normal operation */ TtaCancelSelection(doc); t = GetXTigerDocTemplate(doc); if (!t) return FALSE; /* let Thot perform normal operation */ elType = TtaGetElementType(el); firstEl = TtaGetFirstChild(el); if (firstEl) { RepeatButtonClicked(event); } else { types = GetAttributeStringValueFromNum(el, Template_ATTR_types, NULL); if (types) { listtypes = Template_ExpandTypes(t, types, NULL, FALSE); #ifdef AMAYA_DEBUG printf("UseButtonClicked : \n > %s\n", listtypes); #endif /* AMAYA_DEBUG */ result = QueryStringFromMenu(doc, listtypes); if (result) { decl = Template_GetDeclaration(t, result); if (decl) { /* Prepare insertion.*/ oldStructureChecking = TtaGetStructureChecking (doc); TtaSetStructureChecking (FALSE, doc); TtaOpenUndoSequence (doc, NULL, NULL, 0, 0); /* Insert */ newEl = Template_InsertUseChildren(doc, el, decl); for (child = TtaGetFirstChild(newEl); child; TtaNextSibling(&child)) { TtaRegisterElementCreate(child, doc); } TtaChangeTypeOfElement(el, doc, Template_EL_useSimple); TtaRegisterElementTypeChange(el, Template_EL_useEl, doc); /* xt:currentType attribute.*/ SetAttributeStringValueWithUndo(el, Template_ATTR_currentType, result); /* Finish insertion. */ TtaCloseUndoSequence(doc); TtaSetDocumentModified (doc); TtaSetStructureChecking (oldStructureChecking, doc); firstEl = GetFirstEditableElement(newEl); if (firstEl) { TtaSelectElement (doc, firstEl); TtaSetStatusSelectedElement(doc, view, firstEl); } else { TtaSelectElement (doc, newEl); TtaSetStatusSelectedElement(doc, view, newEl); } } } } TtaFreeMemory(types); TtaFreeMemory(listtypes); TtaFreeMemory(result); } return TRUE; #endif /* TEMPLATES */ return TRUE; } /*---------------------------------------------------------------------- UseSimpleButtonClicked ----------------------------------------------------------------------*/ ThotBool UseSimpleButtonClicked (NotifyElement *event) { #ifdef TEMPLATES ElementType elType, parentType; AttributeType attrType; Attribute att; if (!TtaGetDocumentAccessMode (event->document)) return TRUE; if (!IsTemplateInstanceDocument (event->document)) return FALSE; /* let Thot perform normal operation */ elType = TtaGetElementType (event->element); attrType.AttrSSchema = elType.ElSSchema; attrType.AttrTypeNum = Template_ATTR_option; att = TtaGetAttribute (event->element, attrType); if (att) return OptionButtonClicked (event); parentType = TtaGetElementType (TtaGetParent( event->element)); if (parentType.ElTypeNum == Template_EL_repeat) return RepeatButtonClicked (event); else if (parentType.ElTypeNum == Template_EL_bag) return BagButtonClicked (event); #endif /* TEMPLATES */ return FALSE; } /*---------------------------------------------------------------------- OptionButtonClicked ----------------------------------------------------------------------*/ ThotBool OptionButtonClicked (NotifyElement *event) { #ifdef TEMPLATES Element useEl, contentEl, next; ElementType useType; Document doc; XTigerTemplate t; View view; if (!TtaGetDocumentAccessMode (event->document)) return TRUE; if (!IsTemplateInstanceDocument (event->document)) return FALSE; /* let Thot perform normal operation */ TtaGetActiveView (&doc, &view); if (view != 1) return FALSE; /* let Thot perform normal operation */ doc = event->document; useEl = event->element; if (!useEl) return FALSE; /* let Thot perform normal operation */ useType = TtaGetElementType (useEl); if (useType.ElTypeNum != Template_EL_useEl && useType.ElTypeNum != Template_EL_useSimple) return FALSE; TtaOpenUndoSequence(doc, NULL, NULL, 0, 0); TtaCancelSelection (doc); contentEl = TtaGetFirstChild (useEl); if (!contentEl) /* the "use" element is empty. Instantiate it */ { t = GetXTigerDocTemplate (doc); if (!t) return FALSE; // no template ?!?! InstantiateUse (t, useEl, doc, TRUE); } else /* remove the content of the "use" element */ { do { next = contentEl; TtaNextSibling (&next); TtaRegisterElementDelete(contentEl, doc); TtaDeleteTree (contentEl, doc); contentEl = next; } while (next); if (NeedAMenu (useEl, doc)) { TtaChangeTypeOfElement (useEl, doc, Template_EL_useEl); TtaRegisterElementTypeChange(useEl, Template_EL_useSimple, doc); } } TtaSelectElement (doc, event->element); TtaCloseUndoSequence(doc); return TRUE; /* don't let Thot perform normal operation */ #endif /* TEMPLATES */ return TRUE; } /*---------------------------------------------------------------------- Template_FillFromDocument Fill XTigerTemplate structure with the content of the document. Load dependencies if needed. ----------------------------------------------------------------------*/ void Template_FillFromDocument (Document doc) { #ifdef TEMPLATES XTigerTemplate t = GetXTigerTemplate (DocumentURLs[doc]); Element root; if (t) { #ifdef AMAYA_DEBUG printf("Template_FillFromDocument state: %d\n", t->state); #endif SetTemplateDocument (t, doc); Template_PrepareTemplate(t, doc); if (IsTemplateInstanceDocument(doc)) { #ifdef AMAYA_DEBUG printf(" > instance\n"); #endif // fix all access rights in the instance root = TtaGetRootElement (doc); TtaSetAccessRight (root, ReadOnly, doc); Template_FixAccessRight (t, root, doc); TtaUpdateAccessRightInViews (doc, root); } #ifdef AMAYA_DEBUG else if (t->state&templLibraryFlag) printf(" > library\n"); else if (t->state&templTemplate) printf(" > template\n"); #endif // Mark loaded t->state |= templloaded; TtaSetDocumentUnmodified (doc); UpdateTemplateMenus (doc); #ifdef AMAYA_DEBUG DumpAllDeclarations(); #endif /* AMAYA_DEBUG */ } #endif /* TEMPLATES */ } /*---------------------------------------------------------------------- Template_CheckAndPrepareTemplate checks if the document is a template or a library and prepare XTigerTemplate structure to use it. ----------------------------------------------------------------------*/ ThotBool Template_CheckAndPrepareTemplate(char* docURL) { #ifdef TEMPLATES XTigerTemplate t = NULL; //GetXTigerTemplate(docURL); if (IsXTiger(docURL)) { #ifdef AMAYA_DEBUG printf("Template_CheckAndPrepareTemplate %s templTemplate\n", docURL); #endif t = LookForXTigerTemplate(docURL); t->state |= templTemplate; } else if (IsXTigerLibrary(docURL)) { #ifdef AMAYA_DEBUG printf("Template_CheckAndPrepareTemplate %s templLibrary\n", docURL); #endif t = LookForXTigerLibrary(docURL); t->state |= templLibrary; } return t != NULL; #endif /* TEMPLATES */ return FALSE; } /*---------------------------------------------------------------------- Template_CheckAndPrepareInstance checks if it is a template instance needs. If it's an instance and the template is not loaded, load it into a temporary file ----------------------------------------------------------------------*/ void Template_CheckAndPrepareInstance (char *localFileName, Document doc, char* docURL) { #ifdef TEMPLATES XTigerTemplate t; char *content, *ptr, *begin; gzFile stream; char buffer[2000]; int res; char *template_version = NULL, *template_url = NULL; stream = TtaGZOpen (localFileName); if (stream != 0) { res = gzread (stream, buffer, 1999); if (res >= 0) { buffer[res] = EOS; begin = strstr (buffer, "document); if (t) Template_RemoveReference(t); #endif /* TEMPLATES */ return FALSE; } /*---------------------------------------------------------------------- IsTemplateElement Test if an element is a template element. ----------------------------------------------------------------------*/ ThotBool IsTemplateElement (Element elem) { #ifdef TEMPLATES ElementType elType; elType = TtaGetElementType(elem); if (elType.ElSSchema) return (strcmp(TtaGetSSchemaName(elType.ElSSchema) , "Template") == 0); #endif /* TEMPLATES */ return FALSE; } /*---------------------------------------------------------------------- GetFirstTemplateParentElement Return the first element which has "Template" as schema name or null. ----------------------------------------------------------------------*/ Element GetFirstTemplateParentElement(Element elem) { #ifdef TEMPLATES ElementType elType; elem = TtaGetParent (elem); elType = TtaGetElementType(elem); while (elem && strcmp(TtaGetSSchemaName(elType.ElSSchema), "Template")) { elem = TtaGetParent (elem); elType = TtaGetElementType(elem); } return elem; #else return NULL; #endif /* TEMPLATES */ } /*---------------------------------------------------------------------- IsBeginningSelected Returns TRUE if the selection is athe beginning of an element ----------------------------------------------------------------------*/ ThotBool IsBeginningSelected (Element el, Document doc) { Element selElem, prev; int firstChar, lastChar; TtaGiveFirstSelectedElement(doc, &selElem, &firstChar, &lastChar); if (firstChar <= 1 && lastChar < firstChar) { while (selElem && selElem != el) { prev = selElem; TtaPreviousSibling (&prev); if (prev) return FALSE; selElem = TtaGetParent (selElem); } return TRUE; } return FALSE; } /*---------------------------------------------------------------------- TemplateElementWillBeCreated Processed when an element will be created in a template context. ----------------------------------------------------------------------*/ ThotBool TemplateElementWillBeCreated (NotifyElement *event) { #ifdef TEMPLATES ElementType elType = event->elementType; Element parent = event->element; ElementType parentType = TtaGetElementType(parent); Element ancestor, el, next; ElementType ancestorType; SSchema templateSSchema; XTigerTemplate t; char *types, *ptr, *name = NULL; int len, view, i, doc = event->document; ThotBool b; if (event->info == 1) return FALSE; #ifdef AMAYA_DEBUG printf("TemplateElementWillBeCreated\n"); #endif if (!TtaGetDocumentAccessMode(doc)) return TRUE; templateSSchema = TtaGetSSchema ("Template", doc); if (templateSSchema == NULL) return FALSE; // let Thot do the job // Fisrt, test if in a xt:bag or in a base-element xt:use if (parentType.ElSSchema == templateSSchema) ancestor = parent; else ancestor = GetFirstTemplateParentElement (parent); if (ancestor) { ancestorType = TtaGetElementType(ancestor); if (ancestorType.ElTypeNum == Template_EL_bag) { // only check the use child if (ancestor != parent) return FALSE; // let Thot do the job if (elType.ElSSchema == templateSSchema && (elType.ElTypeNum == Template_EL_useSimple || elType.ElTypeNum == Template_EL_useEl)) return FALSE; return !Template_CanInsertElementInBagElement (doc, elType, ancestor); } else if (ancestorType.ElTypeNum == Template_EL_repeat) { if (elType.ElSSchema != templateSSchema || (elType.ElTypeNum != Template_EL_useSimple && elType.ElTypeNum != Template_EL_useEl)) return TRUE; // don't let Thot do the job t = GetXTigerDocTemplate (doc); el = NULL; i = 0; next = TtaGetFirstChild (ancestor); while (next && i < event->position) { el = next; TtaNextSibling (&next); i++; } if (el) next = el; name = GetUsedTypeName (next); DoReplicateUseElement (t, doc, view, el, ancestor, name); TtaFreeMemory(name); return TRUE; // don't let Thot do the job } else if (ancestorType.ElTypeNum == Template_EL_useSimple || ancestorType.ElTypeNum == Template_EL_useEl) { // only check the bag child @@@ will be check exclude/include later if (elType.ElSSchema == templateSSchema && (elType.ElTypeNum == Template_EL_useSimple || elType.ElTypeNum == Template_EL_useEl)) return TRUE; // don't let Thot do the job if (ancestor != parent) return TtaIsReadOnly (parent); // let or not Thot do the job types = GetAttributeStringValueFromNum (ancestor, Template_ATTR_currentType, NULL); b = Template_CanInsertElementInUse (doc, elType, types, parent, event->position); if (types && !b) { // check with equivalent component name = (char *)GetXMLElementName (elType, doc); if (name) { len = strlen (name); ptr = strstr (types, name); if (ptr && len && (ptr == types || ptr[-1] == SPACE) && (ptr[len] == EOS || ptr[len] == SPACE)) { parent = TtaGetParent(ancestor); parentType = TtaGetElementType(parent); if (parentType.ElSSchema == templateSSchema && parentType.ElTypeNum == Template_EL_repeat) { // duplicate the current use TtaGetActiveView (&doc, &view); t = GetXTigerDocTemplate(doc); if (IsBeginningSelected (ancestor, doc)) TtaPreviousSibling (&ancestor); DoReplicateUseElement (t, doc, view, ancestor, parent, name); } } } } TtaFreeMemory(types); return !b; } else if (ancestorType.ElTypeNum == Template_EL_component) // allow all changes return FALSE; } if (!IsTemplateInstanceDocument(doc) && elType.ElSSchema == templateSSchema && (elType.ElTypeNum == Template_EL_TEXT_UNIT || elType.ElTypeNum == Template_EL_component)) return FALSE; // Cannot insert. return TRUE; #endif /* TEMPLATES*/ return FALSE; } /*---------------------------------------------------------------------- TemplateElementWillBeDeleted Processed when an element will be deleted in a template context. ----------------------------------------------------------------------*/ ThotBool TemplateElementWillBeDeleted (NotifyElement *event) { #ifdef TEMPLATES Document doc = event->document; Element elem = event->element; Element xtElem, parent = NULL, sibling; ElementType xtType, elType; char* type; Declaration dec; SSchema templateSSchema; XTigerTemplate t; ThotBool selparent = FALSE; if (event->info==1) return FALSE; if (!TtaGetDocumentAccessMode(event->document)) return TRUE; templateSSchema = TtaGetSSchema ("Template", event->document); if (templateSSchema == NULL) return FALSE; // let Thot do the job t = GetXTigerDocTemplate(doc); if (Template_IsTemplate(t)||Template_IsLibrary(t)) return FALSE; // If template or library, pass to specialized functions. xtElem = GetFirstTemplateParentElement(elem); if (xtElem) { xtType = TtaGetElementType(xtElem); if (xtType.ElTypeNum==Template_EL_bag) { elType = TtaGetElementType(elem); if (elType.ElSSchema==templateSSchema && (elType.ElTypeNum==Template_EL_useSimple || elType.ElTypeNum==Template_EL_useEl)) { // Remove element manually. TtaOpenUndoSequence(doc, elem, elem, 0, 0); TtaRegisterElementDelete(elem, doc); TtaDeleteTree(elem, doc); TtaCloseUndoSequence(doc); return TRUE; } else return FALSE; // xt:bag always allow remove children. } else if (xtType.ElTypeNum == Template_EL_useSimple || xtType.ElTypeNum == Template_EL_useEl) { parent = TtaGetParent(elem); if (xtElem != parent) { type = GetAttributeStringValueFromNum (xtElem, Template_ATTR_currentType, NULL); dec = Template_GetDeclaration(t, type); TtaFreeMemory (type); if (dec && dec->nature == XmlElementNat) return FALSE; // Can remove element only if in xt:use current type is base language element. else return TRUE; } } else if (xtType.ElTypeNum == Template_EL_repeat) { sibling = TtaGetSuccessor (elem); if (sibling == NULL) { // there is no next element sibling = TtaGetPredecessor (elem); if (sibling == NULL) selparent = TRUE; } TtaRegisterElementDelete(elem, doc); TtaDeleteTree(elem, doc); InstantiateRepeat(t, xtElem, doc, TRUE); if (selparent) // look for the new sibling sibling = TtaGetFirstChild (parent); if (sibling) TtaSelectElement(doc, sibling); else TtaSelectElement(doc, parent); return TRUE; } } //TODO Test if current element is use or repeat. // Because if an element is delete and it is the unique child of its parent, // the parent intends to destroy itself. return TRUE; #else /* TEMPLATES */ return FALSE; #endif /* TEMPLATES */ } /*---------------------------------------------------------------------- CurrentTypeWillBeExported Check if the xt:currentType attribute can be exported ----------------------------------------------------------------------*/ ThotBool CurrentTypeWillBeExported (NotifyAttribute *event) { #ifdef TEMPLATES if (!TtaGetDocumentAccessMode(event->document)) return TRUE; if (IsTemplateDocument(event->document)) return TRUE; #endif /* TEMPLATES */ return FALSE; } /*---------------------------------------------------------------------- TemplateAttrInMenu Called by Thot when building the Attributes menu for template elements. ----------------------------------------------------------------------*/ ThotBool TemplateAttrInMenu (NotifyAttribute * event) { #ifdef TEMPLATES // Prevent from showing attributes for template instance but not templates. if (IsTemplateInstanceDocument(event->document)) return TRUE; else #endif /* TEMPLATES */ return FALSE; } /*---------------------------------------------------------------------- CreateTemplateFromDocument Create a template from the current document. ----------------------------------------------------------------------*/ void CreateTemplateFromDocument(Document doc, View view) { #ifdef TEMPLATES char buffer[MAX_LENGTH], suffix[10]; if (IsW3Path (DocumentURLs[doc]) && DocumentMeta[doc] && DocumentMeta[doc]->full_content_location) { // use the location instead of the current URI strcpy (buffer, DocumentMeta[doc]->full_content_location); } else strcpy (buffer, DocumentURLs[doc]); // remove the current suffix TtaExtractSuffix (buffer, suffix); // the new suffix strcat(buffer, ".xtd"); DontReplaceOldDoc = TRUE; CreateTemplate(doc, buffer); // by default .xtd files are xml files TtaSetDocumentCharset (doc, UTF_8, FALSE); #endif /* TEMPLATES */ } /*---------------------------------------------------------------------- UpdateTemplateMenus ----------------------------------------------------------------------*/ void UpdateTemplateMenus (Document doc) { if (IsTemplateInstanceDocument(doc)) { // Instance document TtaSetItemOff (doc, 1, Tools, BCreateTemplateFromDocument); TtaSetItemOff (doc, 1, Tools, BTemplateCreateTextBox); TtaSetItemOff (doc, 1, Tools, BTemplateCreateUseBox); TtaSetItemOff (doc, 1, Tools, BTemplateCreateUseCompBox); TtaSetItemOff (doc, 1, Tools, BTemplateCreateRepeat); TtaSetItemOff (doc, 1, Tools, BTemplateCreateRepeatComp); TtaSetItemOff (doc, 1, Tools, BTemplateCreateFreeBox); TtaSetItemOff (doc, 1, Tools, BTemplateCreateUnion); } else if (DocumentURLs[doc] && IsXTiger (DocumentURLs[doc])) { // Template document TtaSetItemOff (doc, 1, Tools, BCreateTemplateFromDocument); TtaSetItemOn (doc, 1, Tools, BTemplateCreateTextBox); TtaSetItemOn (doc, 1, Tools, BTemplateCreateUseBox); TtaSetItemOn (doc, 1, Tools, BTemplateCreateUseCompBox); TtaSetItemOn (doc, 1, Tools, BTemplateCreateRepeat); TtaSetItemOn (doc, 1, Tools, BTemplateCreateRepeatComp); TtaSetItemOn (doc, 1, Tools, BTemplateCreateFreeBox); TtaSetItemOn (doc, 1, Tools, BTemplateCreateUnion); } else { //Standard document TtaSetItemOn (doc, 1, Tools, BCreateTemplateFromDocument); TtaSetItemOff (doc, 1, Tools, BTemplateCreateTextBox); TtaSetItemOff (doc, 1, Tools, BTemplateCreateUseBox); TtaSetItemOff (doc, 1, Tools, BTemplateCreateUseCompBox); TtaSetItemOff (doc, 1, Tools, BTemplateCreateRepeat); TtaSetItemOff (doc, 1, Tools, BTemplateCreateRepeatComp); TtaSetItemOff (doc, 1, Tools, BTemplateCreateFreeBox); TtaSetItemOff (doc, 1, Tools, BTemplateCreateUnion); } } /*---------------------------------------------------------------------- UninstanciateTemplateDocument An instance of a template is tranformed into a template-less docuemnt. Remove link between XTigerTemplate structure and document. ----------------------------------------------------------------------*/ void UninstanciateTemplateDocument(Document doc) { #ifdef TEMPLATES XTigerTemplate t = GetXTigerDocTemplate(doc); if (t) Template_Close(t); #endif /* TEMPLATES */ } /*---------------------------------------------------------------------- Template_PrepareInstance Allocate XTigerTemplate structure for instance and initialize template url and template version. ----------------------------------------------------------------------*/ void Template_PrepareInstance(char *fileName, Document doc, char* template_version, char* template_url) { #ifdef TEMPLATES XTigerTemplate t = GetXTigerTemplate(fileName); if (!t) t = NewXTigerTemplate(fileName); t->state = templInstance; t->templateVersion = template_version; t->base_uri = template_url; t->doc = doc; t->ref = 1; #endif /* TEMPLATES */ } /*---------------------------------------------------------------------- SetDocumentAsXTigerTemplate Set the document template structure as template. ----------------------------------------------------------------------*/ void SetDocumentAsXTigerTemplate(Document doc) { #ifdef TEMPLATES XTigerTemplate t = GetXTigerDocTemplate(doc); if (t) t->state |= templTemplate; #endif /* TEMPLATES */ } /*---------------------------------------------------------------------- SetDocumentAsXTigerLibrary Set the document template structure as template library. ----------------------------------------------------------------------*/ void SetDocumentAsXTigerLibrary(Document doc) { #ifdef TEMPLATES XTigerTemplate t = GetXTigerDocTemplate(doc); if (t) t->state |= templLibrary; #endif /* TEMPLATES */ } /*---------------------------------------------------------------------- TemplateCreateTextBox Create a xt:use types="string" box around the selection. ----------------------------------------------------------------------*/ void TemplateCreateTextBox(Document doc, View view) { #ifdef TEMPLATES Element selElem; ElementType selType; int firstChar, lastChar; SSchema sstempl = TtaGetSSchema ("Template", doc); ElementType useType; Element use; char buffer[128]; ThotBool oldStructureChecking; char *title = TtaGetMessage (AMAYA, AM_TEMPLATE_USESTRING); char *label = TtaGetMessage (AMAYA, AM_TEMPLATE_LABEL); if (!TtaGetDocumentAccessMode(doc)) return; if (doc && TtaGetDocumentAccessMode(doc) && sstempl && IsTemplateDocument(doc) && !IsTemplateInstanceDocument(doc)) { TtaGiveFirstSelectedElement(doc, &selElem, &firstChar, &lastChar); if (selElem) { selType = TtaGetElementType(selElem); if (!TtaIsLeaf(selType)) { selElem = TtaGetFirstLeaf(selElem); selType = TtaGetElementType(selElem); firstChar = lastChar = 0; } if (selType.ElTypeNum == 1) { // request the element label QueryStringFromUser(label, title, buffer, 127); if (buffer[0] == EOS) return; useType.ElSSchema = sstempl; useType.ElTypeNum = Template_EL_useSimple; oldStructureChecking = TtaGetStructureChecking (doc); TtaSetStructureChecking (FALSE, doc); if (firstChar==0) { use = TtaNewElement(doc, useType); if (use) { TtaOpenUndoSequence (doc, NULL, NULL, 0, 0); TtaInsertSibling(use, selElem, FALSE, doc); TtaRegisterElementCreate(use, doc); TtaRegisterElementDelete (selElem, doc); TtaRemoveTree(selElem, doc); TtaInsertFirstChild(&selElem, use, doc); TtaRegisterElementDelete (selElem, doc); SetAttributeStringValue(use, Template_ATTR_types, "string"); SetAttributeStringValue(use, Template_ATTR_title, buffer); TtaCloseUndoSequence(doc); TtaSelectElement(doc, use); } } else { GenerateInlineElement(Template_EL_useSimple, sstempl, 0, "", TRUE); TtaGiveFirstSelectedElement(doc, &use, &firstChar, &lastChar); selType = TtaGetElementType(use); if (selType.ElSSchema==sstempl && selType.ElTypeNum==Template_EL_useSimple) { SetAttributeStringValue(use, Template_ATTR_types, "string"); SetAttributeStringValue(use, Template_ATTR_title, buffer); } } TtaSetStructureChecking (oldStructureChecking, doc); } } } #endif /* TEMPLATES */ } /*---------------------------------------------------------------------- TemplateCreateFreeBox Create a xt:bag types="string" box around the selection. ----------------------------------------------------------------------*/ void TemplateCreateFreeBox(Document doc, View view) { #ifdef TEMPLATES Element selElem, selElem2, parent, parent2, current, child = NULL; ElementType selType, selType2; int firstChar, lastChar, firstChar2, lastChar2; SSchema sstempl = TtaGetSSchema ("Template", doc); ElementType bagType; Element bag; char buffer[128]; char *title = TtaGetMessage (AMAYA, AM_TEMPLATE_BAGANY); char *label = TtaGetMessage (AMAYA, AM_TEMPLATE_LABEL); if (!TtaGetDocumentAccessMode(doc)) return; if (doc && TtaGetDocumentAccessMode(doc) && sstempl && IsTemplateDocument(doc) && !IsTemplateInstanceDocument(doc)) { TtaGiveFirstSelectedElement(doc, &selElem, &firstChar, &lastChar); TtaGiveLastSelectedElement(doc, &selElem2, &firstChar2, &lastChar2); if (selElem && selElem2) { // request the element label QueryStringFromUser(label, title, buffer, 127); if (buffer[0] == EOS) return; selType = TtaGetElementType(selElem); selType2 = TtaGetElementType(selElem2); bagType.ElSSchema = sstempl; bagType.ElTypeNum = Template_EL_bag; parent = TtaGetParent(selElem); parent2 = TtaGetParent(selElem2); if (firstChar==0 && firstChar2==0 && parent == parent2) { ThotBool oldStructureChecking; DisplayMode dispMode; dispMode = TtaGetDisplayMode (doc); if (dispMode == DisplayImmediately) TtaSetDisplayMode (doc, DeferredDisplay); oldStructureChecking = TtaGetStructureChecking (doc); TtaSetStructureChecking (FALSE, doc); // Create and insert xt:bag element bag = TtaNewElement(doc, bagType); TtaOpenUndoSequence (doc, NULL, NULL, 0, 0); TtaInsertSibling(bag, selElem, TRUE, doc); TtaRegisterElementCreate(bag, doc); TtaNextSibling(&selElem2); while (selElem!=selElem2) { current = selElem; TtaNextSibling(&selElem); TtaRegisterElementDelete (current, doc); TtaRemoveTree(current, doc); if (child) TtaInsertSibling(current, child, FALSE, doc); else TtaInsertFirstChild(¤t, bag, doc); TtaRegisterElementDelete (current, doc); child = current; } SetAttributeStringValue(bag, Template_ATTR_types, "any"); SetAttributeStringValue(bag, Template_ATTR_title, buffer); TtaCloseUndoSequence(doc); TtaSelectElement(doc, bag); TtaSetStructureChecking (oldStructureChecking, doc); TtaSetDisplayMode (doc, dispMode); } } } #endif /* TEMPLATES */ } /*---------------------------------------------------------------------- TemplateCreateUnion Create a xt:union around the selection. ----------------------------------------------------------------------*/ void TemplateCreateUnion(Document doc, View view) { #ifdef TEMPLATES SSchema sstempl = TtaGetSSchema ("Template", doc); Element head, sibling, unionEl; ElementType unionType; XTigerTemplate t = GetXTigerDocTemplate(doc); char *proposed, *name = NULL, *types=NULL; Element selElem; ElementType selType; int firstChar, lastChar; if (t && sstempl) { TtaGiveFirstSelectedElement(doc, &selElem, &firstChar, &lastChar); selType = TtaGetElementType(selElem); if (selType.ElSSchema==sstempl && selType.ElTypeNum==Template_EL_union) Template_ModifyUnionElement(doc, selElem); else { proposed = Template_GetAllDeclarations(t, TRUE, FALSE, TRUE); if (QueryUnionFromUser(proposed, NULL, &name, &types, TRUE)) { TtaOpenUndoSequence (doc, NULL, NULL, 0, 0); head = TemplateFindHead(doc); sibling = TtaGetLastChild(head); unionType.ElSSchema = sstempl; unionType.ElTypeNum = Template_EL_union; unionEl = TtaNewElement(doc, unionType); if (sibling) TtaInsertSibling(unionEl, sibling, FALSE, doc); else { sibling = unionEl; TtaInsertFirstChild(&sibling, head, doc); } Template_SetName(doc, unionEl, name, TRUE); SetAttributeStringValue(unionEl, Template_ATTR_includeAt, types); TtaSetAccessRight(unionEl, ReadOnly, doc); TtaRegisterElementCreate(unionEl, doc); TtaSelectElement(doc, unionEl); TtaCloseUndoSequence(doc); Template_DeclareNewUnion (t, name, types, ""); TtaFreeMemory(proposed); TtaFreeMemory(name); TtaFreeMemory(types); } } } #endif /* TEMPLATES */ } /*---------------------------------------------------------------------- Template_ModifyUnionElement Query the user to modify an xt:union ----------------------------------------------------------------------*/ void Template_ModifyUnionElement(Document doc, Element unionEl) { XTigerTemplate t = GetXTigerDocTemplate(doc); SSchema sstempl = TtaGetSSchema ("Template", doc); ElementType unionType; char *proposed, *checked, *name, *types=NULL; if (doc && unionEl && t && sstempl) { unionType = TtaGetElementType(unionEl); if (unionType.ElSSchema==sstempl && unionType.ElTypeNum==Template_EL_union) { proposed = Template_GetAllDeclarations(t, TRUE, FALSE, TRUE); checked = GetAttributeStringValueFromNum(unionEl, Template_ATTR_includeAt, NULL); name = GetAttributeStringValueFromNum(unionEl, Template_ATTR_name, NULL); if (QueryUnionFromUser(proposed, checked, &name, &types, FALSE)) { TtaOpenUndoSequence (doc, NULL, NULL, 0, 0); SetAttributeStringValueWithUndo(unionEl, Template_ATTR_includeAt, types); TtaCloseUndoSequence(doc); } TtaFreeMemory(proposed); TtaFreeMemory(checked); TtaFreeMemory(name); TtaFreeMemory(types); } } } /*---------------------------------------------------------------------- TemplateCreateRepeat Create a xt:repeat around the selection. ----------------------------------------------------------------------*/ void TemplateCreateRepeat(Document doc, View view) { Template_CreateRepeatFromSelection(doc, FALSE); } /*---------------------------------------------------------------------- TemplateCreateRepeatComp Create a xt:component with the selection and use it in a new xt:repeat ----------------------------------------------------------------------*/ void TemplateCreateRepeatComp(Document doc, View view) { Template_CreateRepeatFromSelection(doc, TRUE); } /*---------------------------------------------------------------------- Template_CreateRepeatFromSelection Create a xt:repeat with the selection. If selection is empty, insert an inline xt:use. If createComp is false, create a xt:use with types at the selected block. If createComp is true, create a component with the selection. Return the xt:use element. ----------------------------------------------------------------------*/ Element Template_CreateRepeatFromSelection(Document doc, ThotBool createComp) { #ifdef TEMPLATES ThotBool oldStructureChecking; DisplayMode dispMode; Element selElem, selElem2, parent, parent2; ElementType selType, selType2; int firstChar, lastChar, firstChar2, lastChar2; SSchema sstempl = TtaGetSSchema ("Template", doc); const char *title = TtaGetMessage (AMAYA, AM_TEMPLATE_REPEATCOMP); const char *label = TtaGetMessage (AMAYA, AM_TEMPLATE_REPEATLABEL); char buffer[128]; ElementType repType; Element rep, use; if (!TtaGetDocumentAccessMode(doc)) return NULL; if (doc && TtaGetDocumentAccessMode(doc) && sstempl && IsTemplateDocument(doc) && !IsTemplateInstanceDocument(doc)) { TtaGiveFirstSelectedElement(doc, &selElem, &firstChar, &lastChar); TtaGiveLastSelectedElement(doc, &selElem2, &firstChar2, &lastChar2); if (selElem && selElem2) { // request the element label QueryStringFromUser(label, title, buffer, 127); if (buffer[0] == EOS) return NULL; selType = TtaGetElementType(selElem); selType2 = TtaGetElementType(selElem2); repType.ElSSchema = sstempl; repType.ElTypeNum = Template_EL_repeat; parent = TtaGetParent(selElem); parent2 = TtaGetParent(selElem2); if (firstChar==0 && firstChar2==0 && parent == parent2 && buffer[0]!=0) { dispMode = TtaGetDisplayMode (doc); if (dispMode == DisplayImmediately) TtaSetDisplayMode (doc, DeferredDisplay); oldStructureChecking = TtaGetStructureChecking (doc); TtaSetStructureChecking (FALSE, doc); if (selElem==selElem2 && selType.ElSSchema==sstempl && (selType.ElTypeNum==Template_EL_useEl || selType.ElTypeNum==Template_EL_useSimple)) use = selElem; else use = Template_CreateUseFromSelection(doc, createComp); if (use) { TtaExtendUndoSequence(doc); rep = TtaNewElement(doc, repType); TtaInsertSibling(rep, use, FALSE, doc); SetAttributeStringValue(rep, Template_ATTR_title, buffer); TtaRegisterElementCreate(rep, doc); TtaRegisterElementDelete (use, doc); TtaRemoveTree(use, doc); TtaInsertFirstChild(&use, rep, doc); TtaRegisterElementCreate(use, doc); TtaSelectElement(doc, use); TtaCloseUndoSequence(doc); } TtaSetStructureChecking (oldStructureChecking, doc); TtaSetDisplayMode (doc, dispMode); return rep; } } } #endif /* TEMPLATES */ return NULL; } /*---------------------------------------------------------------------- TemplateCreateUseBox Create a xt:use around the selection. ----------------------------------------------------------------------*/ void TemplateCreateUseBox(Document doc, View view) { #ifdef TEMPLATES Template_CreateUseFromSelection(doc, FALSE); #endif /* TEMPLATES */ } /*---------------------------------------------------------------------- TemplateCreateUseCompBox Create a xt:use around the selection. ----------------------------------------------------------------------*/ void TemplateCreateUseCompBox(Document doc, View view) { #ifdef TEMPLATES Template_CreateUseFromSelection(doc, TRUE); #endif /* TEMPLATES */ } /*---------------------------------------------------------------------- Template_CreateInlineUse Create an inline xt:use with the selection. ----------------------------------------------------------------------*/ static Element Template_CreateInlineUse(Document doc) { #ifdef TEMPLATES SSchema sstempl = TtaGetSSchema ("Template", doc); int firstChar, lastChar; Element selElem, use = NULL; XTigerTemplate t = GetXTigerDocTemplate(doc); char *proposed, *label = NULL, *types=NULL; ThotBool option; if (t && sstempl) { proposed = Template_GetInlineLevelDeclarations(t, TRUE, TRUE); if (QueryNewUseFromUser(proposed, &label, &types, &option)) { TtaOpenUndoSequence (doc, NULL, NULL, 0, 0); GenerateInlineElement(Template_EL_useEl, sstempl, 0, NULL, TRUE); TtaGiveFirstSelectedElement(doc, &selElem, &firstChar, &lastChar); use = selElem; TtaNextSibling(&use); SetAttributeStringValue(use, Template_ATTR_title, label); SetAttributeStringValue(use, Template_ATTR_types, types); TtaSelectElement(doc, use); TtaCloseUndoSequence(doc); } TtaFreeMemory(proposed); TtaFreeMemory(label); TtaFreeMemory(types); return use; } #endif /* TEMPLATES */ return FALSE; } /*---------------------------------------------------------------------- Template_CreateEmptyBlockUse Create a block xt:use with the selection. ----------------------------------------------------------------------*/ static Element Template_CreateEmptyBlockUse(Document doc) { #ifdef TEMPLATES SSchema sstempl = TtaGetSSchema ("Template", doc); ElementType useType; int firstChar, lastChar; Element selElem, use; XTigerTemplate t = GetXTigerDocTemplate(doc); char *proposed, *label = NULL, *types=NULL; ThotBool option, res = FALSE; if (t && sstempl) { proposed = Template_GetBlockLevelDeclarations(t, TRUE); if (QueryNewUseFromUser(proposed, &label, &types, &option)) { TtaOpenUndoSequence (doc, NULL, NULL, 0, 0); TtaGiveFirstSelectedElement(doc, &selElem, &firstChar, &lastChar); useType.ElSSchema = sstempl; useType.ElTypeNum = Template_EL_useEl; use = TtaNewElement(doc, useType); TtaInsertSibling(use, selElem, FALSE, doc); TtaDeleteTree(selElem, doc); SetAttributeStringValue(use, Template_ATTR_title, label); SetAttributeStringValue(use, Template_ATTR_types, types); TtaRegisterElementCreate(use, doc); res = TRUE; TtaSelectElement(doc, use); TtaCloseUndoSequence(doc); } TtaFreeMemory(proposed); TtaFreeMemory(label); TtaFreeMemory(types); return use; } #endif /* TEMPLATES */ return FALSE; } /*---------------------------------------------------------------------- Template_CreateUseFromSelection Create a xt:use with the selection. If selection is empty, insert an inline xt:use. If createComp is false, create a xt:use with types at the selected block. If createComp is true, create a component with the selection. Return the xt:use element. ----------------------------------------------------------------------*/ Element Template_CreateUseFromSelection(Document doc, ThotBool createComp) { #ifdef TEMPLATES ThotBool oldStructureChecking, option; DisplayMode dispMode; Element selElem, selElem2, parent, parent2; ElementType selType, selType2; int firstChar, lastChar, firstChar2, lastChar2; SSchema sstempl = TtaGetSSchema ("Template", doc), sshtml = TtaGetSSchema ("HTML", doc); XTigerTemplate t; ElementType useType; Element use = NULL, comp, prev; char *proposed, *checked, *types=NULL; char buffer[128]; int sz = 128; if (!TtaGetDocumentAccessMode(doc)) return NULL; const char *title = TtaGetMessage (AMAYA, AM_TEMPLATE_USE); const char *label = TtaGetMessage (AMAYA, AM_NAME); if (doc && TtaGetDocumentAccessMode(doc) && sstempl && IsTemplateDocument(doc) && !IsTemplateInstanceDocument(doc)) { TtaGiveFirstSelectedElement(doc, &selElem, &firstChar, &lastChar); TtaGiveLastSelectedElement(doc, &selElem2, &firstChar2, &lastChar2); // printf(">> %p %d %d %s (%d)\n", selElem, firstChar, lastChar, TtaGetElementTypeName(TtaGetElementType(selElem)), TtaGetElementType(selElem).ElTypeNum); // printf(" > %p %d %d %s (%d)\n", selElem2, firstChar2, lastChar2, TtaGetElementTypeName(TtaGetElementType(selElem2)), TtaGetElementType(selElem2).ElTypeNum); t = GetXTigerDocTemplate(doc); if (selElem && selElem2 && t) { selType = TtaGetElementType(selElem); selType2 = TtaGetElementType(selElem2); parent = TtaGetParent(selElem); parent2 = TtaGetParent(selElem2); dispMode = TtaGetDisplayMode (doc); if (dispMode == DisplayImmediately) TtaSetDisplayMode (doc, DeferredDisplay); oldStructureChecking = TtaGetStructureChecking (doc); TtaSetStructureChecking (FALSE, doc); if (firstChar==0 && firstChar2==0 && parent == parent2) { if (selType.ElSSchema==sstempl) { // The selection is a template element. switch(selType.ElTypeNum) { case Template_EL_useEl: case Template_EL_useSimple: // TODO query to add/remove some types if (TtaGetElementVolume(selElem)==0) { proposed = Template_GetAllDeclarations(t, TRUE, TRUE, TRUE); checked = GetAttributeStringValueFromNum(selElem, Template_ATTR_types, NULL); option = GetAttributeIntValueFromNum(selElem, Template_ATTR_option)==Template_ATTR_option_VAL_option_set; if (QueryUseFromUser(proposed, checked, buffer, 128, &types, &option)) { TtaOpenUndoSequence (doc, NULL, NULL, 0, 0); SetAttributeStringValueWithUndo(selElem, Template_ATTR_types, types); SetAttributeStringValueWithUndo(selElem, Template_ATTR_title, buffer); if (option) SetAttributeIntValue(selElem, Template_ATTR_option, Template_ATTR_option_VAL_option_set, TRUE); else SetAttributeIntValue(selElem, Template_ATTR_option, Template_ATTR_option_VAL_option_unset, TRUE); TtaCloseUndoSequence(doc); } TtaFreeMemory(proposed); TtaFreeMemory(checked); TtaFreeMemory(types); createComp = FALSE; } break; default: // Do nothing break; } } else if (selType.ElSSchema==sshtml && (selType.ElTypeNum==HTML_EL_Element || selType.ElTypeNum==HTML_EL_Basic_Elem || (selType.ElTypeNum==HTML_EL_Paragraph && TtaGetElementVolume(selElem)==0))) { // Create a block-level xt:use instead of selection. use = Template_CreateEmptyBlockUse(doc); } else { prev = selElem; TtaPreviousSibling(&prev); if (createComp) { // Create a xt:component from selection and create a xt:use using it comp = Template_CreateComponentFromSelection(doc); if (comp) { TtaExtendUndoSequence(doc); useType.ElSSchema = sstempl; useType.ElTypeNum = Template_EL_useEl; use = TtaNewElement(doc, useType); if (prev) TtaInsertSibling(use, prev, FALSE, doc); else TtaInsertFirstChild(&use, parent, doc); GiveAttributeStringValueFromNum(comp, Template_ATTR_name, buffer, &sz); SetAttributeStringValue(use, Template_ATTR_types, buffer); SetAttributeStringValue(use, Template_ATTR_title, buffer); TtaRegisterElementCreate(use, doc); TtaSelectElement(doc, use); TtaCloseUndoSequence(doc); } } else { QueryStringFromUser(label, title, buffer, 127); // Create a xt:use around the selection TtaOpenUndoSequence (doc, NULL, NULL, 0, 0); TtaRegisterElementDelete (selElem, doc); TtaRemoveTree(selElem, doc); useType.ElSSchema = sstempl; useType.ElTypeNum = Template_EL_useSimple; use = TtaNewElement(doc, useType); if (prev) TtaInsertSibling(use, prev, FALSE, doc); else TtaInsertFirstChild(&use, parent, doc); TtaInsertFirstChild(&selElem, use, doc); SetAttributeStringValue(use, Template_ATTR_types, TtaGetElementTypeName(TtaGetElementType(selElem))); if (buffer[0]!=EOS) SetAttributeStringValue(use, Template_ATTR_title, buffer); TtaRegisterElementCreate(use, doc); TtaCloseUndoSequence(doc); } } } else if (lastChardocument); char* elemName = GetAttributeStringValueFromNum(event->element, Template_ATTR_name, NULL); if (Template_IsUsedComponent(t, event->document, elemName)) { TtaDisplaySimpleMessage (CONFIRM, AMAYA, AM_TEMPLATE_USEDCOMP_CANTREMOVE); return TRUE; } #endif /* TEMPLATES */ return FALSE; } /*---------------------------------------------------------------------- UnionClicked Called when a xt:union is clicked ----------------------------------------------------------------------*/ void UnionClicked(NotifyElement* event) { if (event->document && event->element) TtaSelectElement(event->document, event->element); } /*---------------------------------------------------------------------- UnionDoubleClicked Called when a xt:union is double clicked ----------------------------------------------------------------------*/ ThotBool UnionDoubleClicked(NotifyElement* event) { Template_ModifyUnionElement(event->document, event->element); TtaSelectElement(event->document, event->element); return TRUE; } /*---------------------------------------------------------------------- TemplateNameAttributeDeleted Called when a xt:name will be deleted ----------------------------------------------------------------------*/ ThotBool TemplateNameAttributeDeleted(NotifyAttribute* event) { // Prevent xt:name deletion return TRUE; } /*---------------------------------------------------------------------- TemplateNameAttributeModified Called when a xt:name will be modified ----------------------------------------------------------------------*/ ThotBool TemplateNameAttributeModified(NotifyAttribute* event) { // Prevent xt:name modification return TRUE; } /*---------------------------------------------------------------------- TemplateNameAttributeCreated Called when a xt:name have been created ----------------------------------------------------------------------*/ void TemplateNameAttributeCreated(NotifyAttribute* event) { MakeUniqueName(event->element, event->document, TRUE, FALSE); } /*---------------------------------------------------------------------- TemplateNameAttrInMenu Called by Thot when building the Attributes menu for template elements. ----------------------------------------------------------------------*/ ThotBool TemplateNameAttrInMenu (NotifyAttribute * event) { #ifdef TEMPLATES ElementType type = TtaGetElementType(event->element); if (type.ElTypeNum==Template_EL_component || type.ElTypeNum==Template_EL_union) return TRUE; #endif /* TEMPLATES */ return FALSE; }