TweetFollow Us on Twitter

Forth Edit Task
Volume Number:2
Issue Number:11
Column Tag:Forth Forum

An Edit Task for Forth

By Jörg Langowski, MacTutor Editorial Board, Juri Munkki, Helsinki, Finland

An Editor Task For Mach2

By Juri Munkki, with Jörg Langowski

[Since this column is the Forth Forum, we must give others space for contributions. This month we received an article by one of our readers, Juri Munkki from Helsinki, who devised a rather practical text editor to run under Forth. Evidently they lack a desk accessory editor like MockPackage+ which will run with HFS. With the multi-tasking Mach2, an editor task helps a lot in developing programs; I had the same problems as Juri trying to use Edit 2.0 in connection with Mach2. Mach2 and Edit just don't want to run together under Switcher - even with the cache switched off - on a Mac Plus. Also, Edit is overkill for Forth development since individual program pieces tend to be rather short.

The 32K text size limitation for this editor is therefore no big drawback; in fact, it is more than the 28K of MockWrite. Read the code carefully for some interesting additions to Mach2, like ON, OFF, ANEW, scrap handling words, an on-window palette menu and auto-indentation on text entry.

Enough of a foreword, I'll pass the keyboard to Juri now. J.L.]

Editing a Problem in Mach 2

Mach 2 is a great language, but it certainly has some flaws. The first one that every programmer confronts is the lack of an integrated editor.

Palo Alto Shipping suggests that Edit and Forth are used with Switcher, but I don't like Edit and and it takes most of my 512K Mac's memory to hold Mach I, Edit and Switcher. The first thing that came to my mind was MockWrite. MockWrite is a very good text editor DA, but the versions everybody have here in Finland trash files under HFS. (I had a lot of fun finding that out.)

I didn't know of any other DA editors when I got Mach I, so I thought writing a small editor would not take too much time.

I still had to find a working combination in order to be able to write the editor. The best solution I found, was to use Edit and Mach without the Switcher. In Edit, I could use "Other" to launch Forth and in Mach, I used an FKEY to launch Edit. I modified the menu in Edit so that I could switch programs with a command key combination. With the cache on, it wasn't unbearably slow, but it wasn't very interactive.

I have done most of my Macintosh programming in MacForth because it was the first good language available. Mach I is different, but learning it was easy since the manual is good and I already knew the toolbox. I wouldn't recommend Mach I to a beginner, but it is very good as a second language. I still miss many of the nice little commands that MacForth has.

I extended Mach I with some MacForth words that I thought would be useful. Anew was probably the most important. In.Heap would also have been nice, but HeapVar was the best I could do with my knowledge of Forth. Heapvar is used to return the heap space that is pointed to by the handle in a variable. I also added RECT, !RECT, ON and OFF. On and Off are very simple, but they make code much more readable and speed up programs.

Example of "HeapVar":

HeapVar A.Variable
Anew MyProgram
Variable A.Variable
 
100 Call NewHandle Drop A.Variable !
 

The editor uses the ROM TextEdit engine and is thus limited to a maximum of 64K of text or 32767 lines. I have limited the editor to 32K of text in order to avoid checking for the maximum amount of lines. The commented source code of TEDDY is about 25K. This limits the amount of source code to 32K, but code segments are not allowed to be larger than 32K.

Teddy is not the first Editor I have written. I wrote a similar program in MacForth when I thought it would be a nice addition to my terminal program. The MacForth version of Teddy is even simpler than MachTeddy, but the experience helped me avoid some bugs and problems.

The program conforms closely to the Macintosh user interface guidelines. The only nonstandard thing I can think of is the lack of an I-beam cursor. I forgot the cursor at first, but when I thought of it, it no longer seemed worth adding. This is a programmer's tool, after all. My MacForth Teddy has an I-Beam cursor, since it is used by non-programmers who expect it.

The comments in the program demonstrate the things that have been less documented in MacTutor. Scrap handling is heavily documented because the program does it 100% according to Inside Macintosh and it has not been discussed in MacTutor. Inside Macintosh gives very specific rules on scrap management.

The scrap is usually held in two places. The desk accessories expect it to be in the desk scrap and every program usually has its own clipboard format. Text Edit has two global variables to handle the clipboard. The TE-scrap must be converted to the global scrap after a cut or paste has been made.

Teddy avoids useless scrap conversion. If a program first converts the scrap to text and then puts it back without a change, all others' formats are lost. Consider a situation where a user has three programs under Switcher and two of these understand pictures. If a picture is copied and then the user switches to the other program, by first switching to the text application, the text application should not delete the picture just because it doesn't like pictures. Destroy the desk scrap only when you have to change it!

Teddy keeps a flag to monitor changes in the text edit scrap. If a cut or copy is made, the flag is set and the scrap will be converted when the window is deactivated. Desk scrap is always moved to the text edit scrap if the editor window is activated.

Menus are a very good invention, but sometimes a palette on a window is easier and faster to use. My first experiment with palettes was with the MacForth version of Teddy. I have used them since then on many of my programs. It would be very easy to add keyboard equivalents to these functions, but Teddy has none. A palette is very simple to implement: it is a collection of boxes that act like buttons. I first tried to use a collection of buttons, but a button takes too much space.

Teddy scrolls automatically if the user tries to type outside the screen. The selection range is used to determine if the screen should be scrolled. Cursor keys work well, because the person who wrote Text Edit was clever enough to implement them.

Clickloop procedures are very hard to do in Forth, so I wrote the scroll routine in assembler. The clickloop routine changes the setting of the scroll bar, but the changes are not drawn during the scrolling because Text Edit sets the clipping rectangle to the text edit view rectangle. If you want to be able to monitor the selection from the scroll bar, insert a few toolbox calls to change the clipping rectangle.

The Macintosh File Manager is easy to use (once you figure out how), but requires a large amount of initialization. I haven't figured how to use the Mach 2 file system, so it wasn't really hard to write the routines in "pure toolbox" instead of Forth. Note that HFS is supported simply by using the volrefnum from the sfreply and putting it in the parmblock. The current (last used) filename and volrefnum are remembered for later use. Loading and saving stop multitasking.

The "Enter" key is useless in MacWrite, but in this editor it provides an indenting alternative to the "Return" key. You might want to switch the meaning of return and enter. Enter looks at the line you are on, adds a return and a few spaces to align the new line with the previous line. Tab generates four spaces.

When I write programs, I use a workspace that contains Teddy and some extensions. The resources (DLOG, ALRT, DITL, PICT) are in the Mach 2 file because that way they aren't deleted when the workspace is updated. Teddy should be snapshotted before it is called, or the workspace will not work.

Teddy might serve as a basis for creating more specialized editors. You may use this source code in any program you write, but it would be nice to mention me in the "About" dialog.

I usually edit a program, save it and load it from a file because this forces me to make constant backups to the disk. Even a very small cache is enough to keep the file in RAM and compiling is just as fast as compiling from the clipboard.

Let's program!

Installation Notes - J.L.

To install the editor in your Mach2 (or Mach1) system, you should create a Workspace immediately after loading the file by either saying workspace ted or new-segment ted (the latter leaving a full 32K of code space after the editor has been invoked). Make sure the workspace file is on the same disk as the copy of Mach2 that it is run with, otherwise the workspace won't work.

You can then, as usual, click the workspace icon and call the editor from within Mach by typing ted. The resources used by the editor should be in the Mach file (see above). Listing 2 shows an Rmaker input to create those resources, which can then be pasted with ResEdit. The PICT ID=900 is not printed here. However, it is on the resource file Teddy.rsrc on our source code disk. I'd recommend you to get that disk anyway instead of typing in all the code.

Listing 1: Teddy, A Text Editor Task for Mach1.
( Teddy -- Text Editor )

( Contains MacForth-like extensions to Mach I in addition to
 the editor. Type TED to call the editor at any time. The 
 MacForth-style extensions are mostly undocumented here.
 Look for examples in this source. )

( Anew:
  Used in the form: ANEW PROGRAM_NAME. It tries to find the
 PROGRAM_NAME and forget it if it is found. It then creates
 PROGRAM_NAME and continues. It should be used in the
 beginning of the program. Old versions are then automatically
 forgotten, if they exist. )

ONLY FORTH DEFINITIONS 
ALSO MAC ALSO ASSEMBLER

: ANEW { | LEN }
  32 WORD DUP C@ 1+ NEGATE -> LEN
  FIND SWAP DROP
   IF LEN >IN +! FORGET CALL DRAWMENUBAR THEN
 LEN >IN +!
CREATE DOES> DROP
;

( Heapvar:
 Used in the form: HEAPVAR VARIABLE_NAME. If
 VARIABLE_NAME exists, it returns the handle from
 VARIABLE_NAME to the heap. It should be used before
 ANEW to free space from the heap. )

: HEAPVAR
  32 WORD
  FIND IF LINK>BODY EXECUTE 
     @ DUP 
     IF DUP CALL HUNLOCK DROP
            CALL DISPOSHANDLE DROP ELSE DROP THEN
     ELSE DROP
   THEN
;

: RECT  
  CREATE  SWAP 2SWAP SWAP   W, W, W, W,
;

GLOBAL
 CODE !RECT
    MOVE.L    (A6)+,A0
    MOVE.W    14(A6),(A0)+
    MOVE.W    10(A6),(A0)+
    MOVE.W     6(A6),(A0)+
    MOVE.W     2(A6),(A0)+
    ADDA.L    #16,A6
    RTS
END-CODE

GLOBAL
 CODE OFF
    MOVEA.L   (A6)+,A0
    CLR.L    (A0)
    RTS
END-CODE
MACH

GLOBAL
 CODE ON
    MOVEA.L   (A6)+,A0
    MOVE.L    #-1,(A0)
    RTS
END-CODE

GLOBAL
 CODE SCALE
    MOVE.L   (A6)+,D0
    BMI.S    @1
    MOVE.L   (A6),D1
    ASL.L    D0,D1
    MOVE.L   D1,(A6)
    RTS
@1  MOVE.L   (A6),D1
    NEG.L    D0
    ASR.L    D0,D1
    MOVE.L   D1,(A6)
    RTS
END-CODE

GLOBAL
 CODE @MOUSE
    SUBQ.L    #4,A6
    MOVE.L    A6,-(A7)
    _GETMOUSE
    RTS
END-CODE

HEADER TEDDY.W2 DC.L    0
HEADER TEDDY.T2 DC.L    0
HEADER TEDDY.S2 DC.L    0
 CODE CLICKPROC
    MOVEM.L   D1-D3/A0-A4,-(A7)
    CLR.L     -(A7)
    MOVE.L    A7,-(A7)
    _GETMOUSE            ( Where is the mouse cursor? )
    MOVE.L    (A7)+,D0
    SWAP.W    D0         ( Get the Y-location to D0.W  )
    CMP.W     #18,D0     ( Is Mouse.Y smaller than 18? )
    BLT.S     @1
    MOVE.L    TEDDY.W2,A0
    MOVE.W    20(A0),D1
    SUB.W     #16,D1
    CMP.W     D1,D0      ( Is Mouse.Y below the text? )
    BGE.S     @2
@4  MOVEM.L   (A7)+,D1-D3/A0-A4
    MOVEQ.L   #1,D0
@3  RTS

@1  CLR.W     -(A7)      ( Are we allowed to scroll down? )
    MOVE.L    TEDDY.S2,-(A7)
    _GETCTLVALUE
    MOVE.W    (A7)+,D0
    BEQ.S     @4         ( If we are on top, do nothing )
    SUBQ.W    #1,D0      ( Scroll one line up )
    MOVE.L    TEDDY.S2,-(A7)
    MOVE.W    D0,-(A7)
    _SETCTLVALUE
    CLR.W     -(A7)
    MOVE.W    #11,-(A7)  ( One line = 11 pixels )
    MOVE.L    TEDDY.T2,-(A7)
    _TESCROLL            ( Scroll the text )
    MOVEM.L   (A7)+,D1-D3/A0-A4
    MOVEQ.L   #1,D0
    RTS
    
@2  CLR.W     -(A7)
    MOVE.L    TEDDY.S2,-(A7)
    _GETCTLVALUE         ( Where are we? )
    MOVE.W    (A7)+,D3
    CLR.W     -(A7)
    MOVE.L    TEDDY.S2,-(A7)
    _GETMAXCTL           ( How high can we go? )
    MOVE.W    (A7)+,D0
    CMP.W     D0,D3
    BGE.S     @4
    ADDQ.W    #1,D3      ( Scroll one line... )
    MOVE.L    TEDDY.S2,-(A7)
    MOVE.W    D3,-(A7)
    _SETCTLVALUE
    CLR.W     -(A7)
    MOVE.W    #-11,-(A7)
    MOVE.L    TEDDY.T2,-(A7)
    _TESCROLL
    MOVEM.L   (A7)+,D1-D3/A0-A4
    MOVEQ.L   #1,D0
    RTS
END-CODE

( The following routine is quite simple. All it does is search a string 
for another one ignoring case and it returns the offset or a flag. )

CODE FINDER   
 ( ?STR ?LEN SEARCHSTR SEARCHLEN -- OFFSET )
    MOVEM.L   D0-D7/A0-A4,-(A7)
    MOVE.L    (A6)+,D0
    MOVE.L    (A6)+,A0
    MOVE.L    (A6)+,D1
    MOVE.L    (A6)+,A1
    MOVE.W    D0,D2
    SUB.W     D1,D2
    CLR.L     D7
@1  CLR.W     D3
@2  MOVE.B    0(A0,D3.W),D4
    BMI.S     @3
    CMP.B     #96,D4
    BLT.S     @3
    SUB.B     #32,D4   ( Remove case )
@3  MOVE.B    0(A1,D3.W),D5
    BMI.S     @4
    CMP.B     #96,D5
    BLT.S     @4
    SUB.B     #32,D5   ( Remove case )
@4  CMP.B     D4,D5    ( Is a char equal to another? )
    BNE.S     @5
    ADDQ.W    #1,D3    ( It was, one match )
    CMP.W     D1,D3    ( Have we found the string? )
    BLT.S     @2
    MOVE.L    D7,-(A6)
    MOVEM.L   (A7)+,D0-D7/A0-A4
    RTS

@5  ADDQ.L    #1,A0    ( No match...yet )
    ADDQ.L    #1,D7
    DBRA      D2,@1    ( Look again? )
    MOVE.L    #-1,-(A6) ( No match...return -1 )
    MOVEM.L   (A7)+,D0-D7/A0-A4
    RTS
END-CODE

( 4ASCII nnnn converts the 4 character string into its numeric
 value. It can only be used in the immediate mode. Examples
 below )

: 4ASCII 
 0
4 0 DO
  8 SCALE 0 WORD 1+ C@ + 
LOOP
;

ONLY FORTH ALSO MAC
4ASCII TEXT CONSTANT "TEXT
4ASCII DRVR CONSTANT DRIVER
4ASCII MACA CONSTANT "MACA
  HEX AB0 CONSTANT TESCRAP.LEN  
 ( Global TeEdit private scrap variables )
            AB4 CONSTANT TESCRAP.HANDLE DECIMAL


                   NEW.WINDOW TEDDY.W
               " Text Editor" TEDDY.W TITLE
                 50 0 304 480 TEDDY.W BOUNDS
ZOOM VISIBLE CLOSEBOX GROWBOX TEDDY.W ITEMS

            400 4000 TERMINAL TEDDY.TASK

NEW.MBAR TEDDY.BAR

900 CONSTANT APPLEID
NEW.MENU APPLEMENU
    HERE 1 C, 20 C, APPLEMENU TITLE
" About Edit...;(-" APPLEMENU ITEMS   ( Add DAs later )
          0 APPLEID APPLEMENU BOUNDS
        
               901 CONSTANT TFILEID
                   NEW.MENU TFILE
                    " File" TFILE TITLE
" Open/O;Save/S;Save as..." TFILE ITEMS
                  0 TFILEID TFILE BOUNDS

           902 CONSTANT TEDITID
               NEW.MENU TEDITMENU
                " Edit" TEDITMENU TITLE
" Cut/X;Copy/C;Paste/V;Select All & Copy;-(;Find/F;Again/G("
                        TEDITMENU ITEMS
              0 TEDITID TEDITMENU BOUNDS
              
: ADD.DRVRS ( Add desk accessories )
 APPLEMENU @ DRIVER CALL ADDRESMENU
;

             NEW.CONTROL TEDDY.SB
VSCROLLBAR VISIBLE 100 0 TEDDY.SB ITEMS

: DAHANDLER { ITEM | Daname }
 ITEM 2 > IF                 ( We must open a desk accessory )
     256 CALL NEWPTR -> DANAME    
 ( Get us a STR255 for the name )
     APPLEMENU @ ITEM DANAME CALL GETITEM
     DANAME CALL OPENDESKACC DROP 
 ( Open the desk accessory )
     DANAME CALL DISPOSPTR        ( Give the String back )
     ELSE
     ITEM 1 = 
( The about edit alert should be shown. The resource must
  be added separately to Mach 2. )
       IF 900 0 CALL ALERT DROP  THEN THEN
;
HEX  44 CONSTANT txFont ( Offsets in a window record )
     46 CONSTANT txFace
     48 CONSTANT txMode
     4A CONSTANT txSize
     6C CONSTANT WindowKind DECIMAL

VARIABLE TEDDY.T     ( PLACEHOLDER FOR TEXT HANDLE )
VARIABLE ACTIVE?     ( ACTIVE FLAG                 )
VARIABLE MUSTCONVERT ( SCRAP CONVERSION FLAG       )
 20 CONSTANT UPARROW ( Part codes )
 21 CONSTANT DOWNARROW
 22 CONSTANT PAGEUP
 23 CONSTANT PAGEDOWN
129 CONSTANT THUMB
 
VARIABLE CURMAX     ( Current scroll bar range   )
VARIABLE CURSET     ( Current scroll bar setting )
: CORRECT.CONTROL.RANGE
 CURMAX @ TEDDY.T @ @ 94 + W@ 1- 0 MAX DUP CURMAX !
   = NOT
  IF TEDDY.SB @ CURMAX @ CALL SETMAXCTL THEN
;
: CORRECT.CONTROL   ( Set scroll bar )
 CURSET @ ( Look at the destination RECT for the position )
 18 TEDDY.T @ @ W@ L_EXT - 11 / DUP CURSET ! = NOT
 IF TEDDY.SB @ CURSET @ CALL SETCTLVALUE THEN
;
: TOO.HIGH.TEDDY    ( Autoscroll, when typing )
 0 TEDDY.W 20 + W@ 40 - TEDDY.T @ @ 
 16 + W@ L_EXT - DUP 11 MOD -
 ?DUP IF ( If tescroll is called with 0 0, the caret disappears! )
 TEDDY.T @ CALL TESCROLL
  ELSE DROP THEN
 CORRECT.CONTROL
;
: TOO.LOW.TEDDY   ( Autoscroll, when typing )
 0 29 TEDDY.T @ @ 16 + W@ L_EXT - DUP 11 MOD - 
 TEDDY.T @ CALL TESCROLL
 CORRECT.CONTROL
;
: CORRECTSCROLL 
 ( If the user is typing, check if we should scroll. )
 TEDDY.T @ @ 32 + DUP W@ SWAP 2+ W@ = 
 ( Do we have a caret? )
  IF TEDDY.T @ @ 16 + W@ L_EXT 29 < IF TOO.LOW.TEDDY  ELSE
     TEDDY.T @ @ 16 + W@ L_EXT
     TEDDY.W 20 + W@ 40 - >         IF TOO.HIGH.TEDDY THEN
   THEN
  THEN 
;
CREATE TEMR 8 ALLOT   ( Temporary storage ) 
: SCRAP->TE           ( Convert global scrap to TeScrap )
 0 "TEXT TEMR CALL GETSCRAP 0>         ( Is there text? )
  IF TESCRAP.HANDLE @ "TEXT TEMR CALL GETSCRAP
 TESCRAP.LEN W! THEN
  MUSTCONVERT OFF     
 ( The scrap does not have to be converted just now )
;
: TE->SCRAP
 MUSTCONVERT @       
  ( Are there any changes after SCRAP->TE? )
  IF
  CALL ZEROSCRAP DROP 
 ( Zero scrap to clear non-text entries )
  TESCRAP.LEN W@ "TEXT TESCRAP.HANDLE @ @ 
 CALL PUTSCRAP DROP
  THEN
;
: CLEAR.TESCRAP     
 ( Word used to clear tescrap when it is not needed )
 TESCRAP.HANDLE @ 0 CALL SETHANDLESIZE DROP
 0 TESCRAP.LEN W!
;

VARIABLE OLDPORT  
  ( Used to save the current window before a dialog )
CREATE DLOG900 0 ,  ( Handle storage for our "FIND" dialog )
VARIABLE DEVENT     ( Dialog "event" )

( You can set the following strings from Forth and then use
 "aGain"  to replace any untypeable characters. 
 Do a find or replace, then set teddy.f1 and f2 and choose
 "aGain". This will do the previous operation with the new
 strings! )

CREATE TEDDY.F1 256 ALLOT ( String to find )
CREATE TEDDY.F2 256 ALLOT ( Replace string )
( The following part finds Teddy.F1 from the text )
: TFIND.REALLY { | SELEND STRSTART }
 TEDDY.T @ @ 34 + W@    -> SELEND
 TEDDY.T @ @ 62 + @ @   -> STRSTART
 TEDDY.F1 COUNT ?DUP 
IF 
 STRSTART SELEND +
 TEDDY.T @ @ 60 + W@ SELEND -
 DUP TEDDY.F1 C@ > 
IF
 FINDER DUP
 0< IF    DROP 10 CALL SYSBEEP 0 0 TEDDY.T @ 
 CALL TESETSELECT
      ELSE
         SELEND + TEDDY.F1 C@ + DUP TEDDY.T @ 
 CALL TESETSELECT
      THEN
ELSE 2DROP 2DROP 10 CALL SYSBEEP 0 0 TEDDY.T @
  CALL TESETSELECT 
THEN
  CORRECTSCROLL 
ELSE DROP 
THEN
;
( The following finds Teddy.F1 and replaces it with Teddy.F2 )
: TEDDY.REPLACE { | SELEND STRSTART }
 TEDDY.T @ @ 34 + W@        -> SELEND
 TEDDY.T @ @ 62 + @ @       -> STRSTART
 TEDDY.F1 COUNT ?DUP 
IF 
 STRSTART SELEND +
 TEDDY.T @ @ 60 + W@ SELEND -
 DUP TEDDY.F1 C@ > 
IF
 FINDER DUP
 0< IF    DROP 10 CALL SYSBEEP 0 0 TEDDY.T @ 
 CALL TESETSELECT
      ELSE DUP
          SELEND + TEDDY.F1 C@ OVER + TEDDY.T @ 
 CALL TESETSELECT
        TEDDY.T @ CALL TEDELETE
        TEDDY.F2 COUNT TEDDY.T @ CALL TEINSERT
        SELEND + TEDDY.F2 C@ + DUP TEDDY.T @ 
 CALL TESETSELECT
      THEN
ELSE 2DROP 2DROP 10 CALL SYSBEEP 0 0 TEDDY.T @
  CALL TESETSELECT 
THEN
 CORRECTSCROLL 
ELSE DROP 
THEN
;  
: TEDDYFIND.SUB ( Find or replace according to button )
 DEVENT W@ CASE
  1 OF TFIND.REALLY         ENDOF
  2 OF TEDDY.REPLACE        ENDOF
  ENDCASE
;
: TEDDYFIND
TE->SCRAP 
 ( Forth receives an activate when the dialog is gone. )
 ( The scrap must be saved to preserve it. )
TEDDY.T @ CALL TEDEACTIVATE
DLOG900 @ 0= 
IF 900 0 -1  CALL GETNEWDIALOG DLOG900 !
 ELSE DLOG900 @ CALL BRINGTOFRONT DLOG900 @ 
 CALL SHOWWINDOW 
THEN
 OLDPORT   CALL GETPORT
 DLOG900 @ CALL SETPORT      ( Set the dialog port )
  BEGIN
   0 DEVENT CALL MODALDIALOG 
 ( Call this until the user has finished )
  DEVENT W@ 4 < 
 UNTIL
OLDPORT @ CALL SETPORT      
 ( Reset "predialog" environment )
DLOG900 @ 5 PAD PAD 4 + PAD 8 + CALL GETDITEM
PAD 4 + @ TEDDY.F1 CALL GETITEXT         ( Set Teddy.F1 )
DLOG900 @ 6 PAD PAD 4 + PAD 8 + CALL GETDITEM
PAD 4 + @ TEDDY.F2 CALL GETITEXT         ( Set Teddy.F2 )
DLOG900 @ CALL HIDEWINDOW
TEDDY.T @ CALL TEACTIVATE
TEDITMENU @ 7 CALL ENABLEITEM
TEDDYFIND.SUB
;
( Handle Cut/Copy/Paste and others for Teddy and DAs )
: TEDITHANDLER { ITEM }
 CALL FRONTWINDOW TEDDY.W = 
IF ( Editor cut/paste )
ITEM  CASE
 1 OF TEDDY.T @ CALL TECUT    MUSTCONVERT ON ENDOF
 2 OF TEDDY.T @ CALL TECOPY   MUSTCONVERT ON ENDOF
 3 OF TESCRAP.LEN W@
          TEDDY.T @ @ 60 + W@
          TEDDY.T @ @ 34 + W@ TEDDY.T @ @ 32 + W@ - -
              + 32767 < IF
           TEDDY.T @ CALL TEPASTE
          ELSE 5 CALL SYSBEEP 5 CALL SYSBEEP THEN ENDOF
 4 OF 0 TEDDY.T @ @ 60 + W@ TEDDY.T @ 
 CALL TESETSELECT
         TEDDY.T @ CALL TECOPY MUSTCONVERT ON ENDOF
  6 OF TEDDYFIND ENDOF
  7 OF TEDDYFIND.SUB ENDOF
ENDCASE
    CORRECT.CONTROL.RANGE
    CORRECTSCROLL
ELSE ( DA cut/copy/paste...Undo is left for you to add... )
     CALL FRONTWINDOW
     WINDOWKIND + W@ L_EXT 0< 
 IF ITEM  4 < 
 IF ITEM 1+  CALL SYSEDIT  DROP THEN
    THEN
THEN
;

ALSO ASSEMBLER
( Here we have support for SFGETFILE and SFPUTFILE; these
 routines are similar to the ones in the Mach 2 manual. )
HEADER TYPES    DC.B    'TEXT'
HEADER GPROMPT  DC.B    20
                DC.B    'Please select a file'
HEADER PPROMPT  DC.B    18
                DC.B    'Please type a name'
 CODE TEDDYGETFILE
    MOVE.W    #50,-(A7)
    MOVE.W    #50,-(A7)
    PEA        GPROMPT
    CLR.L    -(A7)
    MOVE.W    #1,-(A7)
    PEA        TYPES
    CLR.L    -(A7)
    MOVE.L    (A6)+,-(A7)
    MOVE.W    #2,-(A7)
    _PACK3
    RTS
END-CODE
 CODE TEDDYPUTFILE
    MOVE.W    #50,-(A7)
    MOVE.W    #50,-(A7)
    PEA     PPROMPT
    MOVE.L    (A6)+,-(A7)
    CLR.L    -(A7)
    MOVE.L    (A6)+,-(A7)
    MOVE.W    #1,-(A7)
    _PACK3
    RTS
END-CODE
ONLY FORTH ALSO MAC ALSO TALKING
230 USER PARMBLK
CREATE FNAME 0 C, 63 ALLOT 
( Our file has a name. This is where it is kept)
CREATE FPLACE 0 ,          ( This is the folder of our file. HFS! )
( Here we do some "dirty" programming. I use the file manager
 directly. This works, but the code is not very clear. Once the
 PARaMeterBLocK is set, it doesn't need to be changed much.
 Read Inside Macintosh  for details on parameter blocks and
 the file system. )
: TEDDYLOAD ( Replace selection range with a file )
 TE->SCRAP
 PAD TEDDYGETFILE ( Use PAD as SFREPLY )
 PAD C@ IF  PAD 10 + FNAME 64 CMOVE
            PAD 6  + W@ FPLACE !
                       PARMBLK 12 + OFF
            PAD 10 +   PARMBLK 18 + !
            PAD 6 + W@ PARMBLK 22 + W!
            0          PARMBLK 26 + W!
                       PARMBLK 28 + OFF
            PARMBLK CALL OPEN 
             IF 10 CALL SYSBEEP ( Ouch! File Error )
              ELSE
              PARMBLK CALL GETEOF DROP
              PARMBLK 28 + @
              TEDDY.T @ @ 60 + W@
              TEDDY.T @ @ 34 + W@ TEDDY.T @ @ 32 + W@ - -
              + 32767 < ( Does the result fit? )
                IF TESCRAP.HANDLE @ DUP DUP
                   PARMBLK 28 + @ CALL SETHANDLESIZE DROP
                   CALL GETHANDLESIZE TESCRAP.LEN W!
                   CALL HLOCK DROP
                   TESCRAP.HANDLE @ @ PARMBLK 32 + !
                   TESCRAP.LEN   W@   PARMBLK 36 + !
                   0 PARMBLK 44 + W!
                   0 PARMBLK 46 + !
                   PARMBLK CALL READ DROP
                   TESCRAP.HANDLE @ CALL HUNLOCK DROP
                   PARMBLK CALL CLOSE DROP
                   TEDDY.T @ CALL TEPASTE
                   CORRECT.CONTROL.RANGE
                   CORRECTSCROLL
                ELSE 5 CALL SYSBEEP 5 CALL SYSBEEP 
 ( Ouch! Text too long )
                THEN
            THEN
THEN
;
: TEDDYSAVE ( Save selection range )
 TE->SCRAP
 PAD FNAME TEDDYPUTFILE
 PAD C@ IF  PAD 10 + FNAME 64 CMOVE
            PAD 6  + W@ FPLACE !
                       PARMBLK 12 + OFF
            PAD 10 +   PARMBLK 18 + !
            PAD 6 + W@ PARMBLK 22 + W!
            0          PARMBLK 26 + W!
                       PARMBLK 28 + OFF
            PARMBLK CALL CREATE DROP
            PARMBLK CALL OPEN   DROP
            TEDDY.T @ @ 34 + W@ TEDDY.T @ @ 32 + W@ -
            ?DUP 0= IF TEDDY.T @ @  60 + W@ THEN
            PARMBLK 28 + !
            PARMBLK CALL SETEOF DROP
            TEDDY.T @ @ 34 + W@ TEDDY.T @ @ 32 + W@ -
            ?DUP 0= IF
              TEDDY.T @ @ 60 + W@ PARMBLK 36 + !
              TEDDY.T @ @ 62 + @ @ PARMBLK 32 + !
              ELSE 
                                  PARMBLK 36 + !
              TEDDY.T @ @ 62 + @ @ TEDDY.T @ @ 32 + W@ +
                                  PARMBLK 32 + !
              THEN
             0 PARMBLK 44 + W! PARMBLK 46 + OFF
             PARMBLK CALL WRITE  
             PARMBLK CALL FLUSHFILE DROP
             PARMBLK CALL CLOSE DROP
              IF 10 CALL SYSBEEP
                 PARMBLK CALL DELETE
             ELSE 
                 PARMBLK CALL GETFILEINFO DROP
                 "TEXT PARMBLK 32 + ! ( Text filestype TEXT! )
                 "MACA PARMBLK 36 + ! ( MacWrite files )
                 PARMBLK CALL SETFILEINFO DROP 
             THEN
             PARMBLK 18 + OFF
             PARMBLK CALL FLUSHVOL DROP
     THEN
;
: TEDDYSAVEALL ( Save the whole file )
TEDDY.T @ @ 60 + W@ IF
TE->SCRAP
FNAME C@ IF 
                        PARMBLK 12 + OFF
            FNAME      PARMBLK 18 + !
            FPLACE @   PARMBLK 22 + W!
            0          PARMBLK 26 + W!
                       PARMBLK 28 + OFF
            PARMBLK CALL CREATE DROP
            PARMBLK CALL OPEN   DROP
            TEDDY.T @ @  60 + W@ PARMBLK 28 + !
            PARMBLK CALL SETEOF DROP
              TEDDY.T @ @ 60 + W@ PARMBLK 36 + !
              TEDDY.T @ @ 62 + @ @ PARMBLK 32 + !
             0 PARMBLK 44 + W! PARMBLK 46 + OFF
             PARMBLK CALL WRITE  
             PARMBLK CALL FLUSHFILE DROP
             PARMBLK CALL CLOSE DROP
              IF 10 CALL SYSBEEP
                 PARMBLK CALL DELETE
             ELSE 
                 PARMBLK CALL GETFILEINFO DROP
                 "TEXT PARMBLK 32 + !
                 "MACA PARMBLK 36 + ! 
                 PARMBLK CALL SETFILEINFO DROP 
             THEN
             PARMBLK 18 + OFF
             PARMBLK CALL FLUSHVOL DROP
     THEN THEN
;
: TFILEHANDLER ( Handle the file menu )
 CASE 1 OF 0 TEDDY.T @ @ 60 + W@
           TEDDY.T @ CALL TESETSELECT
           TEDDYLOAD                ENDOF
      2 OF TEDDYSAVEALL             ENDOF
      3 OF TEDDY.T @ @ 32 + @
           0 TEDDY.T @ @ 32 + !
           TEDDYSAVE
           TEDDY.T @ @ 32 + !       ENDOF
ENDCASE
;
: TEDDYMENUS ( Menu events are delivered here )
 0 CALL HILITEMENU
 CASE
   APPLEID OF DAHANDLER         ENDOF
   TFILEID OF TFILEHANDLER      ENDOF
   TEDITID OF TEDITHANDLER      ENDOF
  ENDCASE 
;
( This program has a menu on its window. There are 5 items on
 this menu and the names of these items have to be kept
 somewhere. This was a simple way to create an array of
 strings. )
: TITLES
 CASE 0 OF " Select All"      ENDOF
      1 OF " Select Forward"  ENDOF
      2 OF " Select Backward" ENDOF 
      3 OF " Copy From Disk"  ENDOF
      4 OF " Save Selection"  ENDOF ENDCASE
;
: DRAWTITLES     ( Draw palette items )
PAD CALL GETPORT  ( Get our window )
PAD @ TXFONT + W@ ( Save text characteristics )
PAD @ TXSIZE + W@ 
PAD @ TXMODE + W@
1 CALL TEXTFONT   ( Geneva )
9 CALL TEXTSIZE   ( 9 point )
1 CALL TEXTMODE
 5 0 DO           ( 5 items in our palette )
   2 I 90 * 2+ 15 OVER 91 + TEMR !RECT 
 TEMR CALL ERASERECT
 TEMR CALL FRAMERECT
   I 90 * 47 +
   I TITLES CALL STRINGWIDTH 2/ - ( Center the string )
   12 CALL MOVETO I TITLES CALL DRAWSTRING
 LOOP
 CALL TEXTMODE ( Reset text charasteristics )
 CALL TEXTSIZE      
 CALL TEXTFONT
;
168 USER UPDATE-HOOK   ( Mach 2 has a lot of stupid hooks )
152 USER CONTENT-HOOK  ( I have to live with them )
172 USER ACTIVATE-HOOK ( even if I do not like them )

CREATE SPORT 4 ALLOT   ( Saved Port )

: GROWB ( Set the view rectangle )
 TEDDY.W 20 + W@ 16 - 16 SCALE
 TEDDY.W 22 + W@ 16 - + TEDDY.T @ @ 12 + !
;
: TEDDYUP ( Update events are delivered here )
( Note that the zoom box also generates an update event! )
SPORT CALL GETPORT    ( Save some external window )
 TEDDY.W CALL SETPORT 
 ( Use the text editor window for updates )
 TEDDY.W CALL BEGINUPDATE 
 ( Inside Mac says this must be done )
 GROWB
 TEDDY.W 16 + CALL ERASERECT 
 ( Erase area to be updated )
 DRAWTITLES                  ( Draw palette titles )
 TEDDY.W 16 + TEDDY.T @ CALL TEUPDATE
 TEDDY.W CALL DRAWCONTROLS   
 ( We have a scroll bar to update )
 TEDDY.W CALL DRAWGROWICON
 TEDDY.W CALL ENDUPDATE
SPORT @ CALL SETPORT 
 ( Restore the port before the update )
;
( Given the number of the palette item that the mouse was
 pressed in, this procedure tracks the mouse to see what the
 user really wants. )
: DOPALETTE.SUB { SELECTED | SLOC }
 3 SELECTED 90 * 3 + 14 OVER 89 + TEMR !RECT
 0 -> SLOC
BEGIN
 CALL STILLDOWN
 WHILE
  @MOUSE TEMR CALL PTINRECT 0= 0= SLOC XOR
   IF TEMR CALL INVERTRECT SLOC NOT -> SLOC THEN
  REPEAT
SLOC IF TEMR CALL INVERTRECT SELECTED 1+ ELSE 0 THEN
; 
: DOPALETTE ( There is a mousedown in the palette )
 @MOUSE L_EXT
 2 - 90 / DOPALETTE.SUB
  CASE 1 OF 0 TEDDY.T @ @ 60 + W@
              TEDDY.T @ CALL TESETSELECT ENDOF
       2 OF TEDDY.T @ @ 32 + W@ TEDDY.T @ @ 60 + W@
            TEDDY.T @ CALL TESETSELECT ENDOF
       3 OF 0 TEDDY.T @ @ 34 + W@
            TEDDY.T @ CALL TESETSELECT ENDOF
       4 OF TEDDYLOAD                  ENDOF
       5 OF TEDDYSAVE                  ENDOF
ENDCASE
;
( Dotextclick looks at the shift key and calls TeClick.
  0= 0= is the equivalent of MacForth's "Boolean". )
: DOTEXTCLICK ( MOUSEPT -- Click...no ammo in a mouse... )
 
EVENT-RECORD 14 + W@ 512 AND 0= 0= TEDDY.T @ CALL TECLICK
 TEDDY.W CALL DRAWCONTROLS
;

2 2 15 452 RECT BUTTONRECT ( This is the rect of our palette )
: CONTENTCLICK { | MOUSEPT }
 RUN-CONTENT
 TEDDY.W CALL SETPORT
 EVENT-RECORD 10 + @ PAD ! PAD CALL GLOBALTOLOCAL
 PAD @ -> MOUSEPT 
 MOUSEPT BUTTONRECT CALL PTINRECT
    IF DOPALETTE 
    ELSE MOUSEPT TEDDY.T @ @ 8 + CALL PTINRECT
       IF MOUSEPT DOTEXTCLICK
       THEN
   THEN
;
( We set the dest and view rectangles )
: INITTEXT
 18 4 TEDDY.W 20 + W@ 16 - TEDDY.W 22 + W@ 16 - 
 TEMR !RECT TEMR PAD 8 CMOVE
 1 PAD 2+ W! TEMR PAD CALL TENEW TEDDY.T !
 -1 TEDDY.T @ @ 72 + W! 
;
( The following code handles the scroll bar )
( The thumb is called separately...Mach I manual for details )
: DOTHUMB 
 TEDDY.T @ @ W@ L_EXT 18 - NEGATE
 TEDDY.SB @ CALL GETCTLVALUE 11 * -
 0 SWAP TEDDY.T @ CALL TESCROLL
;
: DOARROW 
 TEDDY.SB @      CALL GETCTLVALUE SWAP OVER + 
 TEDDY.SB @ SWAP CALL SETCTLVALUE
 TEDDY.SB @      CALL GETCTLVALUE -
  11 * 0 SWAP TEDDY.T @ CALL TESCROLL
;
: TEDDYBAR
 CASE 
  UPARROW OF  -1  DOARROW ENDOF
  DOWNARROW OF 1  DOARROW ENDOF
 PAGEUP OF 
 TEDDY.W 20 + W@ 40 - -11 / -1 MIN DOARROW ENDOF
 PAGEDOWN OF 
 TEDDY.W 20 + W@ 40 -  11 /  1 MAX DOARROW ENDOF
ENDCASE
;
: TEDDYCONTROL  ( Control )
 CASE ( In case of multiple controls... )
  TEDDY.SB @ OF TEDDYBAR ENDOF
 ENDCASE
;
: TEDDYCONTROL2 ( Control/Part )
 CASE ( In case of multiple controls and parts... )
     TEDDY.SB @ OF  
 CASE THUMB  OF DOTHUMB ENDOF  ENDCASE  
 ENDOF
 ENDCASE
;
( We go to sleep when we are not in use. Deactivate events
 look like Activate events if the program doesn't look hard
 enough )
: ACTIVATE-HANDLER
    RUN-ACTIVATE
    EVENT-RECORD 14 + W@ 1 AND 
        IF WAKE STATUS TASK-> TEDDY.TASK W!
           ACTIVE? ON
           TEDDY.T @ CALL TEACTIVATE
           SCRAP->TE
      ELSE SLEEP STATUS TASK-> TEDDY.TASK W!
           TEDDY.T @ CALL TEDEACTIVATE
           ACTIVE? OFF
           TE->SCRAP
           CLEAR.TESCRAP
      THEN
;
( The Enter key does indentation, return doesn't )
: TEDDY.ENTER { | LOCATION CNTER NSPACES }
 TEDDY.T @ @ 62 + @ @    -> LOCATION
 TEDDY.T @ @ 32 + W@  1- -> CNTER
 0 -> NSPACES
 BEGIN
  LOCATION CNTER + C@ 13 = NOT
  CNTER 1+ 0> AND
  WHILE
   LOCATION CNTER + C@ 32 = IF NSPACES 1+ -> NSPACES 
                            ELSE 0 -> NSPACES THEN
   CNTER 1- -> CNTER
  REPEAT
 13 TEDDY.T @ CALL TEKEY
NSPACES 0> IF
 NSPACES 0 DO
  32 TEDDY.T @ CALL TEKEY
 LOOP THEN
;
( These are done only once, so we have a flag to show if the
 routine must be called. Always Workspace before testing
 TEDDY or you will save the flag in the wrong state! )
CREATE CONFIGFLAG 0 ,
: CONFIGURE.TEDDY
 TEDDY.W ADD
 TEDDY.W TEDDY.TASK BUILD
 TEDDY.BAR ADD
 TEDDY.BAR APPLEMENU ADD
 TEDDY.BAR TFILE ADD
 TEDDY.BAR TEDITMENU ADD
 TEDDY.W TEDDY.SB ADD
 INITTEXT
 ADD.DRVRS
TEDDY.BAR  TEDDY.TASK MBAR>TASK
TEDDY.TASK 
 CONFIGFLAG ON
;
( The following can be done the first time )
: TEDDYGO
CONFIGFLAG @ NOT IF CONFIGURE.TEDDY ACTIVATE THEN
 ACTIVE? OFF
['] TEDDYMENUS       MENU-VECTOR    !
['] TEDDYUP          UPDATE-HOOK    !
['] CONTENTCLICK     CONTENT-HOOK   !
['] ACTIVATE-HANDLER ACTIVATE-HOOK  !
['] TEDDYCONTROL     TEDDY.SB 4 +   !
['] TEDDYCONTROL2    CONTROL-VECTOR !
['] CLICKPROC        TEDDY.T @ @ 42 + !
100 CURMAX ! CORRECT.CONTROL.RANGE
TEDDY.SB @ ['] TEDDY.S2 !
TEDDY.W    ['] TEDDY.W2 !
TEDDY.T  @ ['] TEDDY.T2 !
BEGIN       ( This is the beginning of our "Event" loop )
 ACTIVE? @ IF TEDDY.T @ CALL TEIDLE ( Caret blink, blink, blink...)
              ?TERMINAL ?DUP IF
                1 24 SCALE AND IF ( Is it a cmd key? )
                  KEY CALL MENUKEY DROP
                  ELSE
                  KEY CASE
                       3 OF TEDDY.ENTER                         ENDOF
                       9 OF 4 0 DO 32 TEDDY.T @ CALL TEKEY LOOP ENDOF
                       TEDDY.T @ CALL TEKEY 0 ( EndCase drops! )
                       ENDCASE
                  CORRECTSCROLL     ( Autoscrolling )
                  CORRECT.CONTROL.RANGE
                  CORRECT.CONTROL
                  THEN
                 THEN
     THEN
   PAUSE   ( This is the equivalent of GetNextEvent )
   AGAIN
;
: TED 
  ( TED always starts the editor...even if you hide the window ) 
 CONFIGFLAG @ NOT IF TEDDYGO THEN
  TEDDY.W CALL SHOWWINDOW
  TEDDY.W CALL SELECTWINDOW
  TEDDY.BAR @ CALL SETMENUBAR
  CALL DRAWMENUBAR
  QUIT  
;
Listing 2: Teddy.R, resource listing for editor.
[This file should be compiled with Rmaker and the resulting resources pasted into 
your Mach2 file with ResEdit. The picture is contained within the file Teddy.rsrc which 
is available on the MacTutor source disk for this issue.  JL] 

*   ReditX 1.1, resource decompile option.
*
*   Resource listing from file: "Teddy.RSRC".
*

Teddy.RSRC.rsrc

Type DLOG

     ,900
40 40 130 472
Visible 1 NoGoAway 0
900
New Dialog


Type DITL

     ,10822
5
*   1
BtnItem Enabled
168 312 189 344
Ok

*   2
PicItem Disabled
8 16 147 99
901

*   3
StatText Disabled
16 168 32 248
Teddy V1.0\0D

*   4
StatText Disabled
40 144 56 280
Mach 2
™ text editor\0D

*   5
StatText Disabled
64 120 80 320
Copyright ©1986, Juri Munkki

     ,900
8
*   1
BtnItem Enabled
64 8 88 96
Find

*   2
BtnItem Enabled
64 104 88 192
Replace

*   3
StatText Enabled
8 8 24 48
Find:

*   4
StatText Disabled
32 8 48 70
Replace:

*   5
EditText Enabled
8 80 24 424

*   6
EditText Enabled
32 80 48 424

*   7
BtnItem Enabled
120 104 144 192
Replace All

*   8
StatText Disabled
56 296 89 423
Editor 1.0\0D©1986 Juri Munkki


Type ALRT

     ,900
40 80 200 432
10822
4444


Type PICT

     ,901
*   No format specification available.
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

BusyContacts 1.6.4 - Fast, efficient con...
BusyContacts is a contact manager for OS X that makes creating, finding, and managing contacts faster and more efficient. It brings to contact management the same power, flexibility, and sharing... Read more
Steam 4.0 - Multiplayer and communicatio...
Steam is a digital distribution, digital rights management, multiplayer and communications platform developed by Valve Corporation. It is used to distribute a large number of games and related media... Read more
OmniGraffle Pro 7.19.3 - Create diagrams...
OmniGraffle Pro helps you draw beautiful diagrams, family trees, flow charts, org charts, layouts, and (mathematically speaking) any other directed or non-directed graphs. We've had people use... Read more
OmniGraffle 7.19.3 - Create diagrams, fl...
OmniGraffle helps you draw beautiful diagrams, family trees, flow charts, org charts, layouts, and (mathematically speaking) any other directed or non-directed graphs. We've had people use Graffle to... Read more
Hopper Disassembler 5.3.3- - Binary disa...
Hopper Disassembler is a binary disassembler, decompiler, and debugger for 32- and 64-bit executables. It will let you disassemble any binary you want, and provide you all the information about its... Read more
calibre 5.35.0 - Complete e-book library...
Calibre is a complete e-book library manager. Organize your collection, convert your books to multiple formats, and sync with all of your devices. Let Calibre be your multi-tasking digital librarian... Read more
Sound Studio 4.10.0 - Robust audio recor...
Sound Studio lets you easily record and professionally edit audio on your Mac. Easily rip vinyls and digitize cassette tapes, or record lectures and voice memos. Prepare for live shows with live... Read more
Sparkle Pro 4.0 - Visual website creator...
Sparkle Pro will change your mind if you thought building websites wasn't for you. Sparkle is the intuitive site builder that lets you create sites for your online portfolio, team or band pages, or... Read more
Dropbox 140.4.1951 - Cloud backup and sy...
Dropbox for Mac is a file hosting service that provides cloud storage, file synchronization, personal cloud, and client software. It is a modern workspace that allows you to get to all of your files... Read more
FotoMagico 6.0.5 - Powerful slideshow cr...
FotoMagico lets you create professional slideshows from your photos and music with just a few, simple mouse clicks. It sports a very clean and intuitive yet powerful user interface. High image... Read more

Latest Forum Discussions

See All

Best iPhone Game Updates: ‘Garena Free F...
Hello everyone, and welcome to the week! It’s time once again for our look back at the noteworthy updates of the last seven days. I got busted last week for not including the obligatory free-to-play matching puzzle game update of the week, and my... | Read more »
‘Horizon Chase’ China Spirit DLC Release...
Following the release of the excellent reveal of the Horizon Chase Senna Forever expansion, the game will be getting a new DLC on mobile platforms today. Today, the Horizon Chase China Spirit DLC pack will release on iOS and Android bringing in 9... | Read more »
‘PUZZLED’ from SNK and Hamster Is Out No...
Following ZED BLADE ACA NeoGeo earlier this month, SNK has brought over another game in the ACA NeoGeo series to both iOS and Android in the form of PUZZLED. SNK and Hamster originally brought the series to mobile with Samurai Shodown IV, Alpha... | Read more »
A House Full of Covid – The TouchArcade...
It’s been a rough week as both of our young children tested positive for Covid, and since recording this early on Friday my wife has tested positive now too. Thankfully the kids seemed to recover fairly quickly and are mostly back to normal, and I... | Read more »
TouchArcade Game of the Week: ‘Krispee S...
Krispee Street is a new hidden object game from Frosty Pop that is based on their popular and almost painfully sweet webcomic Krispee. This is one of the latest titles to be added to the Netflix Games catalog, which means you’ll need to log into... | Read more »
SwitchArcade Round-Up: ‘Escape Lala’, ‘B...
Hello gentle readers, and welcome to the SwitchArcade Round-Up for January 21st, 2022. In today’s article, we’ve got a lot of new releases. A lot. There were eight on the schedule when I went to bed last night. There were twenty-four when I woke up... | Read more »
Beta Testers Needed for Huge Version 2.0...
Ya’ll remember Dungeon Raid, right? The phenomenal matching RPG hybrid that launched on mobile more than a decade ago, but was more or less abandoned by its developer only to die a slow death on the App Store before the 32-bit Appocalypse finally... | Read more »
‘Ark Legends’ Gives Players a Chance to...
It’s Airpods and Amazon gift cards galore as Melting Games opens pre-registration for Ark Legends. The upcoming mobile RPG is giving away tons of in-game goodies such as gold, energy, iron core, hero summon chest and rare iron core to players who... | Read more »
‘Nickelodeon Extreme Tennis’ Out Now on...
Nickelodeon Extreme Tennis () from Old Skull Games and Nickelodeon is this week’s new Apple Arcade release. Nickelodeon Extreme Tennis features characters from old and new Nickelodeon shows including SpongeBob, TMNT, and many more. The tennis game... | Read more »
SwitchArcade Round-Up: ‘RPGolf Legends’,...
Hello gentle readers, and welcome to the SwitchArcade Round-Up for January 20th, 2022. In today’s article, we’ve got a massive amount of new releases to check out. We’ve got summaries of all of them, from heaven to hell. We also have the lists of... | Read more »

Price Scanner via MacPrices.net

Verizon’s 2022 iPad promo: $100-$310 off any...
Verizon has cellular-capable iPads on sale for $100-$310 off MSRP when purchased with an Unlimited service plan. Sale price is applied to your account monthly over a 24 or 30 month period, depending... Read more
Sunday Sale: Apple AirPods are on sale for up...
Amazon has Apple AirPods on sale for $10-$100 off MSRP today, depending on the model. All are in stock today with free delivery: – AirPods Max headphones (Blue): $449 $100 off MSRP – AirPods Max... Read more
These Apple resellers are offering 13″ M1 Mac...
Apple resellers are offering discounts on 13″ MacBook Pros with M1 Apple Silicon processors ranging up to $150 off MSRP. Here’s where to get one today: (1): Apple’s 13″ MacBook Pros with M1 Apple... Read more
Amazon lowers prices on select 13″ M1 MacBook...
Amazon has select Apple 13″ M1 MacBook Airs on sale for $150 off MSRP this weekend, starting at only $849. Their prices are the lowest available for new MacBook Airs today. Stock may come and go, so... Read more
Apple has 13″ M1 MacBook Airs back in stock s...
Apple has restocked a full line of 13″ M1 MacBook Airs, Certified Refurbished, starting at only $849 and up to $190 off original MSRP. These are the cheapest M1-powered MacBooks for sale today at... Read more
In stock and on sale! 16″ 10-Core M1 Pro MacB...
Amazon has new 16″ 10-Core/512GB M1 Pro MacBook Pros in stock today and on sale for $50 off MSRP including free shipping. Their prices are the lowest available for new M1 Pro 16″ MacBook Pro from any... Read more
Deal Alert!: 14″ M1 Pro with 10-Core CPU in s...
Amazon has the new 14″ M1 Pro MacBook Pro with a 10-Core CPU and 16-Core GPU in stock today and on sale for $2299.99 including free shipping. Their price is $200 off Apple’s standard MSRP, and it’s... Read more
Apple has 24-inch M1 iMacs (8-Core CPU/8-Core...
Apple has restocked a wide array of 24-inch M1 iMacs with 8-Core CPUs and 8-Core GPUs in their Certified Refurbished store. Models are available starting at only $1269 and range up to $260 off... Read more
Select 24″ M1 iMacs are on sale for $100 off...
Sales of Apple’s new 24″ M1 iMacs have been rare since its introduction, perhaps due to global supply issues. However, B&H is offering a $100 discount on select 24″ iMacs, and they’re in stock... Read more
M1 Mac minis are back in stock today at Apple...
Apple has M1-powered Mac minis available in their Certified Refurbished section starting at only $589 and up to $140 off MSRP. Each mini comes with Apple’s one-year warranty, and shipping is free: –... Read more

Jobs Board

Registered Nurse (RN) Employee Health PSJH -...
…is calling for a Registered Nurse (RN) Employee Health PSJH to our location in Apple Valley, CA.** We are seeking a Registered Nurse (RN) Employee Health PSJH to be Read more
Systems Administrator - Pearson (United State...
…and troubleshoot Windows operating systems (workstation and server), laptop computers, Apple iPads, Chromebooks and printers** + **Administer and troubleshoot all Read more
IT Assistant Level 1- IT Desktop Support Anal...
…providing tier-1 or better IT help desk support in a large Windows and Apple environment * Experience using IT Service Desk Management Software * Knowledge of IT Read more
Human Resources Business Partner PSJH - Provi...
…**is calling a** **Human Resources Business Partner, PSJH** **to our location in Apple Valley, CA.** **Applicants that meet qualifications will receive a text with Read more
Manager Community Health Investment Programs...
…is calling a Manager Community Health Investment Programs PSJH to our location in Apple Valley, CA.** **Qualified candidates will be invited to do a self-paced video Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.