Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
PDlog Expand.c
/* |
File: PDlog Expand.c |
Contains: This example shows how to add items to print dialogs. ItÕs essentially the code |
in Tech Note PR09, only in C. Note that in v1.0.2, a bug was corrected which is |
also in the Tech Note. |
NOTE: Apple reserves the top half of the screen (where the current DITL |
items are located). Applications may use the bottom half of the screen to |
add items, but should not change any items in the top half of the screen. |
An application should expand the print dialogs only as much as is |
absolutely necessary. |
A global search and replace of 'Job' with 'Stl' will produce code that |
modifies the style dialog. |
¥ Another NOTE: As of System 7.5.3, neither our Append_DITL nor the system version |
handles appending of 'ictb' or 'dctb's correctly. This is a bug that's planned to be |
fixed in the future, but I'm warning you about it here. <davep> |
Written by: Dave Hersey |
Copyright: Copyright © 1991-1999 by Apple Computer, Inc., All Rights Reserved. |
You may incorporate this Apple sample source code into your program(s) without |
restriction. This Apple sample source code has been provided "AS IS" and the |
responsibility for its operation is yours. You are not permitted to redistribute |
this Apple sample source code as "Apple sample source code" after having made |
changes. If you're going to re-distribute the source, we require that you make |
it clear in the source that the code was descended from Apple sample source |
code, but that you've made changes. |
Change History (most recent first): |
3/10/2000 DavidH Removed a number of possible memory leaks in error handling code. |
7/26/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1 |
9/3/96 davep Change list handling to match recommendations of |
LW8 engineering. |
8/21/96 mxm Added the list for a sample |
6/10/96 davep Use the OS's AppendDITL if it's there. Add balloon help. |
11/16/95 davep updated to use CallPItemProc() and PItemUPP. Make it fully |
ANSI. Explicitly include needed headers, so back-port to |
MPW is easier. |
6/20/95 njvt updated for Universal Headers and MWC-PPC |
9/29/93 dmh fixed bug in Append_DITL (also in TN #95) which appends 2 extra bytes. |
2/25/92 dmh fixed lack of print handle tossing and added something to print. |
*/ |
#include <Printing.h> |
#include <Dialogs.h> |
#include <Gestalt.h> |
#include <MixedMode.h> |
#include <SegLoad.h> |
#include <Resources.h> |
#include <ToolUtils.h> |
#include <Fonts.h> |
#include <Lists.h> |
static TPPrDlg PrtJobDialog; /* pointer to job dialog */ |
/* |
This points to the following structure: |
struct TPrDlg { |
DialogRecord Dlg; // The Dialog window |
ModalFilterUPP pFltrProc; // The Filter Proc. |
PItemUPP pItemProc; // The Item evaluating proc. |
THPrint hPrintUsr; // The user's print record. |
Boolean fDoIt; |
Boolean fDone; |
long lUser1; // Four longs for apps to hang global data. |
long lUser2; // Plus more stuff needed by the particular |
long lUser3; // printing dialog. |
long lUser4; |
}; |
typedef struct TPrDlg TPrDlg; |
typedef TPrDlg *TPPrDlg; |
typedef TPPrDlg TPPrDlgRef; |
*/ |
/* Declare ÔpascalÕ functions and procedures */ |
extern short Append_DITL(TPPrDlg, short); /* Our AppendDITL function. */ |
extern pascal TPPrDlg MyJobDlgInit(THPrint); /* Our extention to PrJobInit. */ |
extern pascal void MyJobItems(TPPrDlg, short); /* Our modal item handler. */ |
extern OSErr Print(void); |
pascal void MyListDraw(WindowPtr theWindow, short itemNo); //mxm |
#define MyDITL 256 /* resource ID of my DITL to be spliced onto job dialog */ |
#define kPanePopUp (6) /* mxm */ |
ListHandle gList = nil; /* mxm */ |
UserItemUPP myDrawListUPP = nil; /* mxm */ |
THPrint hPrintRec; /* handle to print record */ |
long prFirstItem; /* save our first item here */ |
PItemUPP prPItemProc; /* we need to store the old itemProc here */ |
/*-----------------------------------------------------------------------*/ |
WindowPtr MyWindow; |
OSErr err; |
Str255 myStr; |
#if PRAGMA_ALIGN_SUPPORTED |
#pragma options align=mac68k |
#endif |
typedef struct dialog_item_struct { |
Handle handle; /* handle or procedure pointer for this item */ |
Rect bounds; /* display rectangle for this item */ |
char type; /* item type - 1 */ |
char data[1]; /* length byte of data */ |
} DialogItem, *DialogItemPtr, **DialogItemHandle; |
typedef struct append_item_list_struct { |
short max_index; /* number of items - 1 */ |
DialogItem items[1]; /* first item in the array */ |
} ItemList, *ItemListPtr, **ItemListHandle; |
typedef union signed_byte_union { |
short integer; |
char bytes[2]; |
} ByteAccess; |
static Boolean append_exits(void); |
static Boolean append_exits(void) |
{ |
long response; |
if (Gestalt(gestaltDITLExtAttr,&response) == noErr) { |
return !!(response & (1 << gestaltDITLExtPresent)); |
} else { |
return false; /* simple-minded, but hey, so am I. -DaveP */ |
} |
} |
/* This routine appends all of the items of a specified DITL */ |
/* onto the end of a specified DLOG Ñ We donÕt even need to know the format */ |
/* of the DLOG */ |
/* this will be done in 3 steps: */ |
/* 1. append the items of the specified DITL onto the existing DLOG */ |
/* 2. expand the original dialog window as required */ |
/* 3. return the adjusted number of the first new user item */ |
short Append_DITL(TPPrDlg dialog, short item_list_ID) |
{ |
ItemListHandle append_item_list; /* handle to DITL being appended */ |
ItemListHandle dlg_item_list; /* handle to DLOG's item list */ |
short first_item; |
dlg_item_list = (ItemListHandle)((DialogPeek)dialog)->items; |
first_item = (**dlg_item_list).max_index + 2; |
append_item_list = (ItemListHandle)GetResource('DITL', item_list_ID); |
if ( append_item_list == NULL ) { |
DebugStr("\pError loading DITL resource"); |
return first_item; |
} |
if (append_exits()) { |
AppendDITL((DialogPtr)dialog, (Handle) append_item_list, appendDITLBottom); |
/* Make sure to release the DITL resource before returning */ |
ReleaseResource( (Handle)append_item_list ); |
return first_item; |
} else { |
Point offset; |
Rect max_rect; |
DialogItemPtr item; /* pointer to item being appended */ |
short new_items, data_size, i; |
ByteAccess usb; |
OSErr err; |
/* |
Using the original DLOG |
1. Remember the original window Size. |
2. Set the offset Point to be the bottom of the original window. |
3. Subtract 5 pixels from bottom and right, to be added |
back later after we have possibly expanded window. |
4. Get working Handle to original item list. |
5. Calculate our first item number to be returned to caller. |
6. Get locked Handle to DITL to be appended. |
7. Calculate count of new items. |
*/ |
if (dialog == NULL) ExitToShell(); |
max_rect = ((DialogPeek)dialog)->window.port.portRect; |
offset.v = max_rect.bottom; |
offset.h = 0; |
max_rect.bottom -= 5; |
max_rect.right -= 5; |
HLock((Handle)append_item_list); |
new_items = (**append_item_list).max_index + 1; |
/* |
For each item, |
1. Offset the rectangle to follow the original window. |
2. Make the original window larger if necessary. |
3. fill in item handle according to type. |
*/ |
item = (**append_item_list).items; |
for ( i = 0; i < new_items; i++ ) |
{ |
OffsetRect(&item->bounds, offset.h, offset.v); |
UnionRect(&item->bounds, &max_rect, &max_rect); |
usb.integer = 0; |
usb.bytes[1] = item->data[0]; |
switch ( item->type & 0x7F ) |
{ |
case ctrlItem + btnCtrl : |
case ctrlItem + chkCtrl : |
case ctrlItem + radCtrl : |
item->handle = (Handle)NewControl((DialogPtr) dialog, |
&item->bounds, (StringPtr)item->data, true, |
0, 0, 1, item->type & 0x03, 0); |
break; |
case ctrlItem + resCtrl : |
{ |
item->handle = (Handle)GetNewControl(*(short*)(item->data + 1), (DialogPtr) dialog); |
(**(ControlHandle)item->handle).contrlRect = item->bounds; |
} |
break; |
case statText : |
case editText : |
err = PtrToHand(item->data + 1, &item->handle, usb.integer); |
break; |
case iconItem : |
item->handle = GetIcon(*(short*)(item->data + 1)); |
break; |
case picItem : |
item->handle = (Handle)GetPicture(*(short*)(item->data + 1)); |
break; |
default : |
item->handle = NULL; |
} |
data_size = (usb.integer + 1) & 0xFFFE; |
item = (DialogItemPtr)((char*)item + data_size + sizeof(DialogItem)); |
} |
/* We need to subtract the short below because otherwise the size of the DITL count |
** gets factored in twice, and the resulting DTIL has two bytes of garbage appended |
** to it. This is a problem with the original TN#95 code as well. |
*/ |
err = PtrAndHand((**append_item_list).items, |
(Handle)dlg_item_list, |
GetHandleSize((Handle) append_item_list) |
- sizeof(short)); |
(**dlg_item_list).max_index += new_items; |
HUnlock((Handle) append_item_list); |
ReleaseResource((Handle) append_item_list); |
max_rect.bottom += 5; |
max_rect.right += 5; |
SizeWindow((WindowPtr) dialog, max_rect.right, max_rect.bottom, true); |
return first_item; |
} |
} |
/*------------------------------------------------------------------------*/ |
OSErr Print(void) |
{ |
TPPrPort pPrPort; |
Rect aRect; |
TPrStatus theStatus; |
Boolean printingShouldNotProceed; |
PDlgInitUPP/*PDlgInitProcPtr*/ theInitProcPtr; |
/* Allocate a print record */ |
hPrintRec = (THPrint)(NewHandle(sizeof(TPrint))); |
if ( NULL == hPrintRec ) |
{ |
return iMemFullErr; |
} |
/* Fill in the default values */ |
PrintDefault(hPrintRec); |
/* Make sure the print record is valid and consistent */ |
PrValidate(hPrintRec); |
if (PrError() != noErr) |
{ |
DisposeHandle( (Handle)hPrintRec ); |
return PrError(); |
} |
/* call PrJobInit to get pointer to the invisible job dialog */ |
PrtJobDialog = PrJobInit(hPrintRec); |
if (PrError() != noErr) |
{ |
DisposeHandle( (Handle)hPrintRec ); |
return PrError(); |
} |
/* Create a UPP for the dialog init proc */ |
theInitProcPtr = NewPDlgInitProc(MyJobDlgInit); |
if ( NULL == theInitProcPtr ) |
{ |
DisposeHandle( (Handle)hPrintRec ); |
return iMemFullErr; |
} |
/* this line does all the stuff */ |
printingShouldNotProceed = !PrDlgMain(hPrintRec, theInitProcPtr); |
/* Get rid of our user item draw proc mxm */ |
/* Allocated within MyJobDlgInit() that was called by PrDlgMain() */ |
if (myDrawListUPP) |
{ |
DisposeRoutineDescriptor(myDrawListUPP); |
} |
/* Make sure to free the InitProc UPP */ |
DisposeRoutineDescriptor(theInitProcPtr); |
if (printingShouldNotProceed) |
{ |
DisposeHandle( (Handle)hPrintRec ); |
return iPrAbort; |
} |
if (PrError() != noErr) |
{ |
DisposeHandle( (Handle)hPrintRec ); |
return PrError(); |
} |
/* Open the printing document and page to prepare for drawing |
** into the printing context |
*/ |
pPrPort = PrOpenDoc(hPrintRec, NULL, NULL); |
PrOpenPage(pPrPort, NULL); |
/* Draw something on the page */ |
aRect = (*hPrintRec)->prInfo.rPage; |
InsetRect(&aRect, 20, 20); |
PenSize(4, 4); |
FrameRect(&aRect); |
/* We're done drawing so close up the page and document */ |
PrClosePage(pPrPort); |
PrCloseDoc(pPrPort); |
/* Do we need to spool the job? */ |
if (!PrError() && (*hPrintRec)->prJob.bJDocLoop == bSpoolLoop) |
PrPicFile(hPrintRec, NULL, NULL, NULL, &theStatus); |
/* Don't forget to release the print record memory */ |
DisposeHandle((Handle) hPrintRec); |
return(noErr); |
} /* Print */ |
//----------------------------------------------------------------------------- |
pascal void MyListDraw(WindowPtr theWindow, short itemNo) |
//----------------------------------------------------------------------------- |
{ |
#pragma unused (theWindow,itemNo) |
short itemType; |
Handle itemH; |
Rect itemBox,r; |
Point pt; |
ControlRef cr; |
RgnHandle clip = NewRgn(); |
if (itemNo == kPanePopUp) { |
cr = gList[0]->vScroll; |
HideControl(cr); |
return; |
} |
GetDialogItem((DialogPtr) PrtJobDialog,itemNo,&itemType,&itemH,&itemBox); |
r = theWindow->portRect; |
pt.h = itemBox.left; |
pt.v = itemBox.top; |
cr = gList[0]->vScroll; |
MoveControl(cr,itemBox.right-15,itemBox.top-1); |
ShowControl(cr); |
if (PtInRect(pt,&r)) { |
gList[0]->rView = itemBox; |
gList[0]->rView.right -=15; /* inset for scroll bar */ |
ShowControl(cr); |
LSetDrawingMode(true,gList); |
if (clip) { |
RectRgn(clip,&itemBox); |
LUpdate(clip,gList); |
InsetRect(&itemBox,-1,-1); |
FrameRect(&itemBox); |
} |
} |
/* Free up the region memory */ |
DisposeRgn(clip); |
} |
/*------------------------------------------------------------------------*/ |
pascal TPPrDlg MyJobDlgInit(THPrint hPrint) |
/* |
this routine appends items to the standard job dialog and sets up the |
user fields of the printing dialog record TPRDlg |
This routine will be called by PrDlgMain |
*/ |
{ |
#pragma unused(hPrint) |
short firstItem; /* first new item number */ |
short itemType, item; |
Handle itemH; |
Rect itemBox; |
firstItem = Append_DITL(PrtJobDialog, MyDITL); /* call routine to do this */ |
prFirstItem = firstItem; /* save this so MyJobItems can find it */ |
/* now we'll set up our DITL items -- The radio buttons */ |
for (item = 5; item <= 7; item++) |
{ |
GetDialogItem((DialogPtr) PrtJobDialog,firstItem + item -1,&itemType,&itemH,&itemBox); |
SetControlValue((ControlHandle) itemH, (item == 5)); |
} |
/* now we'll set up the second of our DITL items -- The checkbox */ |
GetDialogItem((DialogPtr) PrtJobDialog,firstItem +2,&itemType,&itemH,&itemBox); |
SetControlValue((ControlHandle) itemH,1); |
// here is the list stuff (mxm) |
{ |
Rect bounds ; |
Point theCell = {16,200}; |
GetDialogItem((DialogPtr) PrtJobDialog,firstItem +8,&itemType,&itemH,&itemBox); |
SetRect(&bounds,0,0,1,0); |
itemBox.right -=15; |
gList = LNew(&itemBox,&bounds,theCell,0,(GrafPtr)PrtJobDialog,false,false,false,true); |
if (gList) { // this is how you need to do the scroll-bar so it'll |
// move off correctly with LaserWriter 8.4 and later |
// |
// The theory is that we have another user item which |
// has the handle for the control item for the scrollbar |
// for the list. This way, the scroll bar gets redrawn |
// in the right order, so it realises it's off the panel |
// when it is. If you don't have this code, the scroller |
// gets drawn, then the list gets moved, so you get a |
// phantom scroller in the dialog. -DaveP 9/3/96 |
short scrollType; |
Handle scrollHandle; |
Rect scrollRect; |
GetDialogItem((DialogPtr)PrtJobDialog,firstItem + 9, &scrollType, |
&scrollHandle, &scrollRect); |
SetDialogItem((DialogPtr)PrtJobDialog,firstItem + 9, ctrlItem, |
(Handle) (**gList).vScroll, &scrollRect); |
} |
LAddRow(9,0,gList); |
SetPt(&theCell,0,0); |
// Lets add some data |
LSetCell("Testing 1",9,theCell,gList); |
theCell.v ++; |
LSetCell("Testing 2",9,theCell,gList); |
theCell.v ++; |
LSetCell("Testing 3",9,theCell,gList); |
theCell.v ++; |
LSetCell("Testing 4",9,theCell,gList); |
theCell.v ++; |
LSetCell("Testing 5",9,theCell,gList); |
theCell.v ++; |
LSetCell("Testing 6",9,theCell,gList); |
theCell.v ++; |
LSetCell("Testing 7",9,theCell,gList); |
theCell.v ++; |
LSetCell("Testing 8",9,theCell,gList); |
theCell.v ++; |
LSetCell("Testing 9",9,theCell,gList); |
LActivate(true,gList); |
myDrawListUPP = NewUserItemProc(MyListDraw); |
SetDialogItem((DialogPtr) PrtJobDialog,firstItem + 8,itemType,(Handle)myDrawListUPP,&itemBox); |
} |
/* |
Now comes the part where we patch in our item handler. We have to save |
the old item handler address, so we can call it if one of the standard |
items is hit, and put our item handler's address in pItemProc field of |
the TPrDlg struct |
*/ |
prPItemProc = PrtJobDialog->pItemProc; |
/* Now we'll tell the modal item handler where our routine is */ |
PrtJobDialog->pItemProc = NewPItemProc(MyJobItems) ; |
/* PrDlgMain expects a pointer to the modified dialog to be returned...*/ |
return PrtJobDialog; |
} /*myJobDlgInit*/ |
/*-----------------------------------------------------------------------*/ |
static pascal Boolean testFilter(DialogPtr theDialog, EventRecord *theEvent, short *itemHit) |
{ |
#pragma unused(theDialog) |
#pragma unused(theEvent) |
#pragma unused(itemHit) |
// DebugStr("\pMy filter proc got called"); // This gets called a lot if you uncomment it |
return false; |
} |
static pascal void testDialogUserItem(WindowPtr theWindow, short itemNo); |
static pascal void testDialogUserItem(WindowPtr theWindow, short itemNo) |
{ |
if (itemNo != 4) return; |
else { |
short oldFont = ((GrafPtr)theWindow)->txFont; |
short oldSize = ((GrafPtr)theWindow)->txSize; |
short itemType; |
Handle h; |
Rect r; |
SetPort(theWindow); |
TextFont(GetAppFont()); |
TextSize(9); |
GetDialogItem((DialogPtr)theWindow,4,&itemType,&h,&r); |
MoveTo(r.left,r.bottom); |
DrawString("\pThe Pooh-snif network."); |
TextFont(oldFont); |
TextSize(oldSize); |
} |
} |
static void TestDialog(void); |
static void TestDialog(void) |
{ |
DialogPtr theDialog = GetNewDialog(257,nil,(WindowPtr)-1L); |
short itemHit = 0; |
ModalFilterUPP mySubDialogFilterProc = nil; // NewModalFilterProc(testFilter); // for testing |
UserItemUPP mySubDialogUserProc = NewUserItemProc(testDialogUserItem); |
{ // set up the user item to draw the outline around the ok button |
short t; |
Handle h; |
Rect r; |
GetDialogItem(theDialog,4,&t,&h,&r); |
SetDialogItem(theDialog,4,t,(Handle)mySubDialogUserProc,&r); |
} |
SetDialogDefaultItem(theDialog,1); |
while((itemHit != 1) && (itemHit != 2)) { // a WAY bad way of seeing if we're done, but I'm in a hurry |
ModalDialog(mySubDialogFilterProc, &itemHit); |
} |
DisposeDialog(theDialog); |
if (mySubDialogFilterProc != nil) |
DisposeRoutineDescriptor(mySubDialogFilterProc); |
if (mySubDialogUserProc != nil) |
DisposeRoutineDescriptor(mySubDialogUserProc); |
} |
/* here's the analogue to the SF dialog hook */ |
pascal void MyJobItems( TPPrDlg theDialog, short itemNo ) |
{ /* MyJobItems */ |
short myItem; |
short firstItem, item, itemType, theValue; |
Handle itemH; |
Rect itemBox; |
firstItem = prFirstItem; /* remember, we saved this in myJobDlgInit */ |
// DebugStr("\p Looking at the item..."); |
myItem = itemNo-firstItem+1; /* "localize" current item No */ |
if (myItem > 0) /* if localized item > 0, it's one of ours */ |
{ |
/* find out which of our items was hit */ |
switch (myItem) |
{ |
case 1: /* Static text. */ |
break; |
case 2: /* Edit text. */ |
break; |
case 3: /* Check box. */ |
GetDialogItem((DialogPtr) theDialog,firstItem +2,&itemType,&itemH,&itemBox); |
theValue = GetControlValue((ControlHandle) itemH); |
SetControlValue((ControlHandle) itemH, theValue != 1); |
break; |
case 4: /* Push button. */ |
TestDialog(); |
break; |
case 5: /* Radio buttons */ |
case 6: |
case 7: |
for (item = 5; item <= 7; item++) { |
GetDialogItem((DialogPtr) theDialog,firstItem +item -1,&itemType,&itemH,&itemBox); |
SetControlValue((ControlHandle) itemH, item == myItem); |
} |
break; |
case 8: /* Edit text. */ |
case 9: /* Our List. */ |
{ |
Point pt; |
GetMouse(&pt); |
LClick(pt,0,gList); |
} |
break; |
case 10: /* our list's scroll bar. We don't need to do anything, but we need |
* to have some code here so we don't hit the default case */ |
break; |
default: Debugger(); /* OH OH */ |
} /* switch */ |
} /* if (myItem > 0) */ |
else /* chain to standard item handler, whose address is saved in prPItemProc */ |
{ |
CallPItemProc(prPItemProc,(DialogPtr)theDialog, itemNo); |
if (theDialog->fDone) { |
// we're going away, need to clean up our scroll bar and list |
if (gList) { |
(**gList).vScroll = 0L; |
LDispose(gList); |
} |
} |
} |
} /* MyJobItems */ |
void main(void) |
{ |
Rect myWRect; |
InitGraf(&qd.thePort); |
InitFonts(); |
InitWindows(); |
InitMenus(); |
InitDialogs((long)nil); |
InitCursor(); |
SetRect(&myWRect,50,260,350,340); |
/* call the routine that does printing */ |
PrOpen(); |
err = Print(); |
PrClose(); |
} /* main */ |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14