unit Edit; {Editing routines used by NIH Image} interface uses Types, Memory, QuickDraw, QuickDrawText, Packages, Menus, Events, Fonts, Scrap, ToolUtils, Resources, Errors, Palettes, StandardFile, Windows, Controls, TextEdit, Files, Dialogs, TextUtils, Finder, MixedMode, Processes, globals, Utilities, Graphics, Camera, analysis, file1, filters, stacks, Lut, Text, math; procedure FlipOrRotate (DoWhat: FlipRotateType); procedure RotateToNewWindow (DoWhat: FlipRotateType); procedure Rotate (DoWhat: FlipRotateType); procedure DoCopy; procedure DoCut; procedure DoPaste; procedure DoClear; procedure ShowClipboard; procedure DoObject (obj: ObjectType; event: EventRecord); procedure DoSprayCan; procedure DoBrush (event: EventRecord); procedure DoText (loc: point); procedure SetSprayCanSize; procedure SetBrushSize; procedure SetLineWidth; procedure UpdateEditMenu; procedure ConverToSystemClipboard; procedure ZoomOut; procedure ZoomIn (event: EventRecord); procedure Scroll (event: EventRecord); procedure DoFill (event: EventRecord); procedure DoGrow (WhichWindow: WindowPtr; event: EventRecord); procedure DrawCharacter (ch: char); procedure ConvertFromSystemClipboard; procedure SetupOperation (item: integer); procedure PastePicture; procedure DoUndo; procedure FindWhatToCopy; procedure CopyResults; implementation procedure PivotSelection (var SelectionRect: rect; WindowRect: rect); var OldWidth, NewWidth, OldHeight, NewHeight, hCenter, vCenter, NewLeft, NewTop: integer; begin with SelectionRect do begin OldWidth := right - left; OldHeight := bottom - top; hCenter := left + OldWidth div 2; vCenter := top + OldHeight div 2; end; NewWidth := OldHeight; NewHeight := OldWidth; NewLeft := hCenter - NewWidth div 2; NewTop := vCenter - NewHeight div 2; with WindowRect do begin if (NewLeft + NewWidth) > right then NewLeft := right - NewWidth; if (NewTop + NewHeight) > bottom then NewTop := bottom - NewHeight; if NewLeft < 0 then NewLeft := 0; if NewTop < 0 then NewTop := 0; end; with SelectionRect do begin left := NewLeft; top := NewTop; right := NewLeft + NewWidth; bottom := NewTop + NewHeight; end; end; procedure FlipLine (var LineBuf: LineType; width: integer); var TempLine: LineType; i, WidthLessOne: integer; begin TempLine := LineBuf; WidthLessOne := width - 1; for i := 0 to width - 1 do LineBuf[i] := TempLine[WidthLessOne - i]; end; procedure ScreenToOffscreenRect (var r: rect); var p1, p2: point; begin with r do begin p1.h := left; p1.v := top; p2.h := right; p2.v := bottom; ScreenToOffscreen(p1); ScreenToOffscreen(p2); Pt2Rect(p1, p2, r); end; end; procedure FlipOrRotate (DoWhat: FlipRotateType); var SaveInfo: InfoPtr; width, height, hDst, vSrc, vDst, hSrc, i, inc: integer; LineBuf: LineType; srect, drect, MaskRect: rect; AutoSelectAll: boolean; SaveRow:integer; NextUpdate: LongInt; begin if NotRectangular or NotInBounds or NoUndo then exit(FlipOrRotate); AutoSelectAll := not Info^.RoiShowing; if AutoSelectAll then SelectAll(true); if TooWide then exit(FlipOrRotate); ShowWatch; SetupUndoFromClip; SetupUndo; if (DoWhat = RotateLeft) or (DoWhat = RotateRight) then WhatToUndo := UndoRotate else WhatToUndo := UndoFlip; SetupUndoInfoRec; SaveInfo := Info; srect := info^.RoiRect; case DoWhat of RotateLeft, RotateRight: with srect do begin if OptionKeyWasDown then DoOperation(EraseOp); drect := srect; with info^ do begin PivotSelection(drect, PicRect); MaskRect := drect; RoiRect := drect; RectRgn(roiRgn, RoiRect); end; width := right - left; if DoWhat = RotateLeft then begin hDst := drect.left; inc := 1 end else begin hDst := drect.right - 1; inc := -1 end; SaveRow:=top; NextUpdate:=TickCount+6; {10/sec} for vSrc := top to bottom - 1 do begin Info := UndoInfo; GetLine(left, vSrc, width, LineBuf); if DoWhat = RotateLeft then FlipLine(LineBuf, width); Info := SaveInfo; PutColumn(hDst, drect.top, width, LineBuf); hDst := hDst + inc; if TickCount>=NextUpdate then begin SetRect(MaskRect, left, SaveRow, left+width, vSrc + 1); UpdateScreen(MaskRect); SaveRow:=vSrc+1; NextUpdate:=TickCount+6; ShowAnimatedWatch; end; end; SetRect(MaskRect, left, SaveRow, left+width, bottom); UpdateScreen(MaskRect); end; FlipVertical: with srect do begin width := right - left; vDst := bottom; for vSrc := top to bottom - 1 do begin Info := UndoInfo; GetLine(left, vSrc, width, LineBuf); Info := SaveInfo; vDst := vDst - 1; PutLine(left, vDst, width, LineBuf); end; end; FlipHorizontal: with srect do begin width := right - left; SaveRow:=top; NextUpdate:=TickCount+6; {10/sec} for vSrc := top to bottom - 1 do begin Info := UndoInfo; GetLine(left, vSrc, width, LineBuf); FlipLine(LineBuf, width); Info := SaveInfo; PutLine(left, vSrc, width, LineBuf); if TickCount>=NextUpdate then begin SetRect(MaskRect, left, SaveRow, left+width, vSrc + 1); UpdateScreen(MaskRect); SaveRow:=vSrc+1; NextUpdate:=TickCount+6; ShowAnimatedWatch; end; end; SetRect(MaskRect, left, SaveRow, left+width, bottom); UpdateScreen(MaskRect); end; end; {case} Info := SaveInfo; Info^.changes := true; SetupRoiRect; if AutoSelectAll then KillRoi; end; procedure RotateToNewWindow (DoWhat: FlipRotateType); var SrcInfo, DstInfo: InfoPtr; Srcwidth, DstWidth, DstHeight, hDst, vSrc, vDst, hSrc, i, inc, ignore: integer; LineBuf: LineType; SourceRect, DstRect, MaskRect: rect; AutoSelectAll, isStack: boolean; SaveCol:integer; NextUpdate: LongInt; begin if NotRectangular or NotInBounds then exit(RotateToNewWindow); AutoSelectAll := not Info^.RoiShowing; isStack := info^.StackInfo <> nil; if AutoSelectAll then SelectAll(true); if TooWide then exit(RotateToNewWindow); ShowWatch; SrcInfo := info; with info^, info^.RoiRect do begin SourceRect := RoiRect; SrcWidth := right - left; DstWidth := bottom - top; DstHeight := right - left; if not NewPicWindow(title, DstWidth, DstHeight) then begin KillRoi; AbortMacro; exit(RotateToNewWindow) end; DstInfo := info; DstRect := info^.PicRect; end; if DoWhat = RotateLeft then begin hDst := 0; inc := 1 end else begin hDst := DstWidth - 1; inc := -1 end; with SourceRect do begin SaveCol:=hDst; NextUpdate:=TickCount+6; {10/sec} for vSrc := top to bottom - 1 do begin Info := SrcInfo; GetLine(left, vSrc, SrcWidth, LineBuf); if DoWhat = RotateLeft then FlipLine(LineBuf, SrcWidth); Info := DstInfo; PutColumn(hDst, 0, SrcWidth, LineBuf); if TickCount>=NextUpdate then begin if DoWhat=RotateLeft then SetRect(MaskRect, SaveCol, 0, hDst+1, SrcWidth) else SetRect(MaskRect, hDst, 0, SaveCol+1, SrcWidth); UpdateScreen(MaskRect); SaveCol:=hDst+1; NextUpdate:=TickCount+6; ShowAnimatedWatch; end; hDst := hDst + inc; end; {for} if DoWhat=RotateLeft then SetRect(MaskRect, SaveCol, 0, dstWidth, SrcWidth) else SetRect(MaskRect, 0, 0, SaveCol+1, SrcWidth); UpdateScreen(MaskRect); end; {with} info^.changes := true; if AutoSelectAll and not isStack then with SrcInfo^ do begin Changes := false; ignore := CloseAWindow(wptr); info := DstInfo; end; end; procedure Rotate; {(DoWhat: FlipRotateType)} const NewWindowID = 3; var mylog: DialogPtr; item: integer; NewWindow: boolean; begin with info^, info^.RoiRect do if RoiShowing then NewWindow := ((right - left) > PicRect.bottom) or ((bottom - top) > PicRect.right) else begin RotateToNewWindow(DoWhat); exit(Rotate); end; InitCursor; mylog := GetNewDialog(120, nil, pointer(-1)); SetDlogItem(mylog, NewWindowID, ord(NewWindow)); OutlineButton(MyLog, ok, 16); repeat if item = NewWindowID then begin NewWindow := not NewWindow; SetDlogItem(mylog, NewWindowID, ord(NewWindow)); end; ModalDialog(nil, item); until (item = ok) or (item = cancel); DisposeDialog(mylog); if item = cancel then exit(Rotate); if NewWindow then RotateToNewWindow(DoWhat) else FlipOrRotate(DoWhat); end; function CopyImage: boolean; var err, width, EvenWidth, height, size: LongInt; line: integer; ClipXOffset, ClipYOffset: integer; SavePort: GrafPtr; SaveGDevice: GDHandle; begin if OpPending then begin KillRoi; RestoreRoi; end; with info^, info^.RoiRect do begin if (RoiType = RectRoi) and (PictureType = FrameGrabberType) then begin {We can't offset an roi copied from Camera window or "live" paste won't work} ClipXOffset := 0; ClipYOffset := 0; width := picRect.right; height := picRect.bottom; end else begin ClipXOffset := left; ClipYOffset := top; width := right - left; height := bottom - top; end; if odd(width) then EvenWidth := width + 1 else EvenWidth := width; size := EvenWidth * height; if size > ClipBufSize then begin PutError(StringOf('This ',size div 1024:1,'K selection is larger than the ',ClipBufSize div 1024:1,'K Clipboard buffer.')); WhatsOnClip := NothingOnClip; AbortMacro; CopyImage := false; exit(CopyImage) end; end; with ClipBufInfo^ do begin PixelsPerLine := width; BytesPerRow := EvenWidth; nLines := height; RoiRect := info^.RoiRect; OffsetRect(RoiRect, -ClipXOffset, -ClipYOffset); roiType := Info^.roiType; PicRect := RoiRect; with osPort^.portPixMap^^ do begin RowBytes := BitOr(BytesPerRow, $8000); bounds := PicRect; end; with osPort^ do begin PortRect := PicRect; RectRgn(visRgn, PicRect); end; if RoiType = RectRoi then begin if info^.PictureType = FrameGrabberType then WhatsOnClip := CameraPic else WhatsOnClip := RectPic end else WhatsOnClip := NonRectPic; SaveGDevice := GetGDevice; SetGDevice(osGDevice); GetPort(SavePort); SetPort(GrafPtr(osPort)); CopyRgn(info^.roiRgn, roiRgn); OffsetRgn(roiRgn, -ClipXOffset, -ClipYOffset); ctable := info^.ctable; IndexToRgbForeColor(BlackIndex); IndexToRgbBackColor(WhiteIndex); CopyBits(BitMapHandle(Info^.osPort^.portPixMap)^^, BitMapHandle(osPort^.PortPixMap)^^, Info^.RoiRect, RoiRect, SrcCopy, nil); IndexToRgbForeColor(ForegroundIndex); IndexToRgbBackColor(BackgroundIndex); SetPort(SavePort); SetGDevice(SaveGDevice); end; {with} CopyImage := true; end; procedure CopyWindow; var tPort: GrafPtr; WindowSize: LongInt; WindowRect: rect; WhichWindow: WindowPtr; kind, ignore: integer; HidingPasteControl: boolean; SaveGDevice: GDHandle;i:integer; begin WhichWindow := FrontWindow; if WhichWindow = nil then exit(CopyWindow); WindowRect := WhichWindow^.PortRect; kind := WindowPeek(WhichWindow)^.WindowKind; HidingPasteControl := false; with WindowRect do begin WindowSize := right; WindowSize := WindowSize * bottom; end; if kind = LUTKind then WindowRect.bottom := 256; case kind of ProfilePlotKind: begin ConvertPlotToText; ClipTextInBuffer := true; end; CalibrationPlotKind: begin ConvertCalibrationCurveToText; ClipTextInBuffer := true; end; HistoKind, LUTKind, MapKind, ToolKind: begin if PasteControl <> nil then begin ignore := CloseAWindow(PasteControl); HidingPasteControl := true; end; case kind of HistoKind: begin ConvertHistoToText; ClipTextInBuffer := true; DrawHistogram; end; MapKind: DrawMap; LUTKind: DrawLUT; ToolKind: DrawTools; end; {case} end; otherwise end; {case} if NoUndo then begin WhatsOnClip := NothingOnClip; exit(CopyWindow) end; ClipboardConverted := false; with ClipBufInfo^ do begin RoiType := RectRoi; RoiRect := WindowRect; RectRgn(roiRgn, RoiRect); PicRect := WindowRect; PixelsPerLine := WindowRect.right; BytesPerRow := PixelsPerLine; if odd(BytesPerRow) then BytesPerRow := BytesPerRow + 1; nLines := WindowRect.bottom; with osPort^.portPixMap^^ do begin RowBytes := BitOr(BytesPerRow, $8000); bounds := WindowRect; end; with osPort^ do begin PortRect := PicRect; RectRgn(visRgn, PicRect); SetRectRgn(ClipRgn, 0, 0, 30000, 30000); end; WhatsOnClip := RectPic; SaveGDevice := GetGDevice; SetGDevice(osGDevice); GetPort(tPort); SetPort(GrafPtr(osPort)); RGBForeColor(BlackRGB); RGBBackColor(WhiteRGB); if (kind = ProfilePlotKind) or (kind = CalibrationPlotKind) then begin EraseRect(osPort^.portRect); DrawPlot end else CopyBits(WhichWindow^.PortBits, BitMapHandle(osPort^.portPixMap)^^, WindowRect, WindowRect, SrcCopy, nil); SetPort(tPort); SetGDevice(SaveGDevice); end; {with} if HidingPasteControl then ShowPasteControl; end; procedure CopyResults; var err: OSErr; begin CopyResultsToBuffer(1, mCount, ShowHeadings); UnsavedResults := false; err := ZeroScrap; if err = NoErr then begin err := PutScrap(TextBufSize, 'TEXT', ptr(TextBufP)); WhatsOnClip := NothingOnClip; {The text is on the System Scrap} end; end; procedure DoCopy; var err: OSErr; begin err := ZeroScrap; OldScrapCount := GetScrapCount; case WhatToCopy of CopyColor: DoCopyColor; CopySelection: begin if not CopyImage then exit(DoCopy); ClipTextInBuffer := false; ClipboardConverted := false; end; CopyHistogram, CopyPlot, CopyCalibrationPlot, CopyCLUT, CopyGrayMap, CopyTools: CopyWindow; CopyMeasurements: CopyResults; CopyText: DoTextCopy; otherwise beep; end; end; procedure DoCut; begin DoCopy; DoClear; end; procedure CenterRect (inRect, outRect: rect; var ResultRect: rect); {Creates a new rectangle(ResultsRect) that is the same size as inRect, but centered within outRect.} var width, height, hcenter, vcenter: integer; begin with inRect do begin width := right - left; height := bottom - top; end; with outRect do begin hcenter := left + (right - left) div 2; vcenter := top + (bottom - top) div 2; end; with ResultRect do begin left := hcenter - width div 2; top := vcenter - height div 2; right := left + width; bottom := top + height; end; end; procedure PastePicture; var loc: point; SrcWidth, SrcHeight, DstHeight, DstWidth, dh, dv: integer; DestRect: rect; WindowNotResized: boolean; begin if LivePasteMode or (PasteTransferMode <> SrcCopy) then begin LivePasteMode := false; PasteTransferMode := SrcCopy; if PasteControl <> nil then DrawPasteControl end; with info^ do begin SetupUndo; WhatToUndo := UndoPaste; if RoiShowing then with RoiRect do {Pasting back into selection of same size?} if ((right - left) = (ClipBufInfo^.RoiRect.right - ClipBufInfo^.RoiRect.left)) and ((bottom - top) = (ClipBufInfo^.RoiRect.bottom - ClipBufInfo^.RoiRect.top)) and (ClipBufInfo^.RoiType = RoiType) then begin OpPending := true; CurrentOp := PasteOp; exit(PastePicture) end; with ClipBufInfo^.RoiRect do {Pasting into same size window?} if (PicRect.right = right - left) and (PicRect.bottom = (bottom - top)) and (ClipBufInfo^.RoiType = RectRoi) then begin SelectAll(true); WhatToUndo := UndoPaste; OpPending := true; CurrentOp := PasteOp; exit(PastePicture) end; if RoiShowing or (roiType <> NoRoi) then KillRoi; with ClipBufInfo^.RoiRect do begin SrcWidth := right - left; SrcHeight := bottom - top; end; with SrcRect do begin DstWidth := right - left; DstHeight := bottom - top; end; with initwrect do WindowNotResized := (DstWidth = (right - left)) and (DstHeight = (bottom - top)); if ((SrcWidth > DstWidth) or (SrcHeight > DstHeight)) and WindowNotResized then DestRect := PicRect else DestRect := SrcRect; CenterRect(ClipBufInfo^.RoiRect, DestRect, RoiRect); roiType := ClipBufInfo^.roiType; CopyRgn(ClipBufInfo^.roiRgn, roiRgn); dh := RoiRect.left - roiRgn^^.rgnbbox.left; dv := RoiRect.top - roiRgn^^.rgnbbox.top; OffsetRgn(roiRgn, dh, dv); RoiShowing := true; OpPending := true; CurrentOp := PasteOp; BinaryPic := false; end;{with} end; procedure ConvertFromSystemClipboard; {Converts system-wide clipboard to local clipboard.} var phandle: handle; offset, length, size, EvenWidth: LongInt; pframe: rect; width, height: LongInt; tPort: GrafPtr; ScrapInfo: ScrapStuffPtr; SaveGDevice: GDHandle; begin ScrapInfo := InfoScrap; if ScrapInfo^.ScrapSize <= 0 then exit(ConvertFromSystemClipboard); phandle := NewHandle(0); length := GetScrap(phandle, 'PICT', offset); if length > 0 then begin ShowWatch; pframe := PicHandle(phandle)^^.PicFrame; with pframe do begin width := right - left; if odd(width) then EvenWidth := width + 1 else EvenWidth := width; height := bottom - top; size := EvenWidth * height; if size > ClipBufSize then begin PutError(StringOf('The ', size div 1024:1,'K image on the system clipboard is too large to paste.')); DisposeHandle(phandle); exit(ConvertFromSystemClipboard) end; end; with ClipBufInfo^ do begin PixelsPerLine := width; nlines := height; SetRect(PicRect, 0, 0, width, height); RoiRect := PicRect; RoiType := RectRoi; SaveGDevice := GetGDevice; SetGDevice(osGDevice); GetPort(tPort); SetPort(GrafPtr(osPort)); RectRgn(roiRgn, RoiRect); BytesPerRow := EvenWidth; with osPort^.portPixMap^^ do begin RowBytes := BitOr(BytesPerRow, $8000); bounds := PicRect; end; with CGrafPtr(osPort)^ do begin PortRect := PicRect; RectRgn(visRgn, PicRect); SetRectRgn(ClipRgn, 0, 0, 30000, 30000); end; RGBForecolor(WhiteRGB); PaintRect(PicRect); DrawPicture(PicHandle(phandle), PicRect); SetPort(tPort); SetGDevice(SaveGDevice); end; {with} WhatsOnClip := ImportedPic; end else begin length := GetScrap(phandle, 'TEXT', offset); if (length > 0) and (length < MaxTextBufSize) then begin BlockMove(phandle^, ptr(TextBufP), length); TextBufSize := length; WhatsOnClip := TextOnClip; end; end; DisposeHandle(phandle); end; procedure PasteText; var nTextLines, LineWidth, MaxLineWidth, MaxRectWidth, MaxRectHeight: integer; LineStart, LineEnd, height, kind: integer; fwptr: WindowPtr; SaveGDevice: GDHandle; okay: boolean; begin fwptr := FrontWindow; if fwptr = nil then exit(PasteText); kind := WindowPeek(fwptr)^.WindowKind; if Kind = TextKind then begin DoTextPaste; exit(PasteText); end; if TextBufSize > 5000 then begin PutError('The maximum number of characters that can be pasted is 5000.'); exit(PasteText); end; if (Info = NoInfo) or NoUndo then exit(PasteText); with ClipBufInfo^ do begin SaveGDevice := GetGDevice; SetGDevice(osGDevice); SetPort(GrafPtr(osPort)); RGBForeColor(BlackRGB); RGBBackColor(WhiteRGB); TextFont(CurrentFontID); TextFace(CurrentStyle); TextSize(CurrentSize); end; with info^ do if (not RoiShowing) or (RoiShowing and (RoiType <> RectRoi)) then begin KillRoi; nTextLines := 1; MaxLineWidth := 10; LineStart := 1; LineEnd := 0; repeat LineEnd := LineEnd + 1; if TextBufP^[LineEnd] = cr then begin nTextLines := nTextLines + 1; LineWidth := TextWidth(ptr(TextBufP), LineStart - 1, LineEnd - LineStart); if LineWidth > MaxLineWidth then MaxLineWidth := LineWidth; LineStart := LineEnd; end; until LineEnd >= TextBufSize; if LineEnd > LineStart then begin LineWidth := TextWidth(ptr(TextBufP), LineStart - 1, LineEnd - LineStart); if LineWidth > MaxLineWidth then MaxLineWidth := LineWidth; end; height := nTextLines * CurrentSize + CurrentSize div 4; MaxRectHeight := (PicRect.bottom * 2) div 3; if height > MaxRectHeight then height := MaxRectHeight; MaxLineWidth := MaxLineWidth + CurrentSize div 2; MaxRectWidth := (PicRect.right * 2) div 3; if MaxLineWidth > MaxRectWidth then begin MaxLineWidth := MaxRectWidth; height := MaxRectHeight; end; with RoiRect do begin left := 0; top := 0; right := MaxLineWidth; bottom := height; end; RoiType := RectRoi; MakeRegion; end; okay := CopyImage; if okay then begin WhatsOnClip := TextOnClip; SetRectRgn(ClipBufInfo^.osPort^.ClipRgn, 0, 0, 30000, 30000); {Why is this needed?} TETextBox(ptr(TextBufP), TextBufSize, ClipBufInfo^.RoiRect, TextJust); PastePicture; end; SetGDevice(SaveGDevice); end; procedure DoPaste; var NewScrapCount: integer; begin if ((info = NoInfo) and (WhatsOnClip in [RectPic, NonRectPic, ImportedPic, CameraPic])) then begin if CurrentWindow <> TextKind then begin PutError('You must have an image window open to paste.'); exit(DoPaste); end else WhatsOnClip := NothingOnClip; end; RoiUpdateTime := 0; NewScrapCount := GetScrapCount; if NewScrapCount <> OldScrapCount then begin WhatsOnClip := NothingOnClip; OldScrapCount := NewScrapCount; end; case WhatsOnClip of aColor: PasteColor; RectPic, NonRectPic, ImportedPic, CameraPic: PastePicture; TextOnClip: PasteText; LivePic: WhatsOnClip := NothingOnClip; NothingOnClip: begin ConvertFromSystemClipboard; if (WhatsOnClip = ImportedPic) and (info <> NoInfo) then PastePicture else if WhatsOnClip = textOnClip then PasteText else beep; end; end; end; procedure DoClear; var fwptr: WindowPtr; kind: integer; begin fwptr := FrontWindow; if fwptr = nil then exit(DoClear); kind := WindowPeek(fwptr)^.WindowKind; if Kind = TextKind then begin DoTextClear; exit(DoClear); end; if not NoSelection then begin SetupUndo; WhatToUndo := UndoClear; CurrentOp := EraseOp; OpPending := true; RoiUpdateTime := 0; end; end; procedure ShowClipboard; var width, height, hstart, vstart, i, NewScrapCount: integer; okay:boolean; begin NewScrapCount := GetScrapCount; if NewScrapCount <> OldScrapCount then begin WhatsOnClip := NothingOnClip; OldScrapCount := NewScrapCount; end; if WhatsOnClip = NothingOnClip then ConvertFromSystemClipboard; if (WhatsOnClip = RectPic) or (WhatsOnClip = NonRectPic) or (WhatsOnClip = ImportedPic) or (WhatsOnClip = CameraPic) then with ClipBufinfo^.RoiRect do begin width := right - left; height := bottom - top; if NewPicWindow('Clipboard', width, height) then begin PastePicture; KillRoi; SetupUndo; info^.changes := false; end; end; if WhatsOnClip = TextOnClip then begin if MakeNewTextWindow('Clipboard', 400, 350) then DoTextPaste; end; end; function ScreenToPixmapH (hloc: integer): extended; begin with info^ do ScreenToPixmapH := SrcRect.left + hloc / magnification; end; function ScreenToPixmapV (vloc: integer): extended; begin with info^ do ScreenToPixmapV := SrcRect.top + vloc / magnification; end; procedure DoSelection (obj: ObjectType; start, finish: point); var tRect: rect; temp, StartH, StartV, FinishH, FinishV: integer; TempRgn: RgnHandle; begin WhatToUndo := NothingToUndo; Info^.RoiShowing := false; RoiUpdateTime := 0; if (start.h = finish.h) or (start.v = finish.v) then exit(DoSelection); if start.h > finish.h then begin temp := start.h; start.h := finish.h; finish.h := temp; end; if start.v > finish.v then begin temp := start.v; start.v := finish.v; finish.v := temp; end; StartH := round(ScreenToPixmapH(start.h)); StartV := round(ScreenToPixmapV(start.v)); FinishH := round(ScreenToPixmapH(finish.h)); FinishV := round(ScreenToPixmapV(finish.v)); SetRect(tRect, StartH, StartV, FinishH, FinishV); with info^ do begin RoiShowing := true; if SelectionMode <> NewSelection then TempRgn := NewRgn; OpenRgn; case obj of SelectionOval: begin FrameOval(tRect); roiType := OvalRoi; end; SelectionRect: begin FrameRect(tRect); roiType := RectRoi; end; end; if SelectionMode = NewSelection then CloseRgn(roiRgn) else begin CloseRgn(TempRgn); if RgnNotTooBig(roiRgn, TempRgn) then begin if SelectionMode = AddSelection then UnionRgn(roiRgn, TempRgn, roiRgn) else begin DiffRgn(roiRgn, TempRgn, roiRgn); UpdatePicWindow; end; end; DisposeRgn(TempRgn); if GetHandleSize(handle(roiRgn)) = 10 then roiType := RectRoi else roiType := FreehandRoi; nCoordinates := 0; end; RoiRect := roiRgn^^.rgnBBox; end;{with} measuring := false; end; procedure DoObject; {(obj: ObjectType; event: EventRecord)} var Start, Finish, ScreenStart, ScreenFinish, osStart, osFinish: point; r: rect; DeltaX, DeltaY, switch: integer; Constrain: boolean; StartH, StartV: extended; begin SetPort(info^.wptr); if obj = LineObj then DrawLabels('DX:', 'DY:', 'Length:') else DrawLabels('Width:', 'Height:', ''); start := event.where; StartH := ScreenToPixmapH(start.h); StartV := ScreenToPixmapV(start.v); osStart := start; ScreenToOffscreen(osStart); finish := start; osFinish := finish; ScreenToOffscreen(osFinish); PenNormal; PenMode(PatXor); PenSize(1, 1); while button do begin GetMouse(finish); with finish, Info^ do begin if h > wrect.right then h := wrect.right; if v > wrect.bottom then v := wrect.bottom; if h < 0 then h := 0; if v < 0 then v := 0; end; if ShiftKeyDown then begin DeltaX := finish.h - start.h; DeltaY := finish.v - start.v; if obj = lineObj then begin if abs(DeltaX) > abs(DeltaY) then finish.v := start.v else finish.h := start.h end else begin if ((DeltaX > 0) and (DeltaY < 0)) or ((DeltaX < 0) and (DeltaY > 0)) then switch := -1 else switch := 1; if abs(DeltaX) > abs(DeltaY) then finish.h := start.h + switch * DeltaY else finish.v := start.v + switch * DeltaX; end; end; osFinish := finish; ScreenToOffscreen(osfinish); case obj of LineObj: begin MoveTo(start.h, start.v); LineTo(finish.h, finish.v); ShowDxDy(abs(ScreenToPixMapH(finish.h) - StartH), abs(ScreenToPixMapV(finish.v) - StartV)); MoveTo(start.h, start.v); LineTo(finish.h, finish.v); end; Rectangle, SelectionRect: begin if obj = SelectionRect then begin PatIndex := (PatIndex + 1) mod 8; PenPat(AntPattern[PatIndex]); end; Pt2Rect(start, finish, r); FrameRect(r); Show3Values(osfinish.h - osstart.h, osfinish.v - osstart.v, -1); Pt2Rect(start, finish, r); FrameRect(r); end; SelectionOval: begin PatIndex := (PatIndex + 1) mod 8; PenPat(AntPattern[PatIndex]); Pt2Rect(start, finish, r); FrameOval(r); Show3Values(osfinish.h - osstart.h, osfinish.v - osstart.v, -1); Pt2Rect(start, finish, r); FrameOval(r); end; end; {case} end; {while button} if (obj = SelectionRect) or (obj = SelectionOval) then begin DoSelection(obj, start, finish); exit(DoObject); end; if (obj = LineObj) and ((CurrentTool = LineTool) or (CurrentTool = PlotTool)) then begin MoveTo(start.h, start.v); LineTo(finish.h, finish.v); with info^ do begin LX1 := StartH; LY1 := StartV; LX2 := ScreenToPixmapH(finish.h); LY2 := ScreenToPixmapV(finish.v); if LX1 > (PicRect.right - 1) then LX1 := PicRect.right - 1; if LY1 > (PicRect.bottom - 1) then LY1 := PicRect.bottom - 1; if LX1 < 0 then LX1 := 0; if LY1 < 0 then LY1 := 0; if LX2 > (PicRect.right - 1) then LX2 := PicRect.right - 1; if LY2 > (PicRect.bottom - 1) then LY2 := PicRect.bottom - 1; if LX2 < 0 then LX2 := 0; if LY2 < 0 then LY2 := 0; end; exit(DoObject); end; DrawObject(obj, start, finish); end; procedure DrawSprayCan (xcenter, ycenter: integer); var i, xoffset, yoffset, nDots: LongInt; begin nDots := SprayCanDiameter div 4; if nDots < 15 then nDots := 15; for i := 1 to nDots do begin repeat xoffset := random mod SprayCanRadius; yoffset := random mod SprayCanRadius; until xoffset * xoffset + yoffset * yoffset <= SprayCanRadius2; PutPixel(xcenter + xoffset, ycenter + yoffset, ForegroundIndex); end; end; procedure DoSprayCan; {Reference: "Spaying and Smudging", Dick Pountain, Byte, November 1987} var xcenter, ycenter, off: integer; MaskRect: rect; pt: point; SaveTicks:LongInt; begin info^.changes := true; off := SprayCanRadius; SaveTicks:=TickCount; repeat repeat until TickCount<>SaveTicks; {Update no more than 60 times per second} SaveTicks:=TickCount; GetMouse(pt); ScreenToOffscreen(pt); with MaskRect, pt do begin left := h - off; top := v - off; right := h + off; bottom := v + off; end; with pt do begin xcenter := h; ycenter := v end; DrawSprayCan(xcenter, ycenter); UpdateScreen(MaskRect); until not button; WhatToUndo := UndoEdit; end; procedure DoBrush; {(event: EventRecord)} var r, ScreenRect: rect; p1, p2, p2x, start: point; WhichWindow: WindowPtr; SaveLineWidth, SaveForegroundColor: integer; Constrained, MoreHorizontal, FirstTime: boolean; offset, width: integer; rWidth: double; begin SaveLineWidth := LineWidth; p1 := event.where; start := p1; if OptionKeyDown then begin case CurrentTool of Brush, Pencil: GetForegroundColor(event); Eraser: GetBackgroundColor(event); end; if (CurrentTool = Brush) or (CurrentTool = Eraser) then exit(DoBrush); end; case CurrentTool of Pencil: LineWidth := 1; Brush, Eraser: begin if CurrentTool = Brush then width := BrushWidth else width := 16; LineWidth := round(width / info^.magnification); if LineWidth < 1 then LineWidth := 1; end; end; with info^ do rWidth := (LineWidth - 1) * info^.magnification / 2.0; offset := round(rWidth * 1.00000001); {ppc-bug} if CurrentTool <> Pencil then with p1 do begin h := h - offset; v := v - offset end; Constrained := ShiftKeyDown; FirstTime := true; if CurrentTool = eraser then begin SaveForegroundColor := ForegroundIndex; SetForegroundColor(BackgroundIndex) end; repeat GetMouse(p2); if CurrentTool <> Pencil then with p2 do begin h := h - offset; v := v - offset end; if FirstTime then if not EqualPt(p1, p2) then begin MoreHorizontal := abs(p2.h - p1.h) >= abs(p2.v - p1.v); FirstTime := false; end; if Constrained then if MoreHorizontal then p2.v := p1.v else p2.h := p1.h; if CurrentTool = brush then DrawObject(BrushObj, p1, p2) else DrawObject(LineObj, p1, p2); p1 := p2; until not button; if CurrentTool = Eraser then SetForegroundColor(SaveForegroundColor); LineWidth := SaveLineWidth; WhatToUndo := UndoEdit; end; procedure DrawCharacter; {(ch: char)} var str: str255; begin if Info = NoInfo then begin beep; exit(DrawCharacter) end; if ch = cr then with InsertionPoint do begin h := TextStart.h; v := v + CurrentSize; SetupUndo; TextStr := ''; TextStart := InsertionPoint; exit(DrawCharacter) end; if ch = BackSpace then with InsertionPoint do begin if length(TextStr) > 0 then begin delete(TextStr, length(TextStr), 1); DisplayText(true); end; exit(DrawCharacter) end; str := ' '; {Needed for MPW} str[1] := ch; TextStr := Concat(TextStr, str); DisplayText(true); end; procedure DoText; {(loc: point)} {Handles text tool mouse clicks.} var value: extended; str: str255; isValue: boolean; begin if NoUndo then exit(DoText); ScreenToOffscreen(loc); with loc do begin InsertionPoint.h := h; InsertionPoint.v := v + 4; end; IsInsertionPoint := true; TextStart := InsertionPoint; TextStr := ''; if OptionKeyDown then with info^ do begin isValue := true; if (PreviousTool = LineTool) and (nLengths > 0) then value := plength^[mCount2] else if (PreviousTool = AngleTool) and (nAngles > 0) then value := orientation^[mCount2] else if mCount > 0 then if AreaM in Measurements then value := mArea^[mCount2] else if MeanM in Measurements then value := mean^[mCount2] else isValue := false; if isValue then begin RealToString(value, 1, precision, str); if mCount2 > 0 then mCount2 := mCount2 - 1; DrawTextString(str, TextStart, TextJust); end; end; WhatToUndo := UndoEdit; end; procedure DoFill (event: EventRecord); var loc: point; MaskBits: BitMap; BitMapSize: LongInt; tPort: GrafPtr; trect: rect; SaveGDevice: GDHandle; begin ShowWatch; loc := event.where; ScreenToOffscreen(loc); with info^ do begin tRect := PicRect; with tRect do if (right mod 16 <> 0) and not Has32BitQuickDraw then right := (right div 16) * 16 + 16; {Workaround for SeedCFill bug that results in garbage along right edge.} with MaskBits do begin RowBytes := PixelsPerLine div 8 + 1; if odd(RowBytes) then RowBytes := RowBytes + 1; bounds := tRect; BitMapSize := rowBytes * nLines; baseAddr := NewPtr(BitMapSize); if baseAddr = nil then begin beep; exit(DoFill) end; end; SaveGDevice := GetGDevice; SetGDevice(osGDevice); GetPort(tPort); SetPort(GrafPtr(osPort)); IndexToRgbForeColor(ForegroundIndex); SeedCFill(BitMapHandle(osPort^.PortPixMap)^^, MaskBits, tRect, tRect, loc.h, loc.v, nil, 0); CopyBits(MaskBits, BitMapHandle(osPort^.PortPixMap)^^, tRect, tRect, SrcOr, nil); DisposePtr(MaskBits.baseAddr); changes := true; end; {with} SetPort(tPort); SetGDevice(SaveGDevice); UpdatePicWindow; WhatToUndo := UndoEdit; end; procedure SetSprayCanSize; var TempSize: integer; Canceled: boolean; begin TempSize := GetInt('Spray can diameter in pixels(2-250):', SprayCanDiameter, Canceled); if Canceled then exit(SetSprayCanSize); if (TempSize > 1) and (TempSize <= 250) then begin SprayCanDiameter := TempSize; SprayCanRadius := SprayCanDiameter div 2; SprayCanRadius2 := SprayCanRadius * SprayCanRadius end else beep; end; procedure SetBrushSize; var TempSize: integer; Canceled: boolean; i, ticks, x, y: LongInt; v: integer; begin TempSize := GetInt('Brush Size in pixels(1..99):', BrushWidth, Canceled); if Canceled then exit(SetBrushSize); if (TempSize > 0) and (TempSize < 100) then begin BrushWidth := TempSize; BrushHeight := BrushWidth end else beep; {exit(SetBrushSize);} {Timer} x := 100; y := 100; ticks := TickCount; for i := 1 to 1000000 do v := MyGetPixel(x, y); ShowMessage(concat('ticks=', long2str(TickCount - ticks))); end; procedure SetLineWidth; var TempSize: integer; Canceled: boolean; begin TempSize := GetInt('Line Width in pixels(1..100):', LineWidth, Canceled); if Canceled then exit(SetLineWidth); if (TempSize > 0) and (TempSize <= 100) then begin LineWidth := TempSize; ShowLineWidth; end else beep; end; procedure FindWhatToCopy; var kind: integer; WhichWindow: WindowPtr; begin WhatToCopy := NothingToCopy; WhichWindow := FrontWindow; if WhichWindow = nil then exit(FindWhatToCopy); kind := WindowPeek(WhichWindow)^.WindowKind; if (CurrentTool = PickerTool) and (kind <> TextKind) then WhatToCopy := CopyColor else begin if (kind = PicKind) and measuring and (not macro) then kind := ResultsKind; case kind of PicKind: with info^, info^.RoiRect do if RoiShowing and (left >= 0) and (top >= 0) and (right <= PicRect.right) and (bottom <= PicRect.bottom) then WhatToCopy := CopySelection; HistoKind: WhatToCopy := CopyHistogram; ProfilePlotKind: WhatToCopy := CopyPlot; CalibrationPlotKind: WhatToCopy := CopyCalibrationPlot; LUTKind: if info <> NoInfo then WhatToCopy := CopyCLUT; MapKind: if info <> NoInfo then WhatToCopy := CopyGrayMap; ToolKind: WhatToCopy := CopyTools; TextKind: begin TextInfo := TextInfoPtr(WindowPeek(WhichWindow)^.RefCon); if TextInfo <> nil then with TextInfo^.TextTE^^ do if selEnd > selStart then WhatToCopy := CopyText; end; InfoKind, ResultsKind: if mCount > 0 then WhatToCopy := CopyMeasurements; otherwise end; end; end; procedure UpdateEditMenu; var DimUndo, ShowItems: boolean; str: str255; i: integer; begin with info^ do begin if CurrentKind < 0 then begin {DA is active, so activate Edit menu.} SetMenuItemText(EditMenuH, UndoItem, 'Undo'); SetMenuItemText(EditMenuH, CutItem, 'Cut'); SetMenuItemText(EditMenuH, CopyItem, 'Copy'); SetMenuItem(EditMenuH, UndoItem, true); for i := CutItem to ClearItem do SetMenuItem(EditMenuH, i, true); exit(UpdateEditMenu); end; if not (WhatToUndo in [UndoLUT, UndoMeasurement, UndoPoint]) and ((info = NoInfo) or (PixMapSize <> CurrentUndoSize)) then WhatToUndo := NothingToUndo; DimUndo := WhatToUndo = NothingToUndo; SetMenuItem(EditMenuH, UndoItem, not DimUndo); if DimUndo then SetMenuItemText(EditMenuH, UndoItem, 'Undo'); case WhatToUndo of UndoEdit: str := 'Editing'; UndoFlip: str := 'Flip'; UndoRotate: str := 'Rotate'; UndoFilter: str := 'Filter'; UndoPaste: str := 'Paste'; UndoMeasurement, UndoPoint: str := 'Measurement'; UndoTransform: str := 'Transformation'; UndoClear: str := 'Clear'; UndoZoom: str := 'Zoom'; UndoOutline: str := 'Outline'; UndoSliceDelete, UndoFirstSliceDelete: str := 'Delete Slice'; UndoLUT: str := 'LUT Change'; otherwise str := ''; end; SetMenuItemText(EditMenuH, UndoItem, concat('Undo ', str)); FindWhatToCopy; if WhatToCopy = CopySelection then str := 'Cut Selection' else str := 'Cut'; SetMenuItemText(EditMenuH, CutItem, str); SetMenuItem(EditMenuH, CutItem, (WhatToCopy = CopySelection) or (WhatToCopy = CopyText)); case WhatToCopy of NothingToCopy, CopyText: str := ''; CopySelection: str := 'Selection'; CopyCLUT: str := 'LUT'; CopyGrayMap: str := 'Gray Map'; CopyTools: str := 'Tools'; CopyPlot: str := 'Plot'; CopyCalibrationPlot: str := 'Calibration Plot'; CopyHistogram: str := 'Histogram'; CopyMeasurements: str := 'Measurements'; CopyColor: str := 'Color'; end; SetMenuItemText(EditMenuH, CopyItem, concat('Copy ', str)); SetMenuItem(EditMenuH, CopyItem, WhatToCopy <> NothingToCopy); SetMenuItem(EditMenuH, ClearItem, (WhatToCopy = CopySelection) or (WhatToCopy = CopyText)); ShowItems := (WhatsOnClip <> NothingOnClip) or (OldScrapCount <> GetScrapCount); SetMenuItem(EditMenuH, PasteItem, ShowItems); SetMenuItem(EditMenuH, ShowClipboardItem, ShowItems); ShowItems := info <> NoInfo; if CurrentKind = TextKind then SetMenuItemText(EditMenuH, FillItem, 'FindÉ') else SetMenuItemText(EditMenuH, FillItem, 'Fill'); SetMenuItem(EditMenuH, FillItem, ShowItems or (CurrentKind = TextKind)); SetMenuItem(EditMenuH, InvertItem, ShowItems); SetMenuItem(EditMenuH, DrawBoundaryItem, ShowItems); SetMenuItem(EditMenuH, DrawScaleItem, ShowItems); if (RoiShowing and EqualRect(RoiRect, PicRect)) and (CurrentKind <> TextKind) then SetMenuItemText(EditMenuH, SelectAllItem, 'Deselect All') else SetMenuItemText(EditMenuH, SelectAllItem, 'Select All'); SetMenuItem(EditMenuH, SelectAllItem, ShowItems or (CurrentKind = TextKind)); SetMenuItem(EditMenuH, DeselectItem, ShowItems and RoiShowing); SetMenuItem(EditMenuH, ScaleAndRotateItem, ShowItems); for i := RotateLeftItem to FlipHorizontalItem do SetMenuItem(EditMenuH, i, ShowItems); SetMenuItem(EditMenuH, UnZoomItem, ShowItems and ((magnification <> 1.0) or ScaleToFitWindow)); end; {with} end; procedure ZoomOut; var Width, Height, divisor, NewWidth, NewHeight: integer; OldMagnification, xratio, yratio: extended; begin with Info^ do begin if magnification < 2.0 then begin beep; exit(ZoomOut) end; OldMagnification := magnification; if magnification = 2.0 then begin magnification := 1.0; divisor := 4 end else if magnification = 3.0 then begin magnification := 2.0; divisor := 6 end else if magnification = 4.0 then begin magnification := 3.0; divisor := 8 end else begin magnification := magnification / 2.0; divisor := 4 end; if EqualRect(SrcRect, PicRect) then begin {Make window smaller} NewWidth := trunc(PicRect.right * magnification); NewHeight := trunc(PicRect.bottom * magnification); SizeWindow(wptr, NewWidth, NewHeight, true); wrect.right := NewWidth; wrect.bottom := NewHeight; SrcRect := PicRect; UpdateTitleBar; UpdatePicWindow; DrawMyGrowIcon(wptr); exit(ZoomOut); end; if ((wrect.right > PicRect.right) or (wrect.bottom > PicRect.bottom)) then begin xratio := wrect.right / PicRect.right; yratio := wrect.bottom / PicRect.bottom; if (xratio <> yratio) or ((xratio - trunc(xratio)) <> 0.0) then begin UnZoom; Exit(ZoomOut) end; SrcRect := PicRect; Magnification := xratio; UpdateTitleBar; UpdatePicWindow; DrawMyGrowIcon(wptr); Exit(ZoomOut) end; end; {with} with Info^.SrcRect, info^ do begin if magnification = 1.0 then begin width := wrect.right; height := wrect.bottom; end else begin width := round((right - left) * OldMagnification / Magnification); height := round((bottom - top) * OldMagnification / Magnification); end; left := left - (width div divisor); if left < 0 then left := 0; if (left + width) > Info^.PicRect.right then left := Info^.PicRect.right - width; top := top - (height div divisor); if top < 0 then top := 0; if (top + height) > Info^.PicRect.bottom then top := Info^.picRect.bottom - height; right := left + width; bottom := top + height; RoiShowing := false; UpdatePicWindow; DrawMyGrowIcon(wptr); UpdateTitleBar; end; ShowRoi; end; procedure DoGrow; {(WhichWindow: WindowPtr; event: EventRecord)} var NewSize: LongInt; trect, WinRect, SizeRect: rect; kind: integer; WasDigitizing: boolean; ZoomCenterH, ZoomCenterV, width, height: extended; begin kind := WindowPeek(WhichWindow)^.WindowKind; if kind = PicKind then with info^, SizeRect do begin if (PictureType = FrameGrabberType) and GrabbingToScreen then exit(DoGrow); if ScaleToFitWindow then SizeRect := qd.ScreenBits.bounds else begin right := PicRect.right + 1; bottom := PicRect.bottom + 1; if magnification > 1.0 then begin right := round(right * magnification); bottom := round(bottom * magnification); end; left := 32; top := 32; if left > right then left := right; if top > bottom then top := bottom; end end else SetRect(SizeRect, 64, 48, 2048, 2048); NewSize := GrowWindow(WhichWindow, event.where, SizeRect); if newSize = 0 then exit(DoGrow); if kind = PicKind then with Info^ do begin SetPort(wptr); WasDigitizing := digitizing; StopDigitizing; InvalRect(wrect); with trect do begin top := 0; left := 0; right := LoWrd(NewSize); bottom := HiWrd(NewSize); end; if ScaleToFitWindow then begin ScaleImageWindow(trect); wrect := trect; end else begin if trect.right > PicRect.right * magnification then trect.right := trunc(PicRect.right * magnification); if trect.bottom > PicRect.bottom * magnification then trect.bottom := trunc(PicRect.bottom * magnification); wrect := trect; with SrcRect do begin ZoomCenterH := left + (wrect.right / 2.0) / magnification; ZoomCenterV := top + (wrect.bottom / 2.0) / magnification; width := wrect.right / magnification; height := wrect.bottom / magnification; left := round(ZoomCenterH - width / 2.0); if left < 0 then left := 0; if (left + width) > PicRect.right then left := round(PicRect.right - width); top := round(ZoomCenterV - height / 2.0); if top < 0 then top := 0; if (top + height) > PicRect.bottom then top := round(picRect.bottom - height); right := round(left + width); bottom := round(top + height); wrect.right := trunc((right - left) * magnification); wrect.bottom := trunc((bottom - top) * magnification); end; savewrect := wrect; end; SizeWindow(WhichWindow, wrect.right, wrect.bottom, true); WindowState := NormalWindow; if WasDigitizing then StartDigitizing; exit(DoGrow) end; {with info^} if WhichWindow = PlotWindow then begin PlotWidth := LoWrd(NewSize); PlotHeight := HiWrd(NewSize); SetPort(PlotWindow); SizeWindow(PlotWindow, PlotWidth, Plotheight, true); InvalRect(PlotWindow^.PortRect); exit(DoGrow) end; if (kind = TextKind) then begin TextInfo := TextInfoPtr(WindowPeek(WhichWindow)^.RefCon); GrowTextWindow(NewSize); exit(DoGrow) end; if WhichWindow = ResultsWindow then begin ResultsWidth := LoWrd(NewSize); ResultsHeight := HiWrd(NewSize); SetPort(ResultsWindow); with ResultsWindow^.PortRect do SetRect(tRect, right - 12, bottom - 12, right, bottom); EraseRect(trect); {Erase Grow Box} SizeWindow(ResultsWindow, ResultsWidth, ResultsHeight, true); MoveControl(hScrollBar, -1, ResultsHeight - ScrollBarWidth); MoveControl(vScrollBar, ResultsWidth - ScrollBarWidth, -1); SizeControl(hScrollBar, ResultsWidth - 13, ScrollBarWidth + 1); SizeControl(vScrollBar, ScrollBarWidth + 1, ResultsHeight - 13); InvalRect(ResultsWindow^.PortRect); with ListTE^^.viewRect do begin right := left + ResultsWidth - ScrollBarWidth - 4; bottom := top + ResultsHeight - ScrollBarWidth; end; UpdateResultsScrollBars; ScrollResultsText; end; end; procedure ZoomIn; {(event: EventRecord)} var width, height, OldMagnification: extended; PicCenterH, PicCenterV, NewWidth, NewHeight: integer; trect: rect; begin if Info = NoInfo then begin beep; exit(ZoomIn) end; if Info^.ScaleToFitWindow then begin PutError('The magnifying glass does not work in "Scale to Fit Window" mode.'); exit(ZoomIn) end; if BitAnd(Event.modifiers, OptionKey) = OptionKey then begin ZoomOut; WhatToUndo := NothingToUndo; exit(ZoomIn) end; with Info^ do begin OldMagnification := magnification; if magnification = 1.0 then magnification := 2.0 else if magnification = 2.0 then magnification := 3.0 else if magnification = 3.0 then magnification := 4.0 else begin magnification := magnification * 2.0; if magnification > 64.0 then begin magnification := 64.0; exit(ZoomIn) end; end; if (WindowState = NormalWindow) and EqualRect(SrcRect, PicRect) then {Make window bigger?} with trect do begin NewWidth := trunc(PicRect.right * magnification); NewHeight := trunc(PicRect.bottom * magnification); if NewWidth <= 640 then begin GetWindowRect(wptr, trect); if ((left + NewWidth) <= ScreenWidth) and ((top + NewHeight) <= ScreenHeight) then begin SizeWindow(wptr, NewWidth, NewHeight, true); wrect.right := NewWidth; wrect.bottom := NewHeight; end; end; end; end; {with} with Info^.SrcRect, Info^ do begin PicCenterH := left + round(event.where.h / OldMagnification); PicCenterV := top + round(event.where.v / OldMagnification); width := wrect.right / magnification; height := wrect.bottom / magnification; left := PicCenterH - round(width / 2.0); if left < 0 then left := 0; if (left + width) > PicRect.right then left := PicRect.right - round(width); top := PicCenterV - round(height / 2.0); if top < 0 then top := 0; if (top + height) > PicRect.bottom then top := picRect.bottom - round(height); right := left + round(width); bottom := top + round(height); wrect.right := trunc((right - left) * magnification); wrect.bottom := trunc((bottom - top) * magnification); SizeWindow(wptr, wrect.right, wrect.bottom, true); RoiShowing := false; UpdatePicWindow; DrawMyGrowIcon(wptr); UpdateTitleBar; WhatToUndo := UndoZoom; ShowRoi; end; {with} end; procedure SynchScroll; var n: integer; TempInfo, SaveInfo: InfoPtr; begin SaveInfo := info; if allsamesize then for n := 1 to nPics do begin TempInfo := pointer(WindowPeek(PicWindow[n])^.RefCon); TempInfo^.SrcRect := info^.SrcRect; TempInfo^.magnification := Info^.magnification; info := TempInfo; UpdatePicWindow; Info := SaveInfo; end else PutError('Synchronized scrolling requires all images and all windows to be the same size.'); end; procedure Scroll; {(event: EventRecord)} var hstart, vstart, DeltaH, DeltaV, width, height: integer; loc: point; SaveSR: rect; WasDigitizing: boolean; begin with info^ do begin if ScaleToFitWindow then begin PutError('Scrolling does not work in "Scale to Fit Window" mode.'); exit(Scroll) end; WasDigitizing := digitizing; StopDigitizing; with event.where do begin hstart := h; vstart := v end; with SrcRect do begin width := right - left; height := bottom - top end; SaveSR := SrcRect; while StillDown do begin GetMouse(loc); DeltaH := hstart - loc.h; DeltaV := vstart - loc.v; with SrcRect do begin left := SaveSR.left + DeltaH; if left < 0 then left := 0; if (left + width) > PicRect.right then left := PicRect.right - width; right := left + width; top := SaveSR.top + DeltaV; if top < 0 then top := 0; if (top + height) > PicRect.bottom then top := PicRect.bottom - height; bottom := top + height; end; UpdatePicWindow; DrawMyGrowIcon(wptr); end; WhatToUndo := NothingToUndo; ShowRoi; if OptionKeyDown and (nPics > 1) then SynchScroll; if WasDigitizing then StartDigitizing; end; {with info^} end; procedure ConverToSystemClipboard; {Converts local clipboard to system-wide clipboard} {when quitting or switching to other programs.} var PicH: PicHandle; err: LongInt; saveClipRgn: RgnHandle; begin PicH := nil; if ((WhatsOnClip = RectPic) or (WhatsOnClip = CameraPic)) and (ClipBuf <> nil) and not ClipboardConverted then with ClipBufInfo^ do begin ShowWatch; SetPort(GrafPtr(osPort)); saveClipRgn := NewRgn; GetClip(saveClipRgn); ClipRect(RoiRect); LoadLUT(ctable); {Switch to original LUT} RGBForeColor(BlackRGB); RGBBackColor(WhiteRGB); PicH := OpenPicture(RoiRect); with osPort^ do CopyBits(BitMapHandle(portPixMap)^^, BitMapHandle(portPixMap)^^, RoiRect, RoiRect, SrcCopy, nil); ClosePicture; if info <> NoInfo then LoadLUT(info^.ctable); {Restore LUT} if (PicH <> nil) or ClipTextInBuffer then begin err := ZeroScrap; if err = NoErr then begin if PicH <> nil then begin hlock(handle(PicH)); err := PutScrap(GetHandleSize(handle(PicH)), 'PICT', handle(PicH)^); hunlock(handle(PicH)); DisposeHandle(handle(PicH)); end; if (err = noErr) and ClipTextInBuffer then err := PutScrap(TextBufSize, 'TEXT', ptr(TextBufP)); end; {if err=NoErr} end; ClipboardConverted := true; SetClip(saveClipRgn); DisposeRgn(saveClipRgn); end; {with} end; procedure SetupOperation; {(item: integer)} var AutoSelectAll: boolean; begin if NotinBounds then exit(SetupOperation); if item = DrawBoundaryItem then if NoSelection then exit(SetupOperation); if item = InvertItem then if not CheckCalibration then exit(SetupOperation); StopDigitizing; AutoSelectAll := not Info^.RoiShowing; if AutoSelectAll then SelectAll(true); SetupUndo; WhatToUndo := UndoEdit; case Item of FillItem: begin CurrentOp := PaintOp; OpPending := true end; InvertItem: begin CurrentOp := InvertOp; OpPending := true end; DrawBoundaryItem: begin CurrentOp := FrameOp; OpPending := true end; end; if AutoSelectAll then KillRoi; RoiUpdateTime := 0; {Forces outline to be redrawn in scale-to-fit mode.} end; procedure DoUndo; var aok: boolean; begin case WhatToUndo of UndoMeasurement: UndoLastMeasurement(true); UndoPoint: begin Undo; UpdatePicWindow; UndoLastMeasurement(true); WhatToUndo := NothingToUndo; end; UndoZoom: begin ZoomOut; if info^.magnification < 2 then WhatToUndo := NothingToUndo; end; UndoOutLine: begin undo; if WandAutoMeasure then UndoLastMeasurement(true); WhatToUndo := NothingToUndo; UpdatePicWindow; end; UndoSliceDelete, UndoFirstSliceDelete: if info^.StackInfo <> nil then with info^.StackInfo^ do begin if WhatToUndo = UndoFirstSliceDelete then CurrentSlice := 0; aok := AddSlice(false); if aok then begin Undo; UpdatePicWindow; end else if CurrentSlice = 0 then CurrentSlice := 1; end; UndoLUT: begin UndoLutChange; DrawMap; DensitySlicing := false; end; otherwise begin if UndoFromClip then OpPending := false; if not OpPending then undo; WhatToUndo := NothingToUndo; if IsInsertionPoint then begin InsertionPoint := TextStart; TextStr := ''; end; UpdatePicWindow; if OpPending and (CurrentOp = PasteOp) then begin OpPending := false; KillRoi; end; OpPending := false; end; end; {case} end; end.