/*************************************************************************\
 *                                                                       *
 * docgroup.c                                                            *
 *                                                                       *
 * Copyright 1999-2000 Emmanuel Lesueur                                  *
 *                                                                       *
\*************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <libraries/mui.h>
#include <libraries/iffparse.h>
#ifdef HAVE_MUI_LISTTREE_H
#   include <mui/Listtree_mcc.h>
#endif
#include <dos/dostags.h>
#include <exec/nodes.h>
#include <exec/execbase.h>
#include <workbench/startup.h>
#include <workbench/workbench.h>
#define NO_INLINE_STDARG
#include <proto/muimaster.h>
#undef NO_INLINE_STDARG
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/graphics.h>
#define NO_INLINE_STDARG
#include <proto/intuition.h>
#undef NO_INLINE_STDARG
#include <proto/utility.h>
#include "AComm.h"
#include "Apdf.h"
#include "docbitmap.h"
#include "document.h"
#include "docgroup.h"
#include "pageslider.h"
#include "msgs.h"

#define DB(x)   //x
#define Static static

#ifdef __SASC
#   define SAVEDS   __saveds
#   define ASM      __asm
#   define REG(r,a) register __ ## r a
#elif defined(__GNUC__)
#   define SAVEDS
#   define ASM
#   define REG(r,a) a __asm(#r)
#endif

struct DocGroupData {
    struct comm_info *comm_info;
    Object *doc;
    Object *docchg;
    Object *pageobj;
    Object *zoomobj;
    Object *bitmapobj;
    Object *vgroup;
    Object *linkobj;
    Object *searchobj;
    Object *outlineswin;
    Object *outlineslv;
    Object *group;
    Object *group2;
    Object *group3;
    Object *rect;
    BOOL use_vgroup;
};
typedef struct DocGroupData DocGroupData;

/*
 *  Server interface functions.
 */

Static int init_find(struct comm_info *ci,const char* str) {
    struct msg_find* p=ci->cmddata;
    strcpy(p->str,str);
    return (*ci->sv->exchange_msg)(ci,&p->base,sizeof(*p),MSGID_INITFIND);
}

Static int find(struct comm_info *ci,struct PDFDoc *doc,int start,int stop,int* xmin,int* ymin,int* xmax,int* ymax,int* page) {
    struct msg_find* p=ci->cmddata;
    int r;
    p->doc=doc;
    p->top=start;
    p->bottom=stop;
    /*p->xmin=*xmin;
    p->ymin=*ymin;
    p->xmax=*xmax;
    p->ymax=*ymax;
    p->page=*page;*/
    r=(*ci->sv->exchange_msg)(ci,&p->base,sizeof(*p),MSGID_FIND);
    *xmin=p->xmin;
    *ymin=p->ymin;
    *xmax=p->xmax;
    *ymax=p->ymax;
    *page=p->page;
    return r;
}

Static void end_find(struct comm_info *ci) {
    (*ci->sv->exchange_msg)(ci,NULL,0,MSGID_ENDFIND);
}


extern const char *zoomEntries[];


/*
 *  DocGroup Methods.
 */

Static ULONG dgNew(struct IClass *cl,Object *obj,struct opSet *msg) {
    DocGroupData* dat;
    struct TagItem tags[4];
    Object *bitmapobj,*vgroup,*pageobj,*rect,*group3;
    Object *zoomobj,*nxtsrchobj,*linkobj,*searchobj;
    Object *backobj,*fwdobj,*menu,*group,*group2;
    Object *doc=(Object*)GetTagData(MYATTR_DocGroup_Document,0,msg->ops_AttrList);
    struct comm_info *ci=(struct comm_info*)GetTagData(MYATTR_DocGroup_CommInfo,0,msg->ops_AttrList);
    struct PDFDoc *pdfdoc;
    int num_pages;
    if(!doc || !ci)
	return 0;
    DoMethod(doc,MYM_Document_IncRef);
    get(doc,MYATTR_Document_NumPages,&num_pages);
    get(doc,MYATTR_Document_PDFDoc,&pdfdoc);

    vgroup=myVirtgroupObject,
	VirtualFrame,
	MUIA_CycleChain,TRUE,
       Child,group2=HGroup,
	   End,
	End;

    pageobj=PageSliderObject,
	MYATTR_PageSlider_Document,doc,
	MYATTR_PageSlider_CommInfo,ci,
	MUIA_Weight,1000,
	End;

    if(vgroup && pageobj) {
	const char *pagelabel;
	int page,zoom,rotate;
	struct MYS_DocBitmap_CropBox *box;
	pagelabel=(const char*)GetTagData(MYATTR_DocGroup_PageLabel,0,msg->ops_AttrList);
	if(pagelabel) {
	    set(pageobj,MYATTR_PageSlider_Label,pagelabel);
	    get(pageobj,MYATTR_PageSlider_Value,&page);
	} else
	    page=GetTagData(MYATTR_DocGroup_Page,-1,msg->ops_AttrList);
	zoom=GetTagData(MYATTR_DocGroup_Zoom,-1,msg->ops_AttrList);
	rotate=GetTagData(MYATTR_DocGroup_Rotate,-1,msg->ops_AttrList);
	box=(APTR)GetTagData(MYATTR_DocGroup_CropBox,0,msg->ops_AttrList);
	bitmapobj=DocBitmapObject,
	    MYATTR_DocBitmap_CommInfo,ci,
	    MYATTR_DocBitmap_VGroup,vgroup,
	    MYATTR_DocBitmap_Document,doc,
	    page==-1?TAG_IGNORE:MYATTR_DocBitmap_Page,page,
	    zoom==-1?TAG_IGNORE:MYATTR_DocBitmap_Zoom,zoom,
	    rotate==-1?TAG_IGNORE:MYATTR_DocBitmap_Rotate,rotate,
	    box?MYATTR_DocBitmap_CropBox:TAG_IGNORE,box,
	    End;
	if(bitmapobj) {
	    DoMethod(group2,OM_ADDMEMBER,bitmapobj);
	    get(bitmapobj,MYATTR_DocBitmap_Page,&page);
	    set(pageobj,MYATTR_PageSlider_Value,page);
	} else {
	    MUI_DisposeObject(vgroup);
	    vgroup=NULL;
	    /* the call to DoSuperMethod below will fail. */
	}
    }

    tags[0].ti_Tag=MUIA_Group_Child;
    tags[0].ti_Data=(ULONG)(group=HGroup,
	MUIA_Group_Spacing,0,
	MUIA_Group_PageMode,TRUE,
	Child,ScrollgroupObject,
	MUIA_Scrollgroup_Contents,vgroup,
	    End,
	Child,group3=HGroup,
	    VirtualFrame,
	    Child,rect=RectangleObject,
		End,
	    End,
	End);
    tags[1].ti_Tag=MUIA_Group_Child;
    tags[1].ti_Data=(ULONG)HGroup,
	Child,backobj=SimpleButton(STR(MSG_BACK_GAD)),
	Child,fwdobj=SimpleButton(STR(MSG_FORWARD_GAD)),
	Child,Label2(STR(MSG_PAGE_GAD)),
	Child,pageobj,
	End;
    tags[2].ti_Tag=MUIA_Group_Child;
    tags[2].ti_Data=(ULONG)HGroup,
	Child,HGroup,
	    Child,KeyLabel2(KSTR(MSG_ZOOM_GAD),KEY(MSG_ZOOM_GAD)),
	    Child,zoomobj=CycleObject,
		MUIA_Cycle_Entries,zoomEntries,
		MUIA_Cycle_Active,defZoom,
		MUIA_CycleChain,TRUE,
		MUIA_ControlChar,KEY(MSG_ZOOM_GAD),
		End,
	    Child,KeyLabel2(KSTR(MSG_SEARCH_GAD),KEY(MSG_SEARCH_GAD)),
	    Child,searchobj=StringObject,
		StringFrame,
		MUIA_String_MaxLen,99,
		MUIA_Weight,200,
		MUIA_CycleChain,TRUE,
		MUIA_ControlChar,KEY(MSG_SEARCH_GAD),
		MUIA_Weight,1000,
		End,
	    Child,nxtsrchobj=SimpleButton(STR(MSG_NEXT_GAD)),
	    End,
	Child,BalanceObject,
	    End,
	Child,linkobj=TextObject,
	    TextFrame,
	    MUIA_Background,MUII_TextBack,
	    End,
	End;
    tags[3].ti_Tag=TAG_MORE;
    tags[3].ti_Data=(ULONG)msg->ops_AttrList;
    msg->ops_AttrList=tags;

    if(obj=(Object *)DoSuperMethodA(cl,obj,(Msg)msg)) {
	dat=INST_DATA(cl,obj);
	dat->comm_info=ci;
	dat->doc=doc;
	dat->docchg=doc;
	dat->bitmapobj=bitmapobj;
	dat->pageobj=pageobj;
	dat->zoomobj=zoomobj;
	dat->vgroup=vgroup;
	dat->searchobj=searchobj;
	dat->linkobj=linkobj;
	dat->outlineswin=NULL;
	dat->group=group;
	dat->group2=group2;
	dat->group3=group3;
	dat->rect=rect;
	dat->use_vgroup=TRUE;

	DoMethod(pageobj,MUIM_Notify,MYATTR_PageSlider_Value,MUIV_EveryTime,
		 bitmapobj,3,MUIM_NoNotifySet,MYATTR_DocBitmap_Page,MUIV_TriggerValue);
	DoMethod(bitmapobj,MUIM_Notify,MYATTR_DocBitmap_Page,MUIV_EveryTime,
		 pageobj,3,MUIM_NoNotifySet,MYATTR_PageSlider_Value,MUIV_TriggerValue);
	DoMethod(zoomobj,MUIM_Notify,MUIA_Cycle_Active,MUIV_EveryTime,
		 bitmapobj,3,MUIM_NoNotifySet,MYATTR_DocBitmap_Zoom,MUIV_TriggerValue);
	DoMethod(bitmapobj,MUIM_Notify,MYATTR_DocBitmap_Zoom,MUIV_EveryTime,
		 zoomobj,3,MUIM_NoNotifySet,MUIA_Cycle_Active,MUIV_TriggerValue);
	DoMethod(bitmapobj,MUIM_Notify,MYATTR_DocBitmap_Message,MUIV_EveryTime,
		 linkobj,3,MUIM_Set,MUIA_Text_Contents,MUIV_TriggerValue);
	DoMethod(bitmapobj,MUIM_Notify,MYATTR_DocBitmap_DocChanged,MUIV_EveryTime,
		 obj,3,MUIM_Set,MYATTR_DocGroup_Document,MUIV_TriggerValue);
	DoMethod(searchobj,MUIM_Notify,MUIA_String_Acknowledge,MUIV_EveryTime,
		 obj,1,MYM_DocGroup_Search);
	DoMethod(nxtsrchobj,MUIM_Notify,MUIA_Selected,FALSE,
		 obj,1,MYM_DocGroup_Search);
	DoMethod(backobj,MUIM_Notify,MUIA_Selected,FALSE,
		 bitmapobj,1,MYM_DocBitmap_Backward);
	DoMethod(fwdobj,MUIM_Notify,MUIA_Selected,FALSE,
		 bitmapobj,1,MYM_DocBitmap_Forward);
	DoMethod(bitmapobj,MUIM_Notify,MYATTR_DocBitmap_Run,MUIV_EveryTime,
		 obj,3,MUIM_Set,MYATTR_DocGroup_Run,MUIV_TriggerValue);
	DoMethod(bitmapobj,MUIM_Notify,MYATTR_DocBitmap_URL,MUIV_EveryTime,
		 obj,3,MUIM_Set,MYATTR_DocGroup_URL,MUIV_TriggerValue);
	DoMethod(bitmapobj,MUIM_Notify,MYATTR_DocBitmap_Open,MUIV_EveryTime,
		 obj,3,MUIM_Set,MYATTR_DocGroup_Open,MUIV_TriggerValue);
	DoMethod(bitmapobj,MUIM_Notify,MYATTR_DocBitmap_OpenNew,MUIV_EveryTime,
		 obj,3,MUIM_Set,MYATTR_DocGroup_OpenNew,MUIV_TriggerValue);
	DoMethod(bitmapobj,MUIM_Notify,MYATTR_DocBitmap_PageNew,MUIV_EveryTime,
		 obj,3,MUIM_Set,MYATTR_DocGroup_PageNew,MUIV_TriggerValue);
	DoMethod(bitmapobj,MUIM_Notify,MYATTR_DocBitmap_NeedVGroup,MUIV_EveryTime,
		 obj,2,MYM_DocGroup_UseVGroup,MUIV_TriggerValue);
    } else if(DoMethod(doc,MYM_Document_DecRef)==0)
	MUI_DisposeObject(doc);

    msg->ops_AttrList=(struct TagItem*)tags[3].ti_Data;
    return (ULONG)obj;
}

Static ULONG dgDispose(struct IClass *cl,Object *obj,Msg msg) {
    DocGroupData *dat=INST_DATA(cl,obj);
    if(dat->outlineswin) {
	Object *app;
	set(dat->outlineswin,MUIA_Window_Open,FALSE);
	get(dat->outlineswin,MUIA_ApplicationObject,&app);
	DoMethod(app,OM_REMMEMBER,dat->outlineswin);
	MUI_DisposeObject(dat->outlineswin);
    }
    if(dat->doc && DoMethod(dat->doc,MYM_Document_DecRef)==0)
	MUI_DisposeObject(dat->doc);
    return DoSuperMethodA(cl,obj,msg);
}

Static ULONG dgGet(struct IClass *cl,Object *obj,struct opGet *msg) {
    DocGroupData *dat=INST_DATA(cl,obj);
    switch(msg->opg_AttrID) {
      case MYATTR_DocGroup_Document:
	*(Object**)msg->opg_Storage=dat->doc;
	return TRUE;
      case MYATTR_DocGroup_DocChanged:
	*(Object**)msg->opg_Storage=dat->docchg;
	return TRUE;
      case MYATTR_DocGroup_Page:
	get(dat->bitmapobj,MYATTR_DocBitmap_Page,msg->opg_Storage);
	return TRUE;
      case MYATTR_DocGroup_Zoom:
	get(dat->bitmapobj,MYATTR_DocBitmap_Zoom,msg->opg_Storage);
	return TRUE;
      case MYATTR_DocGroup_Rotate:
	get(dat->bitmapobj,MYATTR_DocBitmap_Rotate,msg->opg_Storage);
	return TRUE;
      case MYATTR_DocGroup_CropBox:
	get(dat->bitmapobj,MYATTR_DocBitmap_CropBox,msg->opg_Storage);
	return TRUE;
      case MYATTR_DocGroup_TextAA:
	get(dat->bitmapobj,MYATTR_DocBitmap_TextAA,msg->opg_Storage);
	return TRUE;
      case MYATTR_DocGroup_StrokeAA:
	get(dat->bitmapobj,MYATTR_DocBitmap_StrokeAA,msg->opg_Storage);
	return TRUE;
      case MYATTR_DocGroup_FillAA:
	get(dat->bitmapobj,MYATTR_DocBitmap_FillAA,msg->opg_Storage);
	return TRUE;
      case MYATTR_DocGroup_Run:
      case MYATTR_DocGroup_URL:
      case MYATTR_DocGroup_Open:
      case MYATTR_DocGroup_OpenNew:
      case MYATTR_DocGroup_PageNew:
	*msg->opg_Storage=0;
	return TRUE;
    }
    return DoSuperMethodA(cl,obj,(Msg)msg);
}

Static ULONG dgSet(struct IClass *cl,Object *obj,struct opSet *msg) {
    DocGroupData *dat=INST_DATA(cl,obj);
    struct TagItem *tags=msg->ops_AttrList;
    struct TagItem *tag;
    while(tag=NextTagItem(&tags)) {
	switch(tag->ti_Tag) {
	  case MYATTR_DocGroup_DocChanged:
	    dat->docchg=(Object*)tag->ti_Data;
	    break;

	  case MYATTR_DocGroup_Page:
	    set(dat->bitmapobj,MYATTR_DocBitmap_Page,tag->ti_Data);
	    break;

	  case MYATTR_DocGroup_PageLabel: {
	    ULONG x;
	    set(dat->pageobj,MYATTR_PageSlider_Label,tag->ti_Data);
	    get(dat->pageobj,MYATTR_PageSlider_Value,&x);
	    set(dat->bitmapobj,MYATTR_DocBitmap_Page,x);
	    break;
	  }
	  case MYATTR_DocGroup_Zoom:
	    set(dat->bitmapobj,MYATTR_DocBitmap_Zoom,tag->ti_Data);
	    break;

	  case MYATTR_DocGroup_Rotate:
	    set(dat->bitmapobj,MYATTR_DocBitmap_Rotate,tag->ti_Data);
	    break;

	  case MYATTR_DocGroup_CropBox:
	    set(dat->bitmapobj,MYATTR_DocBitmap_CropBox,tag->ti_Data);
	    break;

	  case MYATTR_DocGroup_Document: {
	    Object *doc=(Object*)tag->ti_Data;
	    const char *name;
	    if(doc && doc!=dat->doc) {
		int n,page;
		Object *olddoc,*doc2;
		struct PDFDoc *pdfdoc;
#ifdef HAVE_MUI_LISTTREE_H
		if(dat->outlineswin) {
		    Object *app;
		    set(dat->outlineswin,MUIA_Window_Open,FALSE);
		    get(dat->outlineswin,MUIA_ApplicationObject,&app);
		    DoMethod(app,OM_REMMEMBER,dat->outlineswin);
		    MUI_DisposeObject(dat->outlineswin);
		    dat->outlineswin=NULL;
		}
#endif
		olddoc=dat->doc;
		dat->doc=doc;
		get(dat->bitmapobj,MYATTR_DocBitmap_Document,&doc2);
		get(doc,MYATTR_Document_NumPages,&n);
		get(doc,MYATTR_Document_PDFDoc,&pdfdoc);
		if(doc2==doc) {
		    get(dat->bitmapobj,MYATTR_DocBitmap_Page,&page);
		} else {
		    page=1;
		}
		SetAttrs(dat->pageobj,
			 MUIA_NoNotify,TRUE,
			 MYATTR_PageSlider_Document,doc,
			 MYATTR_PageSlider_Value,page,
			 TAG_END);
		if(doc2!=doc) {
		    page=GetTagData(MYATTR_DocGroup_Page,-1,msg->ops_AttrList);
		    SetAttrs(dat->bitmapobj,
			     MYATTR_DocBitmap_Document,doc,
			     page==-1?TAG_IGNORE:MYATTR_DocBitmap_Page,page,
			     TAG_END);
		}
		if(olddoc && DoMethod(olddoc,MYM_Document_DecRef)==0)
		    MUI_DisposeObject(olddoc);
		DoMethod(doc,MYM_Document_IncRef);
		set(obj,MYATTR_DocGroup_DocChanged,doc);
	    }
	    break ;
	  }
	  case MYATTR_DocGroup_TextAA:
	    set(dat->bitmapobj,MYATTR_DocBitmap_TextAA,tag->ti_Data);
	    break;

	  case MYATTR_DocGroup_StrokeAA:
	    set(dat->bitmapobj,MYATTR_DocBitmap_StrokeAA,tag->ti_Data);
	    break;

	  case MYATTR_DocGroup_FillAA:
	    set(dat->bitmapobj,MYATTR_DocBitmap_FillAA,tag->ti_Data);
	    break;
	}
    }
    return DoSuperMethodA(cl,obj,(Msg)msg);
}


Static ULONG dgSearch(struct IClass *cl,Object *obj) {
    DocGroupData* dat=INST_DATA(cl,obj);
    struct Rectangle rect;
    int pg,pages;
    int page,top,bottom,xmin,ymin,xmax,ymax;
    BOOL found=FALSE;
    const char *str;
    get(dat->searchobj,MUIA_String_Contents,&str);
    get(dat->bitmapobj,MYATTR_DocBitmap_Page,&page);
    get(dat->bitmapobj,MYATTR_DocBitmap_Selection,&rect);
    bottom=0;

    if(rect.MinX==-1)
	top=1;
    else {
	top=0;
	xmin=rect.MaxX;
	ymin=(rect.MinY+rect.MaxY)/2;
	xmax=0;
	ymax=0;
	if(DoMethod(dat->bitmapobj,MYM_DocBitmap_Search,str,top,&xmin,&ymin,&xmax,&ymax))
	    found=TRUE;
    }
    if(!found) {
	int k,l=0;
	struct List* winlist;
	Object* o;
	struct Node* state;
	ULONG sigs=0;
	struct PDFDoc *doc;
	Object *app,*gauge;
	get(dat->doc,MYATTR_Document_NumPages,&pages);
	get(dat->doc,MYATTR_Document_PDFDoc,&doc);
	get(obj,MUIA_ApplicationObject,&app);
	gauge=create_gauge(app,STR(MSG_SEARCHING),pages);
	pg=page;
	if(init_find(dat->comm_info,str)) {
	    LONG r=0;
	    for(k=pg+1;k<=pages;++k) {
		if(find(dat->comm_info,doc,k,k+1,&xmin,&ymin,&xmax,&ymax,&page)) {
		    found=TRUE;
		    break;
		}
		if(gauge) {
		    set(gauge,MUIA_Gauge_Current,++l);
		    r=DoMethod(app,MUIM_Application_NewInput,&sigs);
		    if(r==MUIV_Application_ReturnID_Quit ||
		       r==ID_ABORT)
			break;
		}
	    }
	    if(!found && r!=ID_ABORT && r!=MUIV_Application_ReturnID_Quit) {
		for(k=1;k<pg;++k) {
		    if(find(dat->comm_info,doc,k,k+1,&xmin,&ymin,&xmax,&ymax,&page)) {
			found=TRUE;
			break;
		    }
		    if(gauge) {
			set(gauge,MUIA_Gauge_Current,++l);
			r=DoMethod(app,MUIM_Application_NewInput,&sigs);
			if(r==MUIV_Application_ReturnID_Quit ||
			   r==ID_ABORT)
			    break;
		    }
		}
	    }
	    end_find(dat->comm_info);
	}
	if(found) {
	    set(dat->pageobj,MYATTR_PageSlider_Value,page);
	    DoMethod(dat->bitmapobj,MYM_DocBitmap_Search,str,TRUE,&xmin,&ymin,&xmax,&ymax);
	}
	delete_gauge(gauge);
    }
    if(found) {
	LONG left,width,top,height,t;
	rect.MinX=xmin;
	rect.MinY=ymin;
	rect.MaxX=xmax;
	rect.MaxY=ymax;
	set(dat->bitmapobj,MYATTR_DocBitmap_Selection,&rect);
       if(dat->use_vgroup) {
	xmin+=bleft;
	ymin+=btop;
	xmax+=bleft;
	ymax+=btop;
	get(dat->vgroup,MUIA_Width,&width);
	get(dat->vgroup,MUIA_InnerRight,&t);
	width-=t;
	get(dat->vgroup,MUIA_InnerLeft,&t);
	width-=t;
	get(dat->vgroup,MUIA_Height,&height);
	get(dat->vgroup,MUIA_InnerBottom,&t);
	height-=t;
	get(dat->vgroup,MUIA_InnerTop,&t);
	height-=t;
	get(dat->vgroup,MUIA_Virtgroup_Left,&left);
	get(dat->vgroup,MUIA_Virtgroup_Top,&top);
	if(xmin<left)
	    left=xmin;
	else if(xmax>left+width)
	    left=xmax-width;
	if(ymin<top)
	    top=ymin;
	else if(ymax>top+height)
	    top=ymax-height;
	SetAttrs(dat->vgroup,
		 MUIA_Virtgroup_Left,left,
		 MUIA_Virtgroup_Top,top,
		 TAG_END);
       }
    } else {
	set(dat->bitmapobj,MYATTR_DocBitmap_Selection,NULL);
	set(dat->linkobj,MUIA_Text_Contents,STR(MSG_NOT_FOUND));
    }
    return 0;
}

Static ULONG dgUseVGroup(struct IClass *cl,Object *obj,struct MYP_DocGroup_UseVGroup *msg) {
    DocGroupData *dat=INST_DATA(cl,obj);
    if(msg->state && !dat->use_vgroup) {
       DoMethod(dat->group2,MUIM_Group_InitChange);
       DoMethod(dat->group3,MUIM_Group_InitChange);
       DoMethod(dat->group3,OM_REMMEMBER,dat->bitmapobj);
       DoMethod(dat->group2,OM_REMMEMBER,dat->rect);
       DoMethod(dat->group3,OM_ADDMEMBER,dat->rect);
       DoMethod(dat->group2,OM_ADDMEMBER,dat->bitmapobj);
       set(dat->bitmapobj,MYATTR_DocBitmap_VGroup,dat->vgroup);
       set(dat->group,MUIA_Group_ActivePage,0);
       DoMethod(dat->group3,MUIM_Group_ExitChange);
       DoMethod(dat->group2,MUIM_Group_ExitChange);
    } else if(!msg->state && dat->use_vgroup) {
       DoMethod(dat->group2,MUIM_Group_InitChange);
       DoMethod(dat->group3,MUIM_Group_InitChange);
       DoMethod(dat->group2,OM_REMMEMBER,dat->bitmapobj);
       DoMethod(dat->group3,OM_REMMEMBER,dat->rect);
       DoMethod(dat->group2,OM_ADDMEMBER,dat->rect);
       DoMethod(dat->group3,OM_ADDMEMBER,dat->bitmapobj);
       set(dat->bitmapobj,MYATTR_DocBitmap_VGroup,NULL);
       set(dat->group,MUIA_Group_ActivePage,1);
       DoMethod(dat->group3,MUIM_Group_ExitChange);
       DoMethod(dat->group2,MUIM_Group_ExitChange);
    }
    dat->use_vgroup=msg->state;
    return 0;
}

#ifdef HAVE_MUI_LISTTREE_H
Static int dgInsertOutlines(Object *listtree,
			    struct MUIS_Listtree_TreeNode *tn,
			    const char **titles,
			    struct LinkAction **actions,
			    int level) {
    struct MUIS_Listtree_TreeNode *last=NULL;
    int n=0;
    while(*titles) {
	const char *p=*titles;
	int limit=last?level+1:level;
	int l=*p++-'a';
	if(l<level)
	    break;
	else if(l==level) {
	    ULONG f=0;
	    if(*p=='T' || *p=='t') {
		f=TNF_LIST;
		if(*p=='T')
		    f|=TNF_OPEN;
		++p;
	    }
	    ++p;
	    last=(struct MUIS_Listtree_TreeNode*)DoMethod(listtree,MUIM_Listtree_Insert,p,*actions,tn,MUIV_Listtree_Insert_PrevNode_Tail,f);
	    ++titles;
	    ++actions;
	    ++n;
	} else {
	    int k=dgInsertOutlines(listtree,last,titles,actions,level+1);
	    n+=k;
	    titles+=k;
	    actions+=k;
	}
    }
    return n;
}

Static ULONG dgOpenOutlines(struct IClass *cl,Object *obj) {
    DocGroupData *dat=INST_DATA(cl,obj);
    Object *win=dat->outlineswin;
    if(!win) {
	struct OutlinesInfo *outlines;
	get(dat->doc,MYATTR_Document_Outlines,&outlines);
	if(outlines) {
	    Object *listtree;
	    dat->outlineslv=ListviewObject,
		MUIA_Listview_List,listtree=ListtreeObject,
		    InputListFrame,
		    MUIA_Font,MUIV_Font_Fixed,
		    MUIA_Background,MUII_ListBack,
		    MUIA_Listtree_EmptyNodes,TRUE,
		    MUIA_Listtree_DuplicateNodeName,FALSE,
		    MUIA_Listtree_DoubleClick,MUIV_Listtree_DoubleClick_Off,
		    End,
		End;
	    win=dat->outlineswin=WindowObject,
		MUIA_Window_Title,STR(MSG_OUTLINES_WIN),
		MUIA_Window_ID,MAKE_ID('O','U','T','L'),
		MUIA_Window_RootObject,dat->outlineslv,
		MUIA_Window_DefaultObject,dat->outlineslv,
		End;
	    if(win) {
		Object* app;
		dgInsertOutlines(listtree,(struct MUIS_Listtree_TreeNode*)MUIV_Listtree_Insert_ListNode_Root,outlines->titles,outlines->actions,0);
		get(obj,MUIA_ApplicationObject,&app);
		DoMethod(app,OM_ADDMEMBER,win);
		DoMethod(win,MUIM_Notify,MUIA_Window_CloseRequest,TRUE,
			 win,3,MUIM_Set,MUIA_Window_Open,FALSE);
		DoMethod(dat->outlineslv,MUIM_Notify,MUIA_Listview_DoubleClick,TRUE,
			 obj,1,MYM_DocGroup_GoToOutline);
	    }
	}
    }
    if(dat->outlineswin)
	set(dat->outlineswin,MUIA_Window_Open,TRUE);
    return 0;
}

Static ULONG dgGoToOutline(struct IClass *cl,Object *obj) {
    DocGroupData *dat=INST_DATA(cl,obj);
    struct MUIS_Listtree_TreeNode *node;
    get(dat->outlineslv,MUIA_Listtree_Active,&node);
    if(node!=MUIV_Listtree_Active_Off)
	DoMethod(dat->bitmapobj,MYM_DocBitmap_Action,node->tn_User,0);
    return 0;
}
#endif

SAVEDS ASM ULONG dgDispatcher(REG(a0,struct IClass *cl),REG(a2,Object *obj),REG(a1,Msg msg)) {
    switch(msg->MethodID) {
      case OM_NEW:                    return dgNew    (cl,obj,(APTR)msg);
      case OM_DISPOSE:                return dgDispose(cl,obj,msg);
      case OM_GET:                    return dgGet    (cl,obj,(APTR)msg);
      case OM_SET:                    return dgSet    (cl,obj,(APTR)msg);
      case MYM_DocGroup_Search:       return dgSearch (cl,obj);
#ifdef HAVE_MUI_LISTTREE_H
      case MYM_DocGroup_OpenOutlines: return dgOpenOutlines(cl,obj);
      case MYM_DocGroup_GoToOutline:  return dgGoToOutline(cl,obj);
#endif
      case MYM_DocGroup_UseVGroup:    return dgUseVGroup(cl,obj,(APTR)msg);
    }
    return DoSuperMethodA(cl,obj,msg);
}


struct MUI_CustomClass *docgroup_mcc;
static int count;

BOOL createDocGroupClass(void) {
    if(count++==0) {
	if(createPageSliderClass()) {
	    if(docgroup_mcc=MUI_CreateCustomClass(NULL,MUIC_Group,NULL,sizeof(DocGroupData),dgDispatcher))
		return TRUE;
	    deletePageSliderClass();
	}
	count=0;
	return FALSE;
    }
    return TRUE;
}

BOOL deleteDocGroupClass(void) {
    if(count && --count==0) {
	MUI_DeleteCustomClass(docgroup_mcc);
	deletePageSliderClass();
    }
    return TRUE;
}

