<> <> <> <> <> DIRECTORY Basics USING [bytesPerWord], Cursors USING [SetCursor], FS USING [Error, StreamOpen], Icons USING [DrawIconProc, IconFileFormat, IconFlavor, iconH, IconRef, IconRep, iconW], Imager USING [ClipRectangleI, Font, SetColor, SetFont, SetXYI, ShowRope, white], ImagerBackdoor USING [DrawBits], InputFocus USING [GetInputFocus, SetInputFocus], IO USING [EndOfStream, SetIndex, STREAM, UnsafeGetBlock], Menus USING [FindEntry, MenuEntry], MessageWindow USING [Append, Blink, Confirm], Process USING [Detach], Rope USING [Concat, Fetch, Length, ROPE, Run, Substr], TIPUser USING [InstantiateNewTIPTable, TIPScreenCoords, TIPTable], UserProfile USING [CallWhenProfileChanges, ProfileChangedProc, Token], VFonts USING [CharWidth, EstablishFont, FontAscent, StringWidth], ViewerClasses USING [ModifyProc, NotifyProc, Viewer], ViewerOps USING [DestroyViewer, OpenIcon, PaintViewer, ChangeColumn, SaveViewer], ViewerPrivate USING [], ViewerTools USING [SelPosRec, SetSelection], WindowManager USING [colorDisplayOn]; IconsImpl: CEDAR MONITOR IMPORTS Cursors, FS, Imager, ImagerBackdoor, InputFocus, IO, Menus, MessageWindow, Process, Rope, TIPUser, UserProfile, VFonts, ViewerOps, ViewerTools, WindowManager EXPORTS Icons, ViewerPrivate SHARES Menus = BEGIN OPEN Icons; ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; Viewer: TYPE = ViewerClasses.Viewer; <> bigFont: Imager.Font _ NIL; bigFontHeight: INTEGER _ 0; smallFont: Imager.Font _ NIL; smallFontHeight: INTEGER _ 0; suppressIconPrefix: ROPE _ NIL; maxIcons: CARDINAL = 64; IconArray: TYPE = ARRAY [0..maxIcons) OF IconRef; icons: REF IconArray _ NEW[IconArray _ ALL[NIL]]; nextFlavor: IconFlavor _ document; <> DrawIcon: PUBLIC ENTRY DrawIconProc = { leading: INTEGER = 3; iconInfo: IconRef ~ icons[ORD[flavor]]; prefix: ROPE _ NIL; IF iconInfo.proc=NIL THEN { ImagerBackdoor.DrawBits[context: context, base: LOOPHOLE[iconInfo], wordsPerLine: iconW/16, sMin: 0, fMin: 0, sSize: iconH, fSize: iconW, tx: x, ty: y+iconH]; IF iconInfo.label THEN { iconFont: Imager.Font; fontHeight: INTEGER; ix: INTEGER _ x+iconInfo.lx; iy: INTEGER _ y+iconInfo.ly+iconInfo.lh; indent: INTEGER _ 0; segments: LIST OF ROPE _ NIL; IF VFonts.StringWidth[label, bigFont]<=iconInfo.lw THEN { iconFont _ bigFont; fontHeight _ bigFontHeight; segments _ LIST[label]; } ELSE { iconFont _ smallFont; fontHeight _ smallFontHeight; indent _ VFonts.CharWidth[' , iconFont]; segments _ Decompose[label, iconInfo.lw, indent, iconFont]; }; Imager.ClipRectangleI[context, ix, iy, iconInfo.lw, -iconInfo.lh]; Imager.SetFont[context, iconFont]; IF iconInfo.invertLabel THEN Imager.SetColor[context, Imager.white]; FOR list: LIST OF ROPE _ segments, list.rest UNTIL list=NIL DO rope: ROPE ~ list.first; iy _ iy-fontHeight; Imager.SetXYI[context, ix, iy]; iy _ iy-leading; Imager.ShowRope[context, rope]; IF list=segments THEN ix _ ix+indent; ENDLOOP; }; } ELSE iconInfo.proc[flavor, context, x, y, label]; }; GetFonts: ENTRY PROC = { bigFont _ VFonts.EstablishFont["Helvetica", 8]; bigFontHeight _ VFonts.FontAscent[bigFont]; smallFont _ VFonts.EstablishFont["Helvetica", 7]; smallFontHeight _ VFonts.FontAscent[smallFont]; }; ChangePrefix: UserProfile.ProfileChangedProc ~ { suppressIconPrefix _ UserProfile.Token[key: "Viewers.SuppressIconPrefix", default: NIL]; }; Decompose: PROC [name: ROPE, width, indent: INTEGER, font: Imager.Font] RETURNS [segments: LIST OF ROPE _ NIL] = { start: INT _ 0; stop: INT _ name.Length[]; extension: ROPE _ NIL; Build: PROC[start: INT] RETURNS[LIST OF ROPE] ~ { maxw: INTEGER _ (IF start=0 THEN width ELSE width-indent); sw: INTEGER _ 0; -- width of string so far next: INT _ -1; IF start>=stop THEN RETURN[IF extension=NIL THEN NIL ELSE LIST[extension]]; FOR i: INT IN[start..stop) DO c: CHAR ~ name.Fetch[i]; sw _ sw+VFonts.CharWidth[c, font]; IF sw>maxw THEN { IF next<0 THEN next _ MAX[i, start+1]; EXIT }; -- stop here SELECT c FROM '], '>, '/, ' => next _ i+1; -- could end a line IN['A..'Z], IN['0..'9] => IF i>start THEN next _ i; -- next line could start here ENDCASE; REPEAT FINISHED => next _ stop; ENDLOOP; RETURN[CONS[name.Substr[start: start, len: next-start], Build[next]]]; }; IF suppressIconPrefix#NIL THEN { prefixLen: INT ~ suppressIconPrefix.Length[]; IF Rope.Run[s1: name, s2: suppressIconPrefix, case: FALSE]=prefixLen THEN start _ prefixLen; }; FOR i: INT DECREASING IN[start..stop) DO IF name.Fetch[i]='. THEN { extension _ name.Substr[start: i]; stop _ i; EXIT }; ENDLOOP; RETURN[Build[start]]; }; NewFlavor: ENTRY PROC RETURNS [newFlavor: IconFlavor] = { newFlavor _ nextFlavor; nextFlavor _ SUCC[newFlavor]; IF LOOPHOLE[nextFlavor, CARDINAL] >= maxIcons THEN ERROR; }; NewIcon: PUBLIC PROC [info: IconRef] RETURNS [newFlavor: IconFlavor] = { newFlavor _ NewFlavor[]; icons[ORD[newFlavor]] _ info; }; IconRefFromStream: PROC[stream: STREAM, n: CARDINAL] RETURNS [IconRef] ~ { format: IconFileFormat; -- 512 words! bytesPerIcon: INT ~ SIZE[IconFileFormat]*Basics.bytesPerWord; IO.SetIndex[stream, bytesPerIcon*n]; TRUSTED { base: LONG POINTER TO IconFileFormat ~ @format; bytesRead: INT ~ IO.UnsafeGetBlock[stream, [base: LOOPHOLE[base], count: bytesPerIcon]]; IF bytesRead#bytesPerIcon THEN ERROR IO.EndOfStream[stream]; }; RETURN[NEW[IconRep _ [bits: format.bits, label: format.label, invertLabel: format.invertLabel, lx: format.lx, ly: format.ly, lw: format.lw, lh: format.lh]]]; }; IconsFromFile: ENTRY PROC [file: ROPE] RETURNS [first: IconFlavor, nRead: INTEGER] ~ { ENABLE UNWIND => NULL; stream: STREAM ~ FS.StreamOpen[file]; first _ nextFlavor; FOR nRead _ 0, nRead+1 DO info: IconRef ~ IconRefFromStream[stream, nRead ! IO.EndOfStream => EXIT]; icons[ORD[nextFlavor]] _ info; nextFlavor _ nextFlavor.SUCC; ENDLOOP; }; NewIconFromFile: PUBLIC PROC [file: ROPE, n: CARDINAL] RETURNS [newFlavor: IconFlavor] ~ { stream: STREAM ~ FS.StreamOpen[file]; info: IconRef ~ IconRefFromStream[stream, n ! IO.EndOfStream => GOTO Fail]; RETURN[NewIcon[info]]; EXITS Fail => RETURN[unInit]; }; <> IconNotify: PUBLIC ViewerClasses.NotifyProc = { mx, my: INTEGER; closeOthers, switchDisplays: BOOL _ FALSE; FOR l: LIST OF REF ANY _ input, l.rest UNTIL l = NIL DO WITH l.first SELECT FROM z: TIPUser.TIPScreenCoords => [mouseX: mx, mouseY: my] _ z^; z: ATOM => SELECT z FROM $OpenDesktop, $ResetDesktop => { IF self.class.flavor = $DeskTop THEN self.class.notify[self, input]; RETURN}; $SetInputFocus => SELECT self.class.flavor FROM $Text => ViewerTools.SetSelection[self, NEW[ViewerTools.SelPosRec_[]]]; $Typescript, $Chat => ViewerTools.SetSelection[self]; ENDCASE; $CloseOthers => closeOthers _ TRUE; $Color => IF WindowManager.colorDisplayOn THEN ViewerOps.ChangeColumn[self, color]; $Delete => DestroyIcon[self]; $Save => { entry: Menus.MenuEntry = Menus.FindEntry[self.menu, "Save"]; MessageWindow.Append["Saving icon . . . ", TRUE]; IF entry = NIL THEN ViewerOps.SaveViewer[self] ELSE entry.proc[self]; MessageWindow.Append["done."]}; $Left => ViewerOps.ChangeColumn[self, left]; $MouseMove => Cursors.SetCursor[textPointer]; $Open => ViewerOps.OpenIcon[self, closeOthers]; $Right => ViewerOps.ChangeColumn[self, right]; $Select => SelectIcon[self]; $TogglePos => ViewerOps.ChangeColumn[self, SELECT self.column FROM left => right, right => left, color => left, ENDCASE => ERROR]; ENDCASE => NULL; ENDCASE => NULL; ENDLOOP; }; IconModify: PUBLIC ViewerClasses.ModifyProc = { SELECT change FROM set => ViewerOps.PaintViewer[self, all]; kill => {selectedIcon _ NIL; ViewerOps.PaintViewer[self, all]}; ENDCASE; }; selectedIcon: PUBLIC ViewerClasses.Viewer _ NIL; DestroyIcon: PROC [icon: Viewer] = { IF icon.inhibitDestroy THEN { MessageWindow.Append["That icon cannot be destroyed.", TRUE]; MessageWindow.Blink[]; RETURN}; IF icon.link = NIL AND (icon.newVersion OR icon.newFile) THEN TRUSTED {Process.Detach[FORK ConfirmDestroyIcon[icon]]} -- fork for confirmation ELSE ViewerOps.DestroyViewer[icon]; }; ConfirmDestroyIcon: PROC [icon: Viewer] = { IF MessageWindow.Confirm[Rope.Concat["Confirm delete (and loss of edits) for icon: ", icon.name]] THEN ViewerOps.DestroyViewer[icon]; }; SelectIcon: PROC [viewer: Viewer] = { IF InputFocus.GetInputFocus[].owner#viewer THEN { InputFocus.SetInputFocus[NIL]; -- kill old so we don't smash selectedIcon selectedIcon _ viewer; InputFocus.SetInputFocus[viewer]; }; }; <> iconTIP: PUBLIC TIPUser.TIPTable _ TIPUser.InstantiateNewTIPTable["Icons.tip"]; {-- Font and Icon initialization takes place here. ok: BOOL _ TRUE; GetFonts[]; [] _ IconsFromFile["Standard.icons" ! FS.Error => IF error.group # bug THEN {ok _ FALSE; CONTINUE}]; IF NOT ok THEN [] _ IconsFromFile["[]<>Standard.icons"]; <> }; UserProfile.CallWhenProfileChanges[ChangePrefix]; END.