;
; $Id: widalloc.pro,v 1.7 1994/06/01 23:08:48 ali Exp $
;
;  WidAlloc
;   Widget Editor allocation related routines.
;
; Copyright (c) 1993, Research Systems, Inc.  All rights reserved.
;   Unauthorized reproduction prohibited.
;
; MODIFICATION HISTORY
;       Written by:     Joshua Goldstein,       12/93
;



;
;  Destroy
;   Generic function to cause an object to delete itself from memory.
;   This is assumed to be recursive (objects containing other objects
;   should destroy their children as well).
;
PRO Destroy, Ptr

    ;   An invalid pointer is considered to be already destroyed
    ;   Not much we could do anyway.
    IF WIDGET_INFO(Ptr, /VALID_ID) EQ 0 THEN RETURN

    ;   Figure out the class specific routine name and call it
    GetType, Ptr, Type
    CALL_PROCEDURE,Type+"_Destroy",Ptr
END


;
;  GenDestroy
;   Generic Object destruction routine.
;
PRO GenDestroy, Ptr, HASVALUE=HasValue
    Ptr2Obj, Ptr, Obj

    IF N_ELEMENTS(Obj) EQ 0 THEN RETURN

    IF KEYWORD_SET(HasValue) THEN $
        WIDGET_CONTROL, Obj.Value1, /DESTROY    ; Destroy value contents

    IF WIDGET_INFO(Obj.Dialog, /VALID) THEN $   ; Destroy dialog box
        WIDGET_CONTROL, Obj.Dialog, /DESTROY

    WIDGET_CONTROL, Ptr, /DESTROY               ; Release pointer memory
    ;   Object is in local variable and goes away as we return
END


;
;  GenCopy
;   Copy an object.  2 copy methods:
;
;   if( copy != NULL)       { *copy = *ptr; free(ptr); }
;   else                    { *(copy = malloc(...)) = *ptr; }
;
PRO GenCopy, Ptr, Copy, HASVALUE=HasValue
  COMMON WidEd_Comm

    IF KEYWORD_SET(Copy) THEN BEGIN     ; Copy is already allocated

        Ptr2Obj, Copy, ThrowAway        ; Release current copy contents

        IF KEYWORD_SET(HasValue) THEN $
            WIDGET_CONTROL, ThrowAway.Value1, /DESTROY  ; Destroy value too

        Ptr2Obj, Ptr, Obj               ; Remove object from original pointer
        Obj2Ptr, Obj, Copy              ; Store in copy pointer
        WIDGET_CONTROL, Ptr, /DESTROY   ; Release original pointer memory

    ENDIF ELSE BEGIN

        Ptr2Obj, Ptr, Obj, /COPY        ; Make a copy of ptr contents

        IF KEYWORD_SET(HasValue) THEN $
            Ptr2Obj, Obj.Value1, Value1, /COPY  ; Get copy of value1 contents

        Copy    = WIDGET_BASE(GROUP=TopDlg)     ; Make a new pointer

        IF KEYWORD_SET(HasValue) THEN BEGIN
            Obj.Value1  = WIDGET_BASE(GROUP=TopDlg) ; New ptr for value
            IF N_ELEMENTS(Value1) NE 0 THEN $
                Obj2Ptr, Value1, Obj.Value1     ; Save value
        ENDIF

        Obj.Id  = NewId()
        Obj2Ptr, Obj, Copy              ; Store copy into new pointer

    ENDELSE
END


;
;  Ptr2Obj
;   Pointers are really unrealized base widget objects and the
;   contents are their UVALUEs.  In general, copying structures is
;   an expensive operation so the default is to REMOVE the UVALUE
;   from the pointer.  This is much faster.  Note that is has the
;   side effect that the pointer is no longer valid (has no object
;   in it).
;
PRO Ptr2Obj, Ptr, Obj, COPY=Copy
    WIDGET_CONTROL, Ptr, GET_UVALUE=Obj, NO_COPY=(1 - KEYWORD_SET(Copy))
END


;
;  Obj2Ptr
;   The reverse of Ptr2Obj.  Place the given object (as a UVALUE) into
;   the given pointer (unrealized base widget).  Copy the value if
;   explicitly requested, otherwise the Object given us will no longer
;   contain a value upon return from this function.
;
PRO Obj2Ptr, Obj, Ptr, COPY=Copy
    WIDGET_CONTROL, Ptr, SET_UVALUE=Obj, NO_COPY=(1 - KEYWORD_SET(Copy))
END


;
;  NextPtr
;   Returned the contents of the .Next field of an object.  All objects
;   must have a .Next field.
;
FUNCTION NextPtr, Ptr
    Ptr2Obj, Ptr, Obj
    Next    = Obj.Next
    Obj2Ptr, Obj, Ptr
    RETURN, Next
END


;
;  AddChild
;       Add an object to a base object.
;
;   NO_UPDATE -- If set, DONT call the routine to update the Cut/Copy/Paste
;           dialog boxes.  Set when adding multiple objects (as in File Open)
;
;   NO_CANCEL -- If set the object is not added to the Active dialog
;           list.  This is done for base objects (Base Object Dialog boxes
;           have no CANCEL button) and should be done for any object class
;           which can not/should not be removed.
;
PRO AddChild, Parent, Child, NO_UPDATE=NoUpdate, NO_CANCEL=NoCancel

  COMMON WidEd_Comm

    ;   Set Parent Ptr in child
    SetTag, Child, "Parent", Parent

    ;   Get the Parent object structure
    Ptr2Obj, Parent, PObj

    ;   First Child ? Set parent child list.
    ;   Else last child in list now has a next child
    ;
    IF PObj.Children EQ 0 THEN PObj.Children    = Child $
    ELSE SetTag, PObj.LastChild, "Next", Child

    ;   Remember the last child
    PObj.LastChild  = Child

    Obj2Ptr, PObj, Parent       ; Restore structure into parent pointer

    ;   Add to active dialog list so we can delete it in the event
    ;   user CANCELs addition.
    IF KEYWORD_SET(NoCancel) EQ 0 THEN BEGIN
        NewDialogs  = [ NewDialogs, { WE_NEWOBJ, Parent, Child, 0L } ]
    ENDIF

    ;   Update Cut/Copy/Paste/Edit Dialog boxes
    IF KEYWORD_SET(NoUpDate) EQ 0 THEN UpdateEdit

    ;   Note that the object tree has been altered.
    Dirty   = 1
END


;
;  Cancel
;   Common routine to handle the 'CANCEL' button on dialog boxes.
;   This performs 2 separate actions depending upon what has happened
;   so far:
;
;   If the object has been added but never realized (no Rebuild) then
;   OldPtr is NULL and there is no state to revert the widget to.
;   Otherwise, we have a copy of the widgets previous state and we
;   restore it instead of deleting it.
;
;   Note: The object information has already been pulled out of the pointer
;   so we need both to be passed in.
;
PRO Cancel, Obj, Ptr

  COMMON WidEd_Comm    

    Type    = Obj.Type      ; Get the type
    Dialog  = Obj.Dialog    ; Get the Dialog Box widget Id.
    Obj2Ptr, Obj, Ptr       ; Stick Object back into pointer so that
                            ; other routines can access object via its ptr

    ;   See if this object can be cancelled. Should always be found.
    ;   In fact we display an error message if we don't

    Idx = WHERE(NewDialogs.ObjPtr EQ Ptr, Count)

    IF Count NE 1 THEN ErrorDialog, TopDlg, $
        "Internal Error: CANCEL'ed widget could not be found"

    ;   Do we have an previous version of this widget to revert to?

    Idx = Idx(0)
    IF NewDialogs(Idx).OldPtr NE 0L THEN BEGIN

        ;   Call class specific copy routine to revert object to
        ;   its previous state:  *ObjPtr = *OldPtr

        CALL_PROCEDURE, Type + "_Copy", NewDialogs(Idx).OldPtr, $
                    NewDialogs(Idx).ObjPtr
        WIDGET_CONTROL, Dialog, /DESTROY    ; Take down dialog box

    ENDIF ELSE BEGIN

        ;   This is more difficult.  Remove the object from its
        ;   parent's list of children

        ParPtr  = NewDialogs(Idx).ParPtr        ; Get Parent Object
        Ptr2Obj, ParPtr, ParObj

        ;   Is it the first child?
        IF ParObj.Children EQ Ptr THEN BEGIN
            ParObj.Children = NextPtr(Ptr)

            ;   Was it an only child?
            IF ParObj.LastChild EQ Ptr THEN ParObj.LastChild = ParObj.Children

        ENDIF ELSE BEGIN


            PrevPtr = ParObj.Children           ; Run down the list of children
            CurrPtr = NextPtr(PrevPtr)          ; until we find the child in
            NxtPtr  = NextPtr(Ptr)              ; question. Keep track of the
            WHILE CurrPtr NE Ptr DO BEGIN       ; previous child.
                PrevPtr = CurrPtr
                CurrPtr = NextPtr(CurrPtr)
            ENDWHILE

            ; Unlink the deleted child from the list of children
            ; If it was the last child of the parent object then set
            ; the LastChild field to the new last child.
            SetTag, PrevPtr, "Next", NxtPtr
            IF ParObj.LastChild EQ Ptr THEN ParObj.LastChild = PrevPtr

        ENDELSE

        Obj2Ptr, ParObj, ParPtr     ; Restore Parent pointer

        ;   The child has been removed from the object tree. Delete
        ;   the child.
        CALL_PROCEDURE, Type + "_Destroy", Ptr  ; Destroy Object
    ENDELSE

    ;   Now we have to remove the deleted object from the list
    ;   of objects with active dialog boxes.

    N   = N_ELEMENTS(NewDialogs)
    IF Idx(0) EQ N-1 THEN $                 ; Last active object?
        NewDialogs  = NewDialogs(0:N-2) $
    ELSE $
        NewDialogs  = [ NewDialogs(0:Idx-1), NewDialogs(Idx+1,*) ]
    UpdateEdit
END


;
;  Accept
;   The user has pressed the 'DONE' button or just closed the dialog
;   box via the window manager menu Close option.  Unfortunately these
;   look like 'DONE's and not 'CANCEL's. Thats what documentation is for.
;
;   Note: as with Cancel, the Object structure is already outside of
;       the pointer
;       Unlike Cancel, Base Objects also need to be handled (not that
;       this makes any difference here)
;
PRO Accept, Obj, Ptr
    Obj2Ptr, Obj, Ptr       ; Stick Object back into pointer
END

PRO WidAlloc
END