ftp.nice.ch/pub/next/database/improv/Grouper.1.21.d.tar.gz#/Grouper1.21Pack/Grouper1.21/grouper.m

This is grouper.m in view mode; [Download] [Up]

/*               Grouper V1.21
Winfried Wille                                            Germany, Hamburg the 10/6/93
Automatic grouping for Lotus Improv - you need the Improv API Toolkit.
Created by Winfried Wille at the end of 1991.
This code is free, but you have to mention me, when you use this code or parts of it.
If you like the code and use it frequently, feel free to send me $15.
The module contains code from Lotus Development Corporation.
I have a lot of other stuff, SQL-Interface, sorting in categorys and some DB-Kit stuff in alpha-versions. But I currently  can't afford to invest time in unpayed work.
The programming project was stopped in March 1992, because my contacts with Lotus gave me a very bad feeling about the coming support from Improv on NeXT.
Thanks to the german company Xpand (registrated NeXTDeveloper), espically Michael Thun, who donated me a NeXTstep 3.0 license and give me some work in the macintosh area.

You can reach me as: wwille@wwille.hanse.de .
*/
#import <stdlib.h>
#import <strings.h>
#import <objc/objc-load.h>
#import <appkit/Application.h>
#import <appkit/Panel.h>
#import <appkit/Text.h>
#import <streams/streams.h>
#import <appkit/Pasteboard.h>
#import <appkit/publicWraps.h>
#import  <objc/Storage.h>
#import "grouper.h"
#import <imxMenuServer.h>
#import <imxPublicAPI.h>

//--------- Informationen fuer den Improv loader u. Menuserver ------------------------
// The Tools menu button is named
#define	MENUITEM		"grouper..."
// The command key for the menu button is assigned
#define HOTKEY			'8'
//  The checkOK procedure is from imxGeneric.m
//-----------------------------------------------------------------------------
//	Proc: checkOK
//-----------------------------------------------------------------------------
// Default error handling

imxErrorCode _checkOK(imxErrorCode result, int lineno)
{
	if (result != imxerr_OK) {
		char buff[256];
		const char *statstr = NULL;
		(void) imxGetErrorText(&statstr, result);
		sprintf(buff, "%s (%d): line %d of file \"%s\"", statstr, result,
				lineno, __FILE__);
		NXRunAlertPanel("Improv API Error", buff, "OK", NULL, NULL);
	}
	return result;
}

//---------------- Hilfsroutinen ------------------------------------------------------

int catCopy(char *cat, const char *s)
{
  char * catl;
  catl=cat;
    for(;*s;s++)
    {
	switch (*s)
	{
	    case ':' : *cat++='_'; break;
	    case '\t' : *cat++='_'; break;
	    case 39 : *cat++=39;*cat++=39; break;
	    default:    *cat++=*s;
	}
    }
    *cat=0;
    return (int)(catl-cat);
}
//-------------------------------------------------------
char *strdup(str) /* nicht im ANSI-Standard enthalten */
	char *str;
{ char *s;
  s=(char *)malloc((size_t)strlen(str)+1);
  strcpy(s, str);
  return s;
}

//----------------------
@implementation grouper
- (imxCellType)  getCellVal: (int) index1 Index2: (int) index2  
			     Zone1: (imxDPZone) zone1 Zone2: (imxDPZone) zone2
			     Save: (BOOL) save ForCat: (BOOL) forCat	
{
    sprintf(buff,"%s[%d]:%s[%d]",   cornerSel1save.category[zone1][0], index1,
				    cornerSel1save.category[zone2][0], index2 );
    return [self getCellVal: buff Save: save ForCat: forCat];
}			     	     

- (imxCellType) getCellVal: (int) row Col: (int) col Save: (BOOL) save ForCat: (BOOL) forCat
{
    sprintf(buff,"%s[%d]:%s[%d]",   cornerSel1save.category[zone_row][0], row,
				    cornerSel1save.category[zone_column][0], col);
    return [self getCellVal: buff Save: save ForCat: forCat];
}

- (imxCellType) getCellVal: (char *) s 
{
    return [self getCellVal: s Save: YES ForCat: NO];
}

- (imxCellType) getCellVal: (char *) s Save: (BOOL) save ForCat: (BOOL) forCat
{
    checkOK(imxGetCellValue(s, &cellType, &cellNVal, 
	&cellSval, sheetNamePSave, modelNamePSave));
    switch (cellType)
    {
	    case cval_TEXT: 
			 if(forCat) catCopy(cellSvalZwi, cellSval); 
			 else strcpy(cellSvalZwi, cellSval);
			 cellNVal = 0.0;
			 cellSval=cellSvalZwi;
			 break;
			
	case cval_EMPTY: cellSvalZwi[0]=0; 
	                 cellSval=cellSvalZwi; cellNVal = 0.0; break;
	case cval_NA:    strcpy(cellSvalZwi, "NA"); 
	                 cellSval=cellSvalZwi; cellNVal = 0.0; break;
	case cval_ERR:   strcpy(cellSvalZwi, "ERR"); 
	                 cellSval=cellSvalZwi; cellNVal = 0.0; break;
	
	case cval_NUMBER:  cellSval=cellSvalZwi; sprintf(cellSvalZwi,"%.0f", cellNVal);
	                  break;
	default :         printf("Error:\tgetCellVal unknown Celltype\n");
    }
    printf("getCellVal: type: %d sVal: %s NVal: %f\n", cellType, cellSval, cellNVal);
    if(save)
    {
	strcpy(cellSvalSave, cellSval);
	cellNVal=cellNValSave;
	cellNValSave=cellNVal;
    }
    return cellType;
}    
//---- komfortableres setzen  von Cellwerten ---------------
- (imxErrorCode) setCellVal: (char *) primname Value: (char *) value
{
    return imxSetCellValue(primname, cval_TEXT, 0.0, 
	                           value, sheetNamePSave, modelNamePSave);				   
}


- (void) printSelInfo
{
    printf("\nSelinfo\n cellNamePSave: %s\n selType: %d\n viewNamePSave: %s\n"
           " sheetNamePSave: %s modelNamePSave: %s\n",
	   cellNamePSave, selType, viewNamePSave, sheetNamePSave, modelNamePSave);
}


- (imxErrorCode) setSel: (int) index sT: (imxSelType) seltype Zone: (int) zone
{
    sprintf(buff,"%s[%d]", cornerSel1save.category[zone][0], index);
    return [self setSel: buff sT: seltype];
}

- (imxErrorCode) setSel: (int) row Col: (int) col 
{
    sprintf(buff,"%s[%d]:%s[%d]", cornerSel1save.category[zone_row][0], row,
				    cornerSel1save.category[zone_column][0], col);
    return [self setSel: buff sT: imxsel_RANGE];
}
    
   
- (imxErrorCode) setSel: (char *) buffT sT: (imxSelType) seltype
{
    int i;
    imxActivateApp(YES);      
    i=imxSetSelection(buffT, seltype, 
	viewNamePSave, sheetNamePSave, modelNamePSave);    
    // if(i!=imxerr_SELECTION_MODIFIER)
    // _checkOK(i, __LINE__);
    return i;
        
}
- (imxErrorCode) setSel:  (imxSelType) st buff: (const char *) fmtstr, ...
{
    va_list ap;
    char mybuff[256];
    
    va_start(ap, fmtstr);
    vsprintf(mybuff, fmtstr, ap);
    return [self setSel: mybuff sT: st];
}


//--- Objektzustand aktualisieren/zuruecksetzen ----------------
- updateSelInfo
{
    if(imxerr_OK==imxGetSelection(&cellNameP, &selType, &viewNameP,
		    &sheetNameP, &modelNameP))
	{
		strcpy(cellNamePSave, cellNameP);
		strcpy(viewNamePSave, viewNameP);
		strcpy(sheetNamePSave,  sheetNameP);
		strcpy(modelNamePSave, modelNameP);
	}else
    {
		strcpy(cellNamePSave, "");
		strcpy(viewNamePSave, "");
		strcpy(sheetNamePSave,  "");
		strcpy(modelNamePSave, "");
	}	
    return self;
}
- (void) updateSelRange;
{
    int                 i, j;

    if(cornerSel1save.category[zone_row][0] != NULL 
       || cornerSel1save.category[zone_column][0] != NULL) 
    {
	for (i = 0; i < MAXDPZONES && cornerSel1save.category[i][0]; i++) 
	{
	    for (j = 0; j < MAXCATSPERDPZONE && cornerSel1save.category[i][j]; j++) 
	    {
		free(cornerSel1save.category[i][j]);cornerSel1save.category[i][j]=NULL;
	    }
	}
    }
    cornerSel1=NULL;    
    checkOK(imxGetSelRangeInfo(&cornerSel1, &cornerSel2));
    cornerSel1save=*cornerSel1;
    cornerSel2save=*cornerSel2;
    if (cornerSel1!=NULL) 
    {
	for (i = 0; i < MAXDPZONES && cornerSel1save.category[i][0]; i++) 
	{
	    for (j = 0; j < MAXCATSPERDPZONE && cornerSel1save.category[i][j]; j++) 
	    {
		cornerSel2save.category[i][j]=
		   cornerSel1save.category[i][j]=strdup(cornerSel1save.category[i][j]);
	    }
	}
    }
}

- (imxErrorCode) restoreSelInfo
{
    return [self setSel: cellNamePSave sT:selType];
}  
//--------------------------------------------------------
static id onlyInstance = NULL; // a'la Examples


// The following 6 methodes are unchanged from imxGeneric
//-----------------------------------------------------------------------------
//	Proc: finishLoading:
//-----------------------------------------------------------------------------

+ finishLoading:(struct mach_header *)header
{
	printf("FinishLoading class: %s\n", [self name]);
	onlyInstance = [[self alloc] initHeader:header];
	return self;
}

//-----------------------------------------------------------------------------
//	Proc: new
//-----------------------------------------------------------------------------
+ new
{
	if (onlyInstance)
		return onlyInstance;

  // This should never really happen since the only instance of the class
  // is allocated when the class is loaded in finishLoading:. 
	onlyInstance = [[self alloc] initHeader:NULL];

	return self;
}

//-----------------------------------------------------------------------------
//	Proc: initHeader:
//-----------------------------------------------------------------------------
- initHeader:(struct mach_header *)header
{
	classHeader = header;

  // install menuItem
	menuTag = [[imxMenuServer new] addMenuItem:MENUITEM target:self
				action:@selector(menuSelect:) key:HOTKEY update:YES];
	
	printf("[%s initHeader:0x%x] => 0x%x\n", [self name], classHeader, self);
	printf("[%s menuTag] => %d\n", [self name], menuTag);

	return self;	
}
//-----------------------------------------------------------------------------
//	Proc: menuItemUpdate:
//-----------------------------------------------------------------------------
- (BOOL)menuItemUpdate:menuCell
{
	BOOL enabled = YES;

  // Determine whether menuCell SHOULD be enabled.
  // Put YOUR code HERE to determine desired "enabled" state...
  
  // Compare desired state with existing state and
  // ask for update if necessary by returning YES.
	if (enabled != [menuCell isEnabled]) {
		[menuCell setEnabled:enabled];
		return YES;
	}

	return NO;
}

//-----------------------------------------------------------------------------
//	Proc: menuSelect:
//-----------------------------------------------------------------------------

- menuSelect:sender
{

	if (!nibPanel) {
		char sname[128];
		sprintf(sname, "%s.nib", [self name]);

		[NXApp loadNibSection:sname owner:self withNames:YES
				fromHeader:classHeader];
	}
	if (nibPanel) {
		[nibPanel makeKeyAndOrderFront:self];	// Modeless panel...
		//[NXApp runModalFor:nibPanel];			// or Modal panel...
	} else
		;				// Put simple operations here...

	return self;
}

//==================================================================================



- groupBy:sender
{
    
    char lastGroupValue[256];
    int i,j; // Iteratoren ueber den Range
    int groupStart;
    BOOL hasAutoGroup=NO;
    
	char *parentName=NULL,  parentNameSave[256];
	char *child1 = NULL;
	char *childN = NULL; 
   int fmNum=1;
   BOOL hasOverlap=NO; 
   //-------------
   imxDPZone iterationZone;
   imxDPZone groupZone;
   int  groupcol;
   
    // Selektion pruefen
	if(imxerr_OK != imxCmdEnabled(imxcmd_COPYASGRAPH))
    {
	    NXRunAlertPanel("Grouper", "You must select items and one row or one column",
		    "OK", NULL, NULL);
	    return nil;
    } 	
	
    [self updateSelInfo];
    [self updateSelRange];  
      
   //-- Iteratator holen ---
    if( cornerSel1save.index[zone_row][0] ==   cornerSel2save.index[zone_row][0])
    {
	iterationZone=zone_column ; groupZone=zone_row; groupcol=cornerSel1save.index[zone_row][0];
    }
    else if( cornerSel1save.index[zone_column][0] ==   cornerSel2save.index[zone_column][0])
    {
	iterationZone=zone_row; groupZone=zone_column; groupcol=cornerSel1save.index[zone_column][0];
    }  
    else
    {
	    NXRunAlertPanel("Grouper", "You must select items and one row or one column",
		    "317", NULL, NULL);
	    return nil;
    } 
    
     // automatische Group Funktion ?   
    //------------------------------ 
    [self setSel: cornerSel1save.index[iterationZone][0] sT: imxsel_ITEM Zone: iterationZone];
    *parentNameSave=0;	  
    checkOK(imxGetItemInfo(&parentName, &child1, &childN, "", "", ""));
    if(parentName) strcpy(parentNameSave, parentName);
    checkOK(imxCmdExec(imxcmd_GROUPITEMS));
    checkOK(imxGetItemInfo(&parentName, &child1, &childN, "", "", ""));
    if(!*parentNameSave && parentName==NULL) hasAutoGroup=NO;
    else if(!*parentNameSave && parentName!=NULL) hasAutoGroup=YES;
    else if(!strcmp(parentNameSave, parentName))  hasAutoGroup=NO;
    else hasAutoGroup=YES; 
    
    // Overlap !?
    //-----------
    if(hasAutoGroup)	
    {   imxFMState fmstatP;
    
	strcpy(parentNameSave, parentName);	
	
	for(fmNum=1; !imxGetFormulaStatus(&fmstatP, fmNum,
		    sheetNamePSave, modelNamePSave); fmNum++); // beschleunigen !?
	checkOK(imxGetFormulaStatus(&fmstatP, --fmNum,
		    sheetNamePSave, modelNamePSave));
	if(fmstat_OVERLAP == fmstatP) hasOverlap=YES; 	    
	else hasOverlap=NO;
	checkOK(imxCmdExec(imxcmd_DELETE));
        [self setSel: parentNameSave sT: imxsel_ITEM];
	// Aufrauemen		
	checkOK(imxCmdExec(imxcmd_UNGROUPITEMS));
        [self setSel: imxsel_FORMULA buff: "%d", fmNum];
	checkOK(imxCmdExec(imxcmd_DELETE));	
	fmNum--;	    				    
    }
    else // Aufrauemen
	checkOK(imxCmdExec(imxcmd_UNGROUPITEMS)); 
	
	
    //---  groupen   ---  
	
    for(i=groupStart=cornerSel1save.index[iterationZone][0],
           j=cornerSel2save.index[iterationZone][0],
           [self getCellVal: i Index2: groupcol 
		Zone1: iterationZone Zone2: groupZone Save: NO ForCat: YES],
	   strcpy(lastGroupValue, cellSval); 
        i<=j; i++)
    {
           [self getCellVal: i Index2: groupcol 
		Zone1: iterationZone Zone2: groupZone Save: NO ForCat: YES];
	if(strcmp(lastGroupValue, cellSval) || i==j)
	{
	    if(i-groupStart > 1)
	    {     
	        strcpy(cellSvalSave,  cellSval); 
		[self setSel: imxsel_ITEM buff: "%s[%d]..%s[%d]", 
		    cornerSel1save.category[iterationZone][0], groupStart, 
		    cornerSel1save.category[iterationZone][0], (i==j)?j:i-1];
		if(imxerr_OK == imxCmdEnabled(imxcmd_GROUPITEMS))
		{
		    checkOK(imxCmdExec(imxcmd_GROUPITEMS));
		    if(hasAutoGroup)
		    {
			checkOK(imxGetItemInfo(&parentName, &child1, &childN, "", "", ""));
			strcpy(parentNameSave, parentName);	
			if(hasOverlap)
			{   ++fmNum;
			    [self setSel: imxsel_FORMULA buff: "%d", fmNum]; 			
			    checkOK(imxResolveOverlap(1, 0,sheetNamePSave, modelNamePSave));
			}  
			[self setSel: parentNameSave sT: imxsel_ITEM];
			i++; j++;		    		
		    }
		    // Gruppennamen setzen
		    checkOK(imxRename(lastGroupValue, imxsel_ITEM, NULL, NULL, NULL)); 
		}
		strcpy(lastGroupValue, cellSvalSave);
	    }
	    else strcpy(lastGroupValue, cellSval);
	    groupStart=i;
	}
    }
    return self;
}    


//---------------------------------------------------------
- unGroup:sender
{
    int i,j; 
	char *parentNameP = NULL, parentNameSave[256];
	char *child1P = NULL, *childNP = NULL, childNSave[256];
   char *sumItemNameP;
   char groupNameSave[256];
   char *itemListP, **pNameVecP;
	Storage *FormularStates;
	
   
   
    //--------  Selektion pruefen ------------
    if(imxerr_OK != imxCmdEnabled(imxcmd_UNGROUPITEMS))
    {
	    NXRunAlertPanel("UnGrouper", "You cannot ungroup this selection", "OK", NULL, NULL);
	    return nil;
    }	
    
   //------ Initialiserungen ----------
   FormularStates= [[Storage alloc] initCount: 0
					elementSize:(unsigned)sizeof(int)
					description: "i"];
    
    //--- View Daten holen ---
   [self updateSelInfo];   
   //-- Formular Status holen --
   {   imxFMState fmstat;
       int fmNum;
       for(fmNum=1; !imxGetFormulaStatus(&fmstat, fmNum,
		    sheetNamePSave, modelNamePSave); fmNum++)
       {
	    [FormularStates addElement: &fmstat];
       }
   }   
   //-- Iteratator holen ---
   imxGetItemRangeInfo(&parentNameP, &itemListP,
	    cellNamePSave, sheetNamePSave, modelNamePSave) ;
   strcpy(parentNameSave, parentNameP);  
   itemListP=strdup(itemListP);
    //---  MainLoop  ---  
    for(j=iimxSplitNames(itemListP, &pNameVecP), i=0; i < j; i++)
    {
	[self setSel: imxsel_ITEM buff: "%s.%s", parentNameSave, pNameVecP[i]];
	imxGetItemInfo(&parentNameP, &child1P, &childNP, "", "", ""); 
	if(childNP)
	{
	    strcpy(childNSave, childNP);	  
	    sprintf(groupNameSave, "%s.%s", parentNameSave, pNameVecP[i]);  
	    imxGetSummaryItem(&sumItemNameP,  groupNameSave, sheetNamePSave, modelNamePSave);
	    if(sumItemNameP)
	    {
		[self setSel: imxsel_ITEM buff: "%s.%s.%s", parentNameSave, pNameVecP[i], childNSave];
		imxCmdExec(imxcmd_DELETE);
	    }
	}
    }
    [self restoreSelInfo ];
    imxCmdExec(imxcmd_UNGROUPITEMS); 
    //--- Formulare die vorher OK waren u. jetzt den Status ERROR haben loeschen -------
   {   imxFMState fmstat, lastFmstat ;
       for(i=[FormularStates count]; i > 0; i--)
       {
	    lastFmstat=*((imxFMState *)[FormularStates elementAt: i-1]);
	    if( ! lastFmstat)
	    {
	         imxGetFormulaStatus(&fmstat, i, sheetNamePSave, modelNamePSave); 
		 if(lastFmstat !=  fmstat && fmstat == fmstat_ERROR)
		 {
		    [self setSel: imxsel_FORMULA buff: "%d", i]; 
		    imxCmdExec(imxcmd_DELETE);
		 }
	    }
	}
    }
    free(itemListP);  
    [FormularStates free];
    return self;
}    

@end

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.