| 1 |
/* |
| 2 |
* Simple Spooler |
| 3 |
* Copyright (C) 2026, Ken McLeod. |
| 4 |
* |
| 5 |
* This application registers itself as a virtual printer on the AppleTalk |
| 6 |
* network and receives print jobs using PAP (Printer Access Protocol). |
| 7 |
* It can optionally send the print job to a locally-connected ImageWriter |
| 8 |
* printer which otherwise would not be visible on the network. It can also |
| 9 |
* generate a PostScript file from the print job automatically, if the IWEm |
| 10 |
* file is present in the spool directory. |
| 11 |
* |
| 12 |
* Written by: Ken McLeod, 2026-01-07 |
| 13 |
* Last update: 2026-07-27 |
| 14 |
* |
| 15 |
* Some code was adapted from the Neighborhood Watch sample application |
| 16 |
* by Ricardo Batista, found on Apple Developer CD Volume IX, and from the |
| 17 |
* MoreFiles utility routines by Jim Luther, found on the March 1994 Dev CD. |
| 18 |
* |
| 19 |
* This software is provided as-is, with no warranties expressed or implied, |
| 20 |
* under the terms of the BSD 2-Clause License. See separate "LICENSE" file. |
| 21 |
*/ |
| 22 |
|
| 23 |
#include <StdIO.h> |
| 24 |
#include <String.h> |
| 25 |
#include <Memory.h> |
| 26 |
#include <Types.h> |
| 27 |
#include <CursorCtl.h> |
| 28 |
#include <AppleTalk.h> |
| 29 |
#include <Packages.h> |
| 30 |
#include <Events.h> |
| 31 |
#include <SysEqu.h> |
| 32 |
#include <GestaltEqu.h> |
| 33 |
#include <Controls.h> |
| 34 |
#include <Desk.h> |
| 35 |
#include <Devices.h> |
| 36 |
#include <Errors.h> |
| 37 |
#include <Fonts.h> |
| 38 |
#include <Menus.h> |
| 39 |
#include <ToolUtils.h> |
| 40 |
#include <Events.h> |
| 41 |
#include <Quickdraw.h> |
| 42 |
#include <Resources.h> |
| 43 |
#include <OSEvents.h> |
| 44 |
#include <Scrap.h> |
| 45 |
#include "Logging.h" |
| 46 |
#include "SpoolFiles.h" |
| 47 |
#include "PrintQueue.h" |
| 48 |
#include "PAP.h" |
| 49 |
|
| 50 |
/* resource IDs */ |
| 51 |
|
| 52 |
#define appleMenu 128 |
| 53 |
#define fileMenu 129 |
| 54 |
#define editMenu 130 |
| 55 |
#define controlMenu 131 |
| 56 |
|
| 57 |
#define quitItem 1 |
| 58 |
|
| 59 |
#define undoItem 1 |
| 60 |
#define cutItem 3 |
| 61 |
#define copyItem 4 |
| 62 |
#define pasteItem 5 |
| 63 |
#define clearItem 6 |
| 64 |
#define selectAllItem 8 |
| 65 |
|
| 66 |
#define startSpoolerItem 1 |
| 67 |
#define stopSpoolerItem 2 |
| 68 |
#define settingsItem 4 |
| 69 |
#define statusItem 5 |
| 70 |
|
| 71 |
#define okItem 1 |
| 72 |
#define cancelItem 2 |
| 73 |
#define spoolerNameItem 3 |
| 74 |
#define iconLWItem 6 |
| 75 |
#define iconLQItem 7 |
| 76 |
#define iconIWItem 8 |
| 77 |
#define modemPortItem 10 |
| 78 |
#define printerPortItem 11 |
| 79 |
#define colorRibbonCBoxItem 13 |
| 80 |
#define sheetFeederCBoxItem 14 |
| 81 |
#define iw1ModeCBoxItem 15 |
| 82 |
#define noReverseCBoxItem 16 |
| 83 |
#define printingEnabledItem 17 |
| 84 |
#define scheduleUserItem 18 |
| 85 |
#define printerTypeUserItem 19 |
| 86 |
#define localPortUserItem 20 |
| 87 |
#define autoRunCBoxItem 21 |
| 88 |
|
| 89 |
#define logWindowID 128 |
| 90 |
#define spoolerDialogID 128 |
| 91 |
#define upDownArrowsPictID 128 |
| 92 |
#define typeStringsID 128 |
| 93 |
#define nameStringsID 129 |
| 94 |
#define portStringsID 130 |
| 95 |
#define buttonStringsID 131 |
| 96 |
|
| 97 |
#define okStrIndex 1 |
| 98 |
#define cancelStrIndex 2 |
| 99 |
#define startStrIndex 3 |
| 100 |
#define saveStrIndex 4 |
| 101 |
|
| 102 |
#define kIconFrameInset -5 |
| 103 |
#define kIconFrameSize 2 |
| 104 |
#define kButtonFrameInset -4 |
| 105 |
#define kButtonFrameSize 3 |
| 106 |
#define kButtonFrameRadius 18 |
| 107 |
#define kMinWindowHeight 100 |
| 108 |
#define kMinWindowWidth 416 |
| 109 |
#define kPrefsVersion 3 |
| 110 |
|
| 111 |
|
| 112 |
/* function prototypes */ |
| 113 |
|
| 114 |
void DoAbout(void); |
| 115 |
void DoStatus(void); |
| 116 |
OSErr ConfigureSpooler(Boolean startingServer); |
| 117 |
OSErr StartSpooler(void); |
| 118 |
OSErr StopSpooler(void); |
| 119 |
void SetMenus(void); |
| 120 |
Boolean Setup(void); |
| 121 |
Boolean RunEventLoop(void); |
| 122 |
Boolean DoCommand(long mResult); |
| 123 |
Boolean HandleMouseDowns(void); |
| 124 |
void LoadPreferences(); |
| 125 |
void SavePreferences(); |
| 126 |
|
| 127 |
char ourName[256]; |
| 128 |
char ourType[34]; |
| 129 |
char ourPort[34]; |
| 130 |
char ourPrefix[4]; |
| 131 |
int serverRef = 0; |
| 132 |
Boolean inFront; |
| 133 |
EventRecord myEvent; |
| 134 |
MenuHandle myMenu[5]; |
| 135 |
WindowPtr textWindow = 0L; |
| 136 |
TEHandle textTE = 0L; |
| 137 |
Rect windowRect = { 40, 10, 320, 490 }; /* default, global coordinates */ |
| 138 |
ControlHandle textScrollBar = 0L; |
| 139 |
PicHandle arrowsPictHdl = 0L; |
| 140 |
short defaultPrinterItem = iconIWItem; |
| 141 |
short defaultPortItem = modemPortItem; |
| 142 |
Boolean defaultColorRibbon = false; |
| 143 |
Boolean defaultSheetFeeder = false; |
| 144 |
Boolean defaultPrintEnabled = true; |
| 145 |
Boolean defaultIW1Mode = false; |
| 146 |
Boolean defaultNoReverse = false; |
| 147 |
Boolean defaultAutoRun = false; |
| 148 |
short curSelectedIndex = 0; /* selected segment in date scheduler */ |
| 149 |
short schedStartHour = 0; /* default start to 12 AM */ |
| 150 |
short schedEndHour = 24; /* default end to next 12 AM */ |
| 151 |
char sAMStr[4]; /* pascal string with first 2 chars of AM string from itl0 */ |
| 152 |
char sPMStr[4]; /* pascal string with first 2 chars of PM string from itl0 */ |
| 153 |
char s12HrCycle; /* values: 0=24hr, 1=12hr */ |
| 154 |
|
| 155 |
char *sepStr = "\p----------------------------------------------------"; |
| 156 |
|
| 157 |
|
| 158 |
void main() |
| 159 |
{ |
| 160 |
if (!Setup()) { |
| 161 |
return; |
| 162 |
} |
| 163 |
/* either auto-run the server, or present the settings panel */ |
| 164 |
DoCommand((controlMenu << 16) + startSpoolerItem); |
| 165 |
|
| 166 |
while (RunEventLoop()) ; |
| 167 |
|
| 168 |
StopSpooler(); |
| 169 |
SavePreferences(); |
| 170 |
UnloadLogWindow(); |
| 171 |
} |
| 172 |
|
| 173 |
Boolean Setup(void) |
| 174 |
{ |
| 175 |
short count; |
| 176 |
for (count = 0; count < 6; count++) { |
| 177 |
MoreMasters(); |
| 178 |
} |
| 179 |
InitGraf(&qd.thePort); |
| 180 |
InitFonts(); |
| 181 |
InitWindows(); |
| 182 |
TEInit(); |
| 183 |
InitMenus(); |
| 184 |
InitDialogs(0L); |
| 185 |
DrawMenuBar(); |
| 186 |
FlushEvents(everyEvent,0L); |
| 187 |
InitCursor(); |
| 188 |
MaxApplZone(); |
| 189 |
SetCursor(*GetCursor(watchCursor)); |
| 190 |
InitAllPacks(); |
| 191 |
SetMenus(); |
| 192 |
if (!InitLogWindow(logWindowID, &textWindow, &textTE, &textScrollBar)) { |
| 193 |
return(false); |
| 194 |
} |
| 195 |
if (InitSpoolFolder() != noErr) { |
| 196 |
Message(LOG_ERR_X,"\pCould not find or create spool folder!"); |
| 197 |
} |
| 198 |
LoadPreferences(); |
| 199 |
ShowLogWindow(&windowRect); |
| 200 |
inFront = true; |
| 201 |
return(true); |
| 202 |
} |
| 203 |
|
| 204 |
void SetMenus() |
| 205 |
{ |
| 206 |
/* load menus from resource file */ |
| 207 |
int i; |
| 208 |
for (i=appleMenu-appleMenu; i<=controlMenu-appleMenu; i++) { |
| 209 |
myMenu[i] = GetMenu(appleMenu+i); |
| 210 |
} |
| 211 |
AddResMenu(myMenu[appleMenu-appleMenu],'DRVR'); |
| 212 |
/* install menus in menu bar */ |
| 213 |
for (i=appleMenu-appleMenu; i<=controlMenu-appleMenu; i++) { |
| 214 |
InsertMenu(myMenu[i], 0); |
| 215 |
} |
| 216 |
EnableItem(myMenu[controlMenu-appleMenu],startSpoolerItem); |
| 217 |
EnableItem(myMenu[editMenu-appleMenu],copyItem); |
| 218 |
EnableItem(myMenu[editMenu-appleMenu],selectAllItem); |
| 219 |
DrawMenuBar(); |
| 220 |
} |
| 221 |
|
| 222 |
Boolean RunEventLoop() |
| 223 |
{ |
| 224 |
register char theChar; |
| 225 |
register Boolean event; |
| 226 |
WindowPtr w; |
| 227 |
GrafPtr savePort; |
| 228 |
Rect box; |
| 229 |
Point mouse; |
| 230 |
|
| 231 |
CheckATP(); /* check incoming requests and responses */ |
| 232 |
CheckPrintQueue(defaultPrintEnabled, schedStartHour, schedEndHour); |
| 233 |
|
| 234 |
event = GetNextEvent(everyEvent,&myEvent); |
| 235 |
w = FrontWindow(); |
| 236 |
if ((w == textWindow) && inFront && textTE) { |
| 237 |
GetPort(&savePort); |
| 238 |
SetPort(textWindow); |
| 239 |
/* %%% don't need to blink insertion point; text isn't editable */ |
| 240 |
/*TEIdle(textTE);*/ |
| 241 |
box = w->portRect; |
| 242 |
GetMouse(&mouse); |
| 243 |
box.right -= 16; |
| 244 |
box.bottom -= 16; |
| 245 |
if (PtInRect(mouse,&box)) { |
| 246 |
SetCursor(*(GetCursor(iBeamCursor))); |
| 247 |
} else { |
| 248 |
InitCursor(); |
| 249 |
} |
| 250 |
SetPort(savePort); |
| 251 |
} |
| 252 |
if (event) { |
| 253 |
switch (myEvent.what) { |
| 254 |
case app4Evt: |
| 255 |
inFront = (myEvent.modifiers & 128) ? true : false; |
| 256 |
break; |
| 257 |
case activateEvt: |
| 258 |
w = (WindowPtr) myEvent.message; |
| 259 |
if (w == textWindow) { |
| 260 |
ActivateLogWindow(myEvent.modifiers & activeFlag); |
| 261 |
} |
| 262 |
break; |
| 263 |
case keyDown: |
| 264 |
case autoKey: |
| 265 |
theChar = myEvent.message & charCodeMask; |
| 266 |
if (myEvent.modifiers & cmdKey) { |
| 267 |
if (myEvent.modifiers & shiftKey) { |
| 268 |
if (theChar == 'l') { |
| 269 |
SetLogLevel(LogLevel()+1); |
| 270 |
if (LogLevel() > LOG_DEBUG) { |
| 271 |
SetLogLevel(LOG_ERR); |
| 272 |
} |
| 273 |
MessageWithIntValue(LOG_ERR,"\pSetting log level:", LogLevel()); |
| 274 |
} else if (theChar == 's') { |
| 275 |
DoStatus(); |
| 276 |
} |
| 277 |
} |
| 278 |
if (!DoCommand(MenuKey(theChar))) { |
| 279 |
return(false); |
| 280 |
} |
| 281 |
} else { |
| 282 |
w = FrontWindow(); |
| 283 |
/* %%% don't accept keyboard input into log text */ |
| 284 |
#if 0 |
| 285 |
if (w == textWindow) { |
| 286 |
short lines; |
| 287 |
HLock((Handle)textTE); |
| 288 |
lines = (**textTE).nLines; |
| 289 |
TEKey(theChar,textTE); |
| 290 |
if (lines != (**textTE).nLines) |
| 291 |
AdjustTextScrollBar(); |
| 292 |
HUnlock((Handle)textTE); |
| 293 |
} |
| 294 |
#endif |
| 295 |
} |
| 296 |
break; |
| 297 |
case updateEvt: |
| 298 |
w = (WindowPtr) myEvent.message; |
| 299 |
if (w == textWindow) |
| 300 |
UpdateLogWindow(); |
| 301 |
break; |
| 302 |
case mouseDown: |
| 303 |
if (!HandleMouseDowns()) |
| 304 |
return(false); |
| 305 |
break; |
| 306 |
default: |
| 307 |
break; |
| 308 |
} |
| 309 |
} |
| 310 |
return(true); |
| 311 |
} |
| 312 |
|
| 313 |
Boolean DoCommand(long mResult) |
| 314 |
{ |
| 315 |
OSErr err; |
| 316 |
register short theItem; |
| 317 |
char st[256]; |
| 318 |
|
| 319 |
theItem = LoWord(mResult); |
| 320 |
switch (HiWord(mResult)) { |
| 321 |
case appleMenu: |
| 322 |
GetItem(myMenu[0],theItem,st); |
| 323 |
if (theItem > 2) |
| 324 |
OpenDeskAcc(st); |
| 325 |
else |
| 326 |
DoAbout(); |
| 327 |
break; |
| 328 |
case fileMenu: |
| 329 |
switch (theItem) { |
| 330 |
case quitItem: |
| 331 |
return(false); |
| 332 |
break; |
| 333 |
default: |
| 334 |
break; |
| 335 |
} |
| 336 |
break; |
| 337 |
case editMenu: |
| 338 |
if (!SystemEdit(theItem-1)) { |
| 339 |
switch (theItem) { |
| 340 |
case undoItem: |
| 341 |
break; |
| 342 |
case cutItem: |
| 343 |
if (textTE) { |
| 344 |
TECut(textTE); |
| 345 |
AdjustTextScrollBar(); |
| 346 |
} |
| 347 |
ZeroScrap(); |
| 348 |
TEToScrap(); |
| 349 |
break; |
| 350 |
case copyItem: |
| 351 |
if (textTE) { |
| 352 |
TECopy(textTE); |
| 353 |
} |
| 354 |
ZeroScrap(); |
| 355 |
TEToScrap(); |
| 356 |
break; |
| 357 |
case pasteItem: |
| 358 |
TEFromScrap(); |
| 359 |
if (textTE) { |
| 360 |
TEPaste(textTE); |
| 361 |
AdjustTextScrollBar(); |
| 362 |
} |
| 363 |
break; |
| 364 |
case clearItem: |
| 365 |
if (textTE) { |
| 366 |
TEDelete(textTE); |
| 367 |
AdjustTextScrollBar(); |
| 368 |
} |
| 369 |
break; |
| 370 |
case selectAllItem: |
| 371 |
if (textTE) { |
| 372 |
int index; |
| 373 |
HLock((Handle)textTE); |
| 374 |
index = (**textTE).teLength; |
| 375 |
HUnlock((Handle)textTE); |
| 376 |
TESetSelect(0,index,textTE); |
| 377 |
} |
| 378 |
break; |
| 379 |
default: |
| 380 |
break; |
| 381 |
} |
| 382 |
} |
| 383 |
break; |
| 384 |
case controlMenu: |
| 385 |
switch (theItem) { |
| 386 |
case startSpoolerItem: |
| 387 |
err = StartSpooler(); |
| 388 |
if (err == noErr) { |
| 389 |
StartPrintQueue(ourPrefix,ourPort); |
| 390 |
Message(LOG_ERR,"\pSpooler started"); |
| 391 |
EnableItem(myMenu[controlMenu-appleMenu],stopSpoolerItem); |
| 392 |
DisableItem(myMenu[controlMenu-appleMenu],startSpoolerItem); |
| 393 |
} else { |
| 394 |
Message(LOG_ERR,"\pSelect Control > Start Spooler to start the server"); |
| 395 |
EnableItem(myMenu[controlMenu-appleMenu],startSpoolerItem); |
| 396 |
DisableItem(myMenu[controlMenu-appleMenu],stopSpoolerItem); |
| 397 |
} |
| 398 |
break; |
| 399 |
case stopSpoolerItem: |
| 400 |
err = StopSpooler(); |
| 401 |
if (err == noErr) { |
| 402 |
StopPrintQueue(); |
| 403 |
Message(LOG_ERR,"\pSpooler stopped"); |
| 404 |
} else { |
| 405 |
MessageWithIntValue(LOG_ERR,"\pERROR: Could not stop spooler:",err); |
| 406 |
} |
| 407 |
EnableItem(myMenu[controlMenu-appleMenu],startSpoolerItem); |
| 408 |
DisableItem(myMenu[controlMenu-appleMenu],stopSpoolerItem); |
| 409 |
break; |
| 410 |
case settingsItem: |
| 411 |
err = ConfigureSpooler(false); |
| 412 |
if (err == noErr && serverRef) { |
| 413 |
/* spooler already running, recursively stop and restart it */ |
| 414 |
Boolean savedAutoRun = defaultAutoRun; |
| 415 |
DoCommand((controlMenu << 16) + stopSpoolerItem); |
| 416 |
defaultAutoRun = true; /* don't put up the dialog twice */ |
| 417 |
DoCommand((controlMenu << 16) + startSpoolerItem); |
| 418 |
defaultAutoRun = savedAutoRun; |
| 419 |
} |
| 420 |
break; |
| 421 |
case statusItem: |
| 422 |
DoStatus(); |
| 423 |
break; |
| 424 |
default: |
| 425 |
break; |
| 426 |
} |
| 427 |
break; |
| 428 |
default: |
| 429 |
break; |
| 430 |
} |
| 431 |
HiliteMenu(0); |
| 432 |
return(true); |
| 433 |
} |
| 434 |
|
| 435 |
Boolean HandleMouseDowns() |
| 436 |
{ |
| 437 |
WindowPtr whichWindow; |
| 438 |
Rect box; |
| 439 |
long new; |
| 440 |
short v, h; |
| 441 |
|
| 442 |
switch (FindWindow(myEvent.where,&whichWindow)) { |
| 443 |
case inSysWindow: |
| 444 |
SystemClick(&myEvent,whichWindow); |
| 445 |
break; |
| 446 |
case inMenuBar: |
| 447 |
return(DoCommand(MenuSelect(myEvent.where))); |
| 448 |
break; |
| 449 |
case inGrow: |
| 450 |
box = qd.screenBits.bounds; |
| 451 |
box.top = kMinWindowHeight; |
| 452 |
box.left = kMinWindowWidth; |
| 453 |
new = GrowWindow(whichWindow,myEvent.where,&box); |
| 454 |
if (new) { |
| 455 |
v = HiWord(new); |
| 456 |
h = LoWord(new); |
| 457 |
SetPort(whichWindow); |
| 458 |
SizeWindow(whichWindow,h,v,true); |
| 459 |
EraseRect(&(whichWindow->portRect)); |
| 460 |
InvalRect(&(whichWindow->portRect)); |
| 461 |
if (whichWindow == textWindow) { |
| 462 |
GrowLogWindow(h,v); |
| 463 |
} |
| 464 |
} |
| 465 |
break; |
| 466 |
case inGoAway: |
| 467 |
if (!TrackGoAway(whichWindow,myEvent.where)) |
| 468 |
break; |
| 469 |
break; |
| 470 |
case inDrag: |
| 471 |
if ((myEvent.modifiers & cmdKey) || (FrontWindow() == whichWindow)) |
| 472 |
DragWindow(whichWindow,myEvent.where,&qd.screenBits.bounds); |
| 473 |
else { |
| 474 |
SelectWindow(whichWindow); |
| 475 |
SetPort(whichWindow); |
| 476 |
} |
| 477 |
break; |
| 478 |
case inContent: |
| 479 |
if (whichWindow != FrontWindow()) { |
| 480 |
SelectWindow(whichWindow); |
| 481 |
SetPort(whichWindow); |
| 482 |
} |
| 483 |
else { |
| 484 |
if (whichWindow == textWindow) { |
| 485 |
HandleMouseInText(&myEvent); |
| 486 |
} |
| 487 |
} |
| 488 |
break; |
| 489 |
default: |
| 490 |
break; |
| 491 |
} |
| 492 |
return(true); |
| 493 |
} |
| 494 |
|
| 495 |
void CenterRect(Rect *r) |
| 496 |
{ |
| 497 |
/* centers the rectangle horizontally on main screen, |
| 498 |
* one-third of the way down vertically from the top. |
| 499 |
*/ |
| 500 |
OffsetRect(r, |
| 501 |
- r->left |
| 502 |
+ ((qd.screenBits.bounds.right - qd.screenBits.bounds.left) |
| 503 |
- (r->right - r->left)) / 2, |
| 504 |
- r->top |
| 505 |
+ (((qd.screenBits.bounds.bottom - qd.screenBits.bounds.top - GetMBarHeight()) |
| 506 |
- (r->bottom - r->top)) / 3) + GetMBarHeight()); |
| 507 |
} |
| 508 |
|
| 509 |
DialogTHndl CenterDialog(short whichID) |
| 510 |
{ |
| 511 |
DialogTHndl d = (DialogTHndl) GetResource('DLOG', whichID); |
| 512 |
if ((d != 0L) && (ResError() == noErr)) { |
| 513 |
HLock((Handle)d); |
| 514 |
CenterRect(&(*d)->boundsRect); |
| 515 |
HUnlock((Handle)d); |
| 516 |
} |
| 517 |
return(d); |
| 518 |
} |
| 519 |
|
| 520 |
void LoadDateFormatStrings(void) |
| 521 |
{ |
| 522 |
/*Intl0Hndl h = (Intl0Hndl)GetResource('itl0',16383); /* explicit user overrides? */ |
| 523 |
Intl0Hndl h = (Intl0Hndl)IUGetIntl(0); /* get current date & time formats */ |
| 524 |
if (h) { |
| 525 |
HLock((Handle)h); |
| 526 |
s12HrCycle = ((**h).timeCycle == 0xFF) ? true:false; /* 0x00,0x01:24h, 0xFF:12hr */ |
| 527 |
if (s12HrCycle) { |
| 528 |
/* these are C strings with a leading space character that we skip */ |
| 529 |
BlockMove(&(**h).mornStr[1],&sAMStr[1],2); |
| 530 |
BlockMove(&(**h).eveStr[1],&sPMStr[1],2); |
| 531 |
sAMStr[0] = sPMStr[0] = 2; |
| 532 |
} else { |
| 533 |
BlockMove("\p00",sAMStr,3); |
| 534 |
BlockMove(sAMStr,sPMStr,3); |
| 535 |
} |
| 536 |
HUnlock((Handle)h); |
| 537 |
ReleaseResource((Handle)h); |
| 538 |
} else { |
| 539 |
s12HrCycle = true; |
| 540 |
BlockMove("\pAM",sAMStr,3); |
| 541 |
BlockMove("\pPM",sPMStr,3); |
| 542 |
} |
| 543 |
} |
| 544 |
|
| 545 |
void GetScheduleStrings(short scheduledHour, char *theHourStr, char *theAmPmStr) |
| 546 |
{ |
| 547 |
/* fill in the provided Pascal string buffers, given scheduledHour as 0..24 */ |
| 548 |
/* note: each buffer must be able to hold at least 2 bytes + 1 length byte. */ |
| 549 |
|
| 550 |
short hour = scheduledHour; |
| 551 |
BlockMove(sAMStr,theAmPmStr,3); |
| 552 |
if (hour >= 12) { |
| 553 |
hour = (hour == 12) ? 12 : hour - 12; |
| 554 |
if (scheduledHour < 24) { |
| 555 |
BlockMove(sPMStr,theAmPmStr,3); |
| 556 |
} |
| 557 |
} |
| 558 |
theHourStr[0] = 2; |
| 559 |
if (!s12HrCycle) { |
| 560 |
theHourStr[1] = (scheduledHour < 10) ? '0':(scheduledHour < 20) ? '1':'2'; |
| 561 |
theHourStr[2] = '0' + (scheduledHour % 10); |
| 562 |
} else if (hour > 9) { |
| 563 |
theHourStr[1] = '1'; |
| 564 |
theHourStr[2] = '0' + (hour - 10); |
| 565 |
} else if (hour < 1) { |
| 566 |
theHourStr[1] = '1'; |
| 567 |
theHourStr[2] = '2'; |
| 568 |
} else { |
| 569 |
theHourStr[1] = ' '; |
| 570 |
theHourStr[2] = '1' + (hour - 1); |
| 571 |
} |
| 572 |
} |
| 573 |
|
| 574 |
void HandleClickInScheduler(Point pt, Rect *r, Boolean redrawAll) |
| 575 |
{ |
| 576 |
Boolean redraw = redrawAll; |
| 577 |
short index; |
| 578 |
Rect tmp = *r; |
| 579 |
Rect parts[7] = { |
| 580 |
{ tmp.top, tmp.left+0, tmp.bottom, tmp.left+0 }, |
| 581 |
{ tmp.top, tmp.left+2, tmp.bottom, tmp.left+21 }, |
| 582 |
{ tmp.top, tmp.left+22, tmp.bottom, tmp.left+43 }, |
| 583 |
{ tmp.top, tmp.left+44, tmp.bottom, tmp.left+54 }, |
| 584 |
{ tmp.top, tmp.left+54, tmp.bottom, tmp.left+73 }, |
| 585 |
{ tmp.top, tmp.left+74, tmp.bottom, tmp.left+95 }, |
| 586 |
{ tmp.top, tmp.left+98, tmp.bottom, tmp.right }, |
| 587 |
}; |
| 588 |
|
| 589 |
for (index = 1; index < 6; index++) { |
| 590 |
if (index == 3 || (!s12HrCycle && (index == 2 || index == 5))) { |
| 591 |
continue; /* not selectable */ |
| 592 |
} |
| 593 |
if (PtInRect(pt, &parts[index])) { |
| 594 |
InvertRect(&parts[curSelectedIndex]); /* deselect the current selection first */ |
| 595 |
if (curSelectedIndex == index) { |
| 596 |
curSelectedIndex = 0; /* we deselected this, so now there is no selection */ |
| 597 |
} else { |
| 598 |
InvertRect(&parts[index]); |
| 599 |
curSelectedIndex = index; /* this index is now the current selection */ |
| 600 |
} |
| 601 |
break; |
| 602 |
} |
| 603 |
} |
| 604 |
if (PtInRect(pt, &parts[6])) { |
| 605 |
/* click in arrow control */ |
| 606 |
Boolean increase = false; |
| 607 |
redraw = true; |
| 608 |
if (pt.v < parts[6].top + ((parts[6].bottom-parts[6].top)/2)) { |
| 609 |
/* down arrow, increase or toggle value */ |
| 610 |
increase = true; |
| 611 |
} |
| 612 |
switch (curSelectedIndex) { |
| 613 |
case 0: /* nothing was selected, so select first item as a hint */ |
| 614 |
curSelectedIndex = 1; |
| 615 |
InvertRect(&parts[curSelectedIndex]); |
| 616 |
break; |
| 617 |
case 1: /* start hour (0..24) */ |
| 618 |
if (increase) { |
| 619 |
if (schedStartHour < 24) { schedStartHour++; } |
| 620 |
} else { |
| 621 |
if (schedStartHour > 0) { schedStartHour--; } |
| 622 |
} |
| 623 |
if (schedStartHour > schedEndHour) { schedEndHour++; /* push it forward */ } |
| 624 |
break; |
| 625 |
case 2: /* start am/pm */ |
| 626 |
if (schedStartHour == 24) { |
| 627 |
/* toggle start from AM to PM (24 to 12) */ |
| 628 |
schedStartHour -= 12; |
| 629 |
if (schedEndHour < schedStartHour) { schedEndHour = schedStartHour; } |
| 630 |
} else if (schedStartHour < 12) { |
| 631 |
/* toggle start from AM to PM (0..11 to 12..23) */ |
| 632 |
schedStartHour += 12; |
| 633 |
if (schedEndHour < schedStartHour) { schedEndHour = schedStartHour; } |
| 634 |
} else { |
| 635 |
/* toggle from PM to AM (12..23 to 0..11) */ |
| 636 |
schedStartHour -= 12; |
| 637 |
} |
| 638 |
break; |
| 639 |
case 4: /* end hour (0..24) */ |
| 640 |
if (increase) { |
| 641 |
if (schedEndHour < 24) { schedEndHour++; } |
| 642 |
} else { |
| 643 |
if (schedEndHour > 0) { schedEndHour--; } |
| 644 |
} |
| 645 |
if (schedEndHour < schedStartHour) { schedStartHour = schedEndHour; } |
| 646 |
break; |
| 647 |
case 5: /* end am/pm */ |
| 648 |
if (schedEndHour == 24) { |
| 649 |
/* toggle start from AM to PM (24 to 12) */ |
| 650 |
schedEndHour -= 12; |
| 651 |
if (schedStartHour > schedEndHour) { schedStartHour = schedEndHour; } |
| 652 |
} else if (schedEndHour < 12) { |
| 653 |
/* toggle start from AM to PM (0..11 to 12..23) */ |
| 654 |
schedEndHour += 12; |
| 655 |
if (schedEndHour < schedStartHour) { schedEndHour = schedStartHour; } |
| 656 |
} else { |
| 657 |
/* toggle from PM to AM (12..23 to 0..11) */ |
| 658 |
schedEndHour -= 12; |
| 659 |
if (schedEndHour < schedStartHour) { schedStartHour = schedEndHour; } |
| 660 |
} |
| 661 |
break; |
| 662 |
default: |
| 663 |
break; |
| 664 |
} |
| 665 |
} |
| 666 |
|
| 667 |
if (redraw) { |
| 668 |
char startHourStr[4]; |
| 669 |
char startAmPmStr[4]; |
| 670 |
char endHourStr[4]; |
| 671 |
char endAmPmStr[4]; |
| 672 |
GetScheduleStrings(schedStartHour, startHourStr, startAmPmStr); |
| 673 |
GetScheduleStrings(schedEndHour, endHourStr, endAmPmStr); |
| 674 |
TextFont(1); |
| 675 |
for (index = 1; index < 6; index++) { |
| 676 |
EraseRect(&parts[index]); |
| 677 |
MoveTo(parts[index].left,tmp.bottom-5); |
| 678 |
switch (index) { |
| 679 |
case 1: DrawString(startHourStr); break; |
| 680 |
case 2: DrawString(startAmPmStr); break; |
| 681 |
case 3: DrawString("\p-"); break; |
| 682 |
case 4: DrawString(endHourStr); break; |
| 683 |
case 5: DrawString(endAmPmStr); break; |
| 684 |
default: |
| 685 |
break; |
| 686 |
} |
| 687 |
if (index == curSelectedIndex) { |
| 688 |
InvertRect(&parts[index]); /* re-select current selection */ |
| 689 |
} |
| 690 |
} |
| 691 |
TextFont(0); |
| 692 |
} |
| 693 |
} |
| 694 |
|
| 695 |
void ShowButtonPress(DialogPtr theDialog, short theItem) |
| 696 |
{ |
| 697 |
long finalCount; |
| 698 |
short tmpInt; |
| 699 |
Handle tmpHandle; |
| 700 |
Rect tmpRect; |
| 701 |
|
| 702 |
GetDItem(theDialog, theItem, &tmpInt, &tmpHandle, &tmpRect); |
| 703 |
HiliteControl((ControlHandle)tmpHandle, true); /* "push" */ |
| 704 |
Delay(10L, &finalCount); |
| 705 |
HiliteControl((ControlHandle)tmpHandle, false); /* "release" */ |
| 706 |
} |
| 707 |
|
| 708 |
pascal Boolean MyDialogFilter(DialogPtr theDialog, EventRecord *theEvent, short *itemHit) |
| 709 |
{ |
| 710 |
char tmpChar; |
| 711 |
short tmpInt; |
| 712 |
Handle tmpHandle; |
| 713 |
Rect tmpRect, schedRect, picRect; |
| 714 |
Point tmpPt = { 0,0 }; |
| 715 |
|
| 716 |
switch(theEvent->what) { |
| 717 |
case keyDown: |
| 718 |
case autoKey: |
| 719 |
/* convert the key to char */ |
| 720 |
tmpChar = (unsigned char)(theEvent->message & charCodeMask); |
| 721 |
if (tmpChar == '\r' || tmpChar == '\n' || tmpChar == '\3') { |
| 722 |
/* return or enter pressed */ |
| 723 |
*itemHit = okItem; |
| 724 |
ShowButtonPress(theDialog, okItem); |
| 725 |
return true; |
| 726 |
} |
| 727 |
else if (((theEvent->modifiers & cmdKey) && (tmpChar == '.')) || |
| 728 |
(tmpChar == 0x1B)) { |
| 729 |
/* command-period or escape pressed */ |
| 730 |
*itemHit = cancelItem; |
| 731 |
ShowButtonPress(theDialog, cancelItem); |
| 732 |
return true; |
| 733 |
} |
| 734 |
return false; |
| 735 |
case mouseDown: |
| 736 |
GetDItem(theDialog, scheduleUserItem, &tmpInt, &tmpHandle, &tmpRect); |
| 737 |
tmpPt = theEvent->where; |
| 738 |
GlobalToLocal(&tmpPt); |
| 739 |
if (PtInRect(tmpPt, &tmpRect)) { |
| 740 |
HandleClickInScheduler(tmpPt, &tmpRect, false); |
| 741 |
} |
| 742 |
return false; |
| 743 |
case updateEvt: |
| 744 |
/* is the update event for this window? */ |
| 745 |
if (theEvent->message == (long)theDialog) { |
| 746 |
/* draw default button indicator */ |
| 747 |
GetDItem(theDialog, okItem, &tmpInt, &tmpHandle, &tmpRect); |
| 748 |
PenSize(kButtonFrameSize,kButtonFrameSize); |
| 749 |
InsetRect(&tmpRect,kButtonFrameInset,kButtonFrameInset); |
| 750 |
FrameRoundRect(&tmpRect,kButtonFrameRadius,kButtonFrameRadius); |
| 751 |
PenNormal(); |
| 752 |
|
| 753 |
/* draw group boxes around user items */ |
| 754 |
GetDItem(theDialog, printerTypeUserItem, &tmpInt, &tmpHandle, &tmpRect); |
| 755 |
PenSize(1,1); |
| 756 |
PenPat(&qd.gray); |
| 757 |
FrameRect(&tmpRect); |
| 758 |
PenNormal(); |
| 759 |
GetDItem(theDialog, localPortUserItem, &tmpInt, &tmpHandle, &tmpRect); |
| 760 |
PenSize(1,1); |
| 761 |
PenPat(&qd.gray); |
| 762 |
FrameRect(&tmpRect); |
| 763 |
PenNormal(); |
| 764 |
|
| 765 |
/* draw arrow control */ |
| 766 |
GetDItem(theDialog, scheduleUserItem, &tmpInt, &tmpHandle, &tmpRect); |
| 767 |
HLock((Handle)arrowsPictHdl); |
| 768 |
picRect = (**arrowsPictHdl).picFrame; |
| 769 |
HUnlock((Handle)arrowsPictHdl); |
| 770 |
schedRect = tmpRect; |
| 771 |
tmpRect.left = schedRect.right - (picRect.right-picRect.left); |
| 772 |
tmpRect.bottom = schedRect.top + (picRect.bottom-picRect.top); |
| 773 |
DrawPicture(arrowsPictHdl,&tmpRect); |
| 774 |
HandleClickInScheduler(tmpPt,&schedRect,true); /* draw current values */ |
| 775 |
} |
| 776 |
return false; |
| 777 |
default: |
| 778 |
return false; |
| 779 |
} |
| 780 |
return false; |
| 781 |
} |
| 782 |
|
| 783 |
OSErr ConfigureSpooler(Boolean willStartServer) |
| 784 |
{ |
| 785 |
char portStr[256]; |
| 786 |
char typeStr[256]; |
| 787 |
char nameStr[256]; |
| 788 |
GrafPtr savePort; |
| 789 |
DialogTHndl dtHdl; |
| 790 |
DialogPtr aDialog; |
| 791 |
short item; |
| 792 |
Handle H; |
| 793 |
Rect box, printerBox, portBox; |
| 794 |
DialogRecord dlgRec; |
| 795 |
|
| 796 |
/* preload stuff */ |
| 797 |
LoadPreferences(); |
| 798 |
LoadDateFormatStrings(); |
| 799 |
arrowsPictHdl = (PicHandle)GetResource('PICT',upDownArrowsPictID); |
| 800 |
|
| 801 |
GetPort(&savePort); |
| 802 |
/* get handle to dialog template and center it before displaying */ |
| 803 |
dtHdl = CenterDialog(spoolerDialogID); |
| 804 |
aDialog = GetNewDialog(spoolerDialogID, (Ptr)&dlgRec, (WindowPtr) -1L); |
| 805 |
if (aDialog) { |
| 806 |
/* set up default and alternate button titles */ |
| 807 |
GetIndString(nameStr,buttonStringsID,(willStartServer) ? startStrIndex:saveStrIndex); |
| 808 |
GetDItem(aDialog, okItem, &item, &H, &box); |
| 809 |
SetCTitle((ControlHandle)H, nameStr); |
| 810 |
/* should we disable button if server is already running? */ |
| 811 |
/* (no, just stop and restart the server to pick up new settings) */ |
| 812 |
/*HiliteControl((ControlHandle)H,(serverRef!=0)?255:0);*/ |
| 813 |
GetIndString(nameStr,buttonStringsID,cancelStrIndex); |
| 814 |
GetDItem(aDialog, cancelItem, &item, &H, &box); |
| 815 |
SetCTitle((ControlHandle)H, nameStr); |
| 816 |
|
| 817 |
/* set up default name, printer type, and printer options */ |
| 818 |
GetIndString(typeStr,typeStringsID,defaultPrinterItem-(iconLWItem-1)); |
| 819 |
GetIndString(nameStr,nameStringsID,defaultPrinterItem-(iconLWItem-1)); |
| 820 |
GetDItem(aDialog, spoolerNameItem, &item, &H, &box); |
| 821 |
SetIText(H, (ourName[0] > 0) ? ourName : nameStr); |
| 822 |
GetDItem(aDialog, defaultPrinterItem, &item, &H, &box); |
| 823 |
printerBox = box; |
| 824 |
|
| 825 |
GetDItem(aDialog, colorRibbonCBoxItem, &item, &H, &box); |
| 826 |
SetCtlValue((ControlHandle)H,defaultColorRibbon); |
| 827 |
HiliteControl((ControlHandle)H,(defaultPrinterItem==iconLWItem)?255:0); |
| 828 |
GetDItem(aDialog, sheetFeederCBoxItem, &item, &H, &box); |
| 829 |
SetCtlValue((ControlHandle)H,defaultSheetFeeder); |
| 830 |
HiliteControl((ControlHandle)H,(defaultPrinterItem==iconLWItem)?255:0); |
| 831 |
GetDItem(aDialog, iw1ModeCBoxItem, &item, &H, &box); |
| 832 |
SetCtlValue((ControlHandle)H,defaultIW1Mode); |
| 833 |
HiliteControl((ControlHandle)H,(defaultPrinterItem==iconLWItem)?255:0); |
| 834 |
GetDItem(aDialog, noReverseCBoxItem, &item, &H, &box); |
| 835 |
SetCtlValue((ControlHandle)H,defaultNoReverse); |
| 836 |
HiliteControl((ControlHandle)H,(defaultPrinterItem==iconLWItem)?255:0); |
| 837 |
GetDItem(aDialog, printingEnabledItem, &item, &H, &box); |
| 838 |
SetCtlValue((ControlHandle)H,defaultPrintEnabled); |
| 839 |
GetDItem(aDialog, autoRunCBoxItem, &item, &H, &box); |
| 840 |
SetCtlValue((ControlHandle)H,defaultAutoRun); |
| 841 |
|
| 842 |
/* set up default port */ |
| 843 |
GetIndString(portStr,portStringsID,defaultPortItem-(modemPortItem-1)); |
| 844 |
GetDItem(aDialog, defaultPortItem, &item, &H, &box); |
| 845 |
portBox = box; |
| 846 |
|
| 847 |
SetPort(aDialog); |
| 848 |
ShowWindow(aDialog); |
| 849 |
InitCursor(); |
| 850 |
|
| 851 |
/* draw frames for initial selections */ |
| 852 |
PenSize(kIconFrameSize,kIconFrameSize); |
| 853 |
InsetRect(&printerBox,kIconFrameInset,kIconFrameInset); |
| 854 |
FrameRect(&printerBox); |
| 855 |
InsetRect(&portBox,kIconFrameInset,kIconFrameInset); |
| 856 |
FrameRect(&portBox); |
| 857 |
PenNormal(); |
| 858 |
|
| 859 |
item = 0; |
| 860 |
while ((item != okItem) && (item != cancelItem)) { |
| 861 |
if (item >= iconLWItem && item <= iconIWItem) { |
| 862 |
short tmpItem, index; |
| 863 |
for (index = iconLWItem; index <= iconIWItem; index++) { |
| 864 |
GetDItem(aDialog, index, &tmpItem, &H, &box); |
| 865 |
InsetRect(&box,kIconFrameInset,kIconFrameInset); |
| 866 |
PenSize(kIconFrameSize,kIconFrameSize); |
| 867 |
PenPat(&qd.white); |
| 868 |
FrameRect(&box); |
| 869 |
PenNormal(); |
| 870 |
} |
| 871 |
GetDItem(aDialog, item, &tmpItem, &H, &box); |
| 872 |
InsetRect(&box,kIconFrameInset,kIconFrameInset); |
| 873 |
PenSize(kIconFrameSize,kIconFrameSize); |
| 874 |
PenPat(&qd.black); |
| 875 |
FrameRect(&box); |
| 876 |
PenNormal(); |
| 877 |
GetIndString(typeStr, 128, item-(iconLWItem-1)); |
| 878 |
GetIndString(nameStr, 129, item-(iconLWItem-1)); |
| 879 |
GetDItem(aDialog, spoolerNameItem, &tmpItem, &H, &box); |
| 880 |
SetIText(H, nameStr); |
| 881 |
GetDItem(aDialog, colorRibbonCBoxItem, &tmpItem, &H, &box); |
| 882 |
HiliteControl((ControlHandle)H,(item==iconLWItem)?255:0); |
| 883 |
GetDItem(aDialog, sheetFeederCBoxItem, &tmpItem, &H, &box); |
| 884 |
HiliteControl((ControlHandle)H,(item==iconLWItem)?255:0); |
| 885 |
GetDItem(aDialog, iw1ModeCBoxItem, &tmpItem, &H, &box); |
| 886 |
HiliteControl((ControlHandle)H,(item!=iconIWItem)?255:0); |
| 887 |
if (item!=iconIWItem) { SetCtlValue((ControlHandle)H,0); } |
| 888 |
GetDItem(aDialog, noReverseCBoxItem, &tmpItem, &H, &box); |
| 889 |
HiliteControl((ControlHandle)H,(item==iconLWItem)?255:0); |
| 890 |
if (item==iconLWItem) { SetCtlValue((ControlHandle)H,0); } |
| 891 |
} |
| 892 |
else if (item >= modemPortItem && item <= printerPortItem) { |
| 893 |
short tmpItem, index; |
| 894 |
for (index = modemPortItem; index <= printerPortItem; index++) { |
| 895 |
GetDItem(aDialog, index, &tmpItem, &H, &box); |
| 896 |
InsetRect(&box,kIconFrameInset,kIconFrameInset); |
| 897 |
PenSize(kIconFrameSize,kIconFrameSize); |
| 898 |
PenPat(&qd.white); |
| 899 |
FrameRect(&box); |
| 900 |
PenNormal(); |
| 901 |
} |
| 902 |
GetDItem(aDialog, item, &tmpItem, &H, &box); |
| 903 |
InsetRect(&box,kIconFrameInset,kIconFrameInset); |
| 904 |
PenSize(kIconFrameSize,kIconFrameSize); |
| 905 |
PenPat(&qd.black); |
| 906 |
FrameRect(&box); |
| 907 |
PenNormal(); |
| 908 |
GetIndString(portStr, 130, item-(modemPortItem-1)); |
| 909 |
} |
| 910 |
else if ((item >= colorRibbonCBoxItem && item <= printingEnabledItem) || |
| 911 |
(item == autoRunCBoxItem)) { |
| 912 |
short tmpItem, curValue; |
| 913 |
GetDItem(aDialog, item, &tmpItem, &H, &box); |
| 914 |
curValue = GetCtlValue((ControlHandle)H); |
| 915 |
SetCtlValue((ControlHandle)H,!curValue); |
| 916 |
} |
| 917 |
ModalDialog((ModalFilterProcPtr)MyDialogFilter, &item); |
| 918 |
} |
| 919 |
if (item == okItem) { |
| 920 |
short tmpItem; |
| 921 |
Boolean hasColorRibbon, hasSheetFeeder; |
| 922 |
Boolean iw1Mode, noReverse, printEnabled, autoRun; |
| 923 |
GetDItem(aDialog, spoolerNameItem, &tmpItem, &H, &box); |
| 924 |
GetIText(H, nameStr); |
| 925 |
GetDItem(aDialog, colorRibbonCBoxItem, &tmpItem, &H, &box); |
| 926 |
hasColorRibbon = (GetCtlValue((ControlHandle)H) != 0); |
| 927 |
GetDItem(aDialog, sheetFeederCBoxItem, &tmpItem, &H, &box); |
| 928 |
hasSheetFeeder = (GetCtlValue((ControlHandle)H) != 0); |
| 929 |
GetDItem(aDialog, iw1ModeCBoxItem, &tmpItem, &H, &box); |
| 930 |
iw1Mode = (GetCtlValue((ControlHandle)H) != 0); |
| 931 |
GetDItem(aDialog, noReverseCBoxItem, &tmpItem, &H, &box); |
| 932 |
noReverse = (GetCtlValue((ControlHandle)H) != 0); |
| 933 |
GetDItem(aDialog, printingEnabledItem, &tmpItem, &H, &box); |
| 934 |
printEnabled = (GetCtlValue((ControlHandle)H) != 0); |
| 935 |
GetDItem(aDialog, autoRunCBoxItem, &tmpItem, &H, &box); |
| 936 |
autoRun = (GetCtlValue((ControlHandle)H) != 0); |
| 937 |
SetPrinterOptions(hasColorRibbon, hasSheetFeeder,iw1Mode,noReverse); |
| 938 |
defaultColorRibbon = hasColorRibbon; |
| 939 |
defaultSheetFeeder = hasSheetFeeder; |
| 940 |
defaultPrintEnabled = printEnabled; |
| 941 |
defaultIW1Mode = iw1Mode; |
| 942 |
defaultNoReverse = noReverse; |
| 943 |
defaultAutoRun = autoRun; |
| 944 |
} |
| 945 |
curSelectedIndex = 0; |
| 946 |
ReleaseResource((Handle)arrowsPictHdl); |
| 947 |
arrowsPictHdl = 0L; |
| 948 |
CloseDialog(aDialog); |
| 949 |
} |
| 950 |
SetPort(savePort); |
| 951 |
UpdateLogWindow(); |
| 952 |
|
| 953 |
if (item == okItem) { |
| 954 |
/* save our name, type, and local printer port */ |
| 955 |
BlockMove(&nameStr[1],&ourName[1],nameStr[0]); |
| 956 |
ourName[0] = nameStr[0]; |
| 957 |
BlockMove(&typeStr[1],&ourType[1],typeStr[0]); |
| 958 |
ourType[0] = typeStr[0]; |
| 959 |
BlockMove(&portStr[1],&ourPort[1],portStr[0]); |
| 960 |
ourPort[0] = portStr[0]; |
| 961 |
/* prefix is based on selected printer type */ |
| 962 |
ourPrefix[0] = 3; |
| 963 |
ourPrefix[1] = ourType[1]; /* 'L' or 'I' */ |
| 964 |
ourPrefix[2] = (ourType[2] == 'Q') ? 'Q' : 'W'; |
| 965 |
ourPrefix[3] = '-'; |
| 966 |
/* save for next time (after spool folder is set up) */ |
| 967 |
SavePreferences(); |
| 968 |
} |
| 969 |
return (item == okItem) ? noErr : userCanceledErr; |
| 970 |
} |
| 971 |
|
| 972 |
OSErr StartSpooler(void) |
| 973 |
{ |
| 974 |
OSErr err = noErr; |
| 975 |
if (!defaultAutoRun) { |
| 976 |
err = ConfigureSpooler(true); |
| 977 |
} |
| 978 |
if (err == noErr) { |
| 979 |
char nameStr[256]; |
| 980 |
EntityName name; |
| 981 |
PAPStatusRec stRec; |
| 982 |
name.zoneStr[0] = 1; |
| 983 |
name.zoneStr[1] = '*'; |
| 984 |
BlockMove(ourType, name.typeStr, 33L); |
| 985 |
BlockMove(ourName, name.objStr, 33L); |
| 986 |
BlockMove("\pRegistering", stRec.statusStr, 12L); |
| 987 |
|
| 988 |
err = SLInit(&name, kPAPMaxQuantum, &stRec, &serverRef); |
| 989 |
if (err != noErr) { |
| 990 |
MessageWithIntValue(LOG_ERR_X,"\pRegistering name:",err); |
| 991 |
(void) SLClose(serverRef); |
| 992 |
serverRef = 0; |
| 993 |
} else { |
| 994 |
BlockMove("Registered “", &nameStr[1],12L); |
| 995 |
nameStr[0] = 12; |
| 996 |
BlockMove(&ourName[1],&nameStr[nameStr[0]+1],(long)((short)ourName[0])); |
| 997 |
nameStr[0] += ourName[0]; |
| 998 |
BlockMove("” in local zone", &nameStr[nameStr[0]+1],15L); |
| 999 |
nameStr[0] += 15; |
| 1000 |
Message(LOG_ERR,nameStr); |
| 1001 |
} |
| 1002 |
} |
| 1003 |
return err; |
| 1004 |
} |
| 1005 |
|
| 1006 |
OSErr StopSpooler(void) |
| 1007 |
{ |
| 1008 |
OSErr err = SLClose(serverRef); |
| 1009 |
serverRef = 0; |
| 1010 |
return err; |
| 1011 |
} |
| 1012 |
|
| 1013 |
void DoAbout() |
| 1014 |
{ |
| 1015 |
Message(0,sepStr); |
| 1016 |
Message(0,"\pSimple Spooler is a basic print spooler application."); |
| 1017 |
Message(0,"\pIt presents itself as an AppleTalk printer and will"); |
| 1018 |
Message(0,"\prespond to Printer Access Protocol (PAP) messages."); |
| 1019 |
Message(0,"\pVersion 0.3, Copyright © 2026 Ken McLeod."); |
| 1020 |
Message(0,sepStr); |
| 1021 |
} |
| 1022 |
|
| 1023 |
void DoStatus() |
| 1024 |
{ |
| 1025 |
Message(0,sepStr); |
| 1026 |
MessageWithIntValue(0,"\pSpooler active:",serverRef); |
| 1027 |
MessageWithQuotedString(0,"\pSpooler name:",ourName); |
| 1028 |
MessageWithQuotedString(0,"\pPrinter type:",ourType); |
| 1029 |
MessageWithIntValue(0,"\pColor ribbon:",(defaultColorRibbon)?1:0); |
| 1030 |
MessageWithIntValue(0,"\pSheet feeder:",(defaultSheetFeeder)?1:0); |
| 1031 |
MessageWithIntValue(0,"\pImageWriter 1 mode:",(defaultIW1Mode)?1:0); |
| 1032 |
MessageWithIntValue(0,"\pSkip reverse feed:",(defaultNoReverse)?1:0); |
| 1033 |
MessageWithQuotedString(0,"\pPrint to port:",ourPort); |
| 1034 |
MessageWithIntValue(0,"\pPrinting enabled:",(defaultPrintEnabled)?1:0); |
| 1035 |
MessageWithIntValue(0,"\pScheduled start hour:",schedStartHour); |
| 1036 |
MessageWithIntValue(0,"\pScheduled end hour:",schedEndHour); |
| 1037 |
MessageWithIntValue(0,"\pJobs spooled:",JobsSpooled()); |
| 1038 |
MessageWithIntValue(0,"\pJobs printed:",JobsPrinted()); |
| 1039 |
Message(0,sepStr); |
| 1040 |
} |
| 1041 |
|
| 1042 |
void LoadPreferences() |
| 1043 |
{ |
| 1044 |
char *p; |
| 1045 |
Ptr prefData = 0L; |
| 1046 |
long count = 4L+2L+8+18+256+34+34; |
| 1047 |
OSErr err = CopyDataFromPrefsFile(&count,&prefData); |
| 1048 |
if (err != noErr || !prefData) { return; } |
| 1049 |
if (*((long*)&prefData[0]) != 'pref' || *((short*)&prefData[4]) != kPrefsVersion) { |
| 1050 |
DisposPtr(prefData); |
| 1051 |
return; |
| 1052 |
} |
| 1053 |
defaultPrinterItem = *((char*)&prefData[6]); |
| 1054 |
defaultPortItem = *((char*)&prefData[7]); |
| 1055 |
defaultColorRibbon = *((char*)&prefData[8]); |
| 1056 |
defaultSheetFeeder = *((char*)&prefData[9]); |
| 1057 |
defaultPrintEnabled = *((char*)&prefData[10]); |
| 1058 |
defaultIW1Mode = *((char*)&prefData[11]); |
| 1059 |
schedStartHour = *((char*)&prefData[12]); |
| 1060 |
schedEndHour = *((char*)&prefData[13]); |
| 1061 |
/* window rect in global coordinates */ |
| 1062 |
windowRect = *((Rect*)&prefData[14]); |
| 1063 |
defaultNoReverse = *((char*)&prefData[22]); |
| 1064 |
defaultAutoRun = *((char*)&prefData[23]); |
| 1065 |
/* reserved chars at prefsData[24..31] */ |
| 1066 |
p = (char*)&prefData[32]; |
| 1067 |
BlockMove(p,ourName,p[0]+1); |
| 1068 |
p += p[0]+1; |
| 1069 |
BlockMove(p,ourType,p[0]+1); |
| 1070 |
p += p[0]+1; |
| 1071 |
BlockMove(p,ourPort,p[0]+1); |
| 1072 |
DisposPtr(prefData); |
| 1073 |
|
| 1074 |
/* let PAP module know about the new options */ |
| 1075 |
SetPrinterOptions(defaultColorRibbon,defaultSheetFeeder,defaultIW1Mode,defaultNoReverse); |
| 1076 |
} |
| 1077 |
|
| 1078 |
void SavePreferences() |
| 1079 |
{ |
| 1080 |
char *p, val; |
| 1081 |
Boolean hasColorRibbon, hasSheetFeeder; |
| 1082 |
Boolean iw1Mode, noReverse, printEnabled, autoRun; |
| 1083 |
long length = 4L+2L+8+18+ourName[0]+1+ourType[0]+1+ourPort[0]+1; |
| 1084 |
Ptr prefData = NewPtrClear(length); |
| 1085 |
if (!prefData) { return; } |
| 1086 |
|
| 1087 |
/* offset 0: magic 'pref' tag (4 bytes) */ |
| 1088 |
*((long*)&prefData[0]) = 'pref'; |
| 1089 |
/* offset 4: prefs version (2 bytes) */ |
| 1090 |
*((short*)&prefData[4]) = kPrefsVersion; |
| 1091 |
/* offset 6: selected printer item */ |
| 1092 |
val = iconIWItem; |
| 1093 |
if (ourType[1] == 'L') { val = (ourType[2] == 'Q') ? iconLQItem : iconLWItem; } |
| 1094 |
*((char*)&prefData[6]) = val; |
| 1095 |
/* offset 7: selected printer port item */ |
| 1096 |
val = (ourPort[1] == 'M') ? modemPortItem : printerPortItem; |
| 1097 |
*((char*)&prefData[7]) = val; |
| 1098 |
/* offset 8, 9: boolean values of Color Ribbon and Sheet Feeder */ |
| 1099 |
GetPrinterOptions(&hasColorRibbon,&hasSheetFeeder,&iw1Mode,&noReverse); |
| 1100 |
*((char*)&prefData[8]) = (hasColorRibbon) ? -1 : 0; |
| 1101 |
*((char*)&prefData[9]) = (hasSheetFeeder) ? -1 : 0; |
| 1102 |
/* offset 10, 11: boolean values of Print Enabled and IW1 Mode */ |
| 1103 |
printEnabled = defaultPrintEnabled; |
| 1104 |
*((char*)&prefData[10]) = (printEnabled) ? -1 : 0; |
| 1105 |
iw1Mode = defaultIW1Mode; |
| 1106 |
*((char*)&prefData[11]) = (iw1Mode) ? -1 : 0; |
| 1107 |
/* offset 12: scheduled start hour for printing (1 byte) */ |
| 1108 |
*((char*)&prefData[12]) = (char)(schedStartHour & 0xFF); |
| 1109 |
/* offset 13: scheduled end hour for printing (1 byte) */ |
| 1110 |
*((char*)&prefData[13]) = (char)(schedEndHour & 0xFF); |
| 1111 |
/* offset 14-15:top, 16-17:left, 18-19:bottom, 20-21:right (window rect) */ |
| 1112 |
*((Rect*)&prefData[14]) = ((GrafPtr)textWindow)->portRect; |
| 1113 |
LocalToGlobal((Point*)&prefData[14]); |
| 1114 |
LocalToGlobal((Point*)&prefData[18]); |
| 1115 |
/* offset 22, 23: boolean values of No Reverse and Auto Run */ |
| 1116 |
*((char*)&prefData[22]) = (noReverse) ? -1 : 0; |
| 1117 |
autoRun = defaultAutoRun; |
| 1118 |
*((char*)&prefData[23]) = (autoRun) ? -1 : 0; |
| 1119 |
/* reserved chars from [24..31] */ |
| 1120 |
p = (char*)&prefData[32]; |
| 1121 |
BlockMove(ourName,p,ourName[0]+1); |
| 1122 |
p += ourName[0]+1; |
| 1123 |
BlockMove(ourType,p,ourType[0]+1); |
| 1124 |
p += ourType[0]+1; |
| 1125 |
BlockMove(ourPort,p,ourPort[0]+1); |
| 1126 |
|
| 1127 |
(void)SaveDataToPrefsFile(length,prefData); |
| 1128 |
DisposPtr(prefData); |
| 1129 |
} |