CharacterSetView.st
author Claus Gittinger <cg@exept.de>
Tue, 09 Jul 2019 20:53:39 +0200
changeset 6083 7a2c0a30e75c
parent 5985 93b6ba79239b
child 6108 218347e50097
permissions -rw-r--r--
#REFACTORING by exept class: NoteBookView changed: #buttonPress:x:y: Transcript showCR:(... bindWith:...) -> Transcript showCR:... with:...

"{ Encoding: utf8 }"

"
 COPYRIGHT (c) 2004 by eXept Software AG
              All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
"{ Package: 'stx:libwidg2' }"

"{ NameSpace: Smalltalk }"

View subclass:#CharacterSetView
	instanceVariableNames:'codePageHolder selectedCodePointHolder masterViewOrNil
		encoderOrNil'
	classVariableNames:''
	poolDictionaries:''
	category:'Views-Special'
!

!CharacterSetView class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 2004 by eXept Software AG
              All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

documentation
"
    Can be used both as an informative display of a font's characters
    (opened via the fontPanels-preview-popUpMenu)
    or to insert characters into a textView 
    (opened by a textEditors misc-specialCharacters menu).

    Author:
        Claus Gittinger
"
!

examples
"
    CharacterSetView openOn:(Button defaultFont).


    CharacterSetView 
        openAsInputFor:Transcript
        label:'Input to Transcript'
        clickLabel:'Click to input character'
"
! !

!CharacterSetView class methodsFor:'instance creation'!

new
    ^ self basicNew initialize.
! !

!CharacterSetView class methodsFor:'startup'!

open
    self openOn:(View defaultFont)

    "
     self open
    "
!

openAsInputFor:aView label:viewLabel clickLabel:clickLabel
    ^ self 
        openOn:aView font
        label:viewLabel 
        clickLabel:clickLabel
        asInputFor:aView
!

openOn:aFont
    ^ self
        openOn:aFont 
        label:aFont printString
        clickLabel:'Click on glyph to see its codePoint.'

    "
     self openOn:(View defaultFont).
    "
!

openOn:aFont in:aTopView label:viewLabel clickLabel:clickLabel asInputFor:aConsumingView encoder:aCharacterEncoderOrNil
    |panel1 panel2 panel3 v 
     bNext bPrev bFirst bLast comboList bHtml 
     rangeLabel codePointLabel
     first last next prev enable update updateCodePoint minPage maxPage insertCharacter deviceFont minCode maxCode
     htmlHolder codePageList|

    htmlHolder := false asValue.

    aCharacterEncoderOrNil isNil ifTrue:[
        deviceFont := aFont onDevice:Screen current.
        minCode := deviceFont minCode.
        maxCode := deviceFont maxCode.
    ] ifFalse:[
        minCode := aCharacterEncoderOrNil minCode.
        maxCode := aCharacterEncoderOrNil maxCode.
    ].

    minPage := minCode >> 8.
    maxPage := maxCode >> 8.

    panel1 := HorizontalPanelView in:aTopView.
    panel1 origin:(0.0 @ 0.0) corner:(1.0 @ 0.0).
    panel1 bottomInset:-30.
    panel1 horizontalLayout:#leftSpace.

    panel2 := HorizontalPanelView in:aTopView.
    panel2 origin:(0.0 @ 0.0) corner:(1.0 @ 0.0).
    panel2 topInset:30; bottomInset:-60.
    panel2 horizontalLayout:#leftSpace.

    panel3 := HorizontalPanelView in:aTopView.
    panel3 origin:(0.0 @ 1.0) corner:(1.0 @ 1.0).
    panel3 topInset:-30.
    panel3 horizontalLayout:#leftSpace.

    v := self origin:(0.0@0.0) corner:(1.0@1.0) in:aTopView.
    v topInset:70; bottomInset:40.
    v font:aFont.
    v codePageHolder value:minPage.
    v characterEncoding:(aFont encoding).
    v encoder:aCharacterEncoderOrNil.

    aConsumingView notNil ifTrue:[
        v consumingView:aConsumingView.
        v useSameFontAs:aConsumingView.
    ].

    "/ actions
    aConsumingView notNil ifTrue:[
        insertCharacter := 
            [:char | 
                |pastedString|

                (v consumingView notNil and:[v consumingView shown]) ifTrue:[
                    pastedString := (CharacterEncoder encode:char from:v consumingView characterEncoding into:#unicode) asCharacter asString.
                    htmlHolder value ifTrue:[
                        "/ insert html
                        pastedString := HTMLUtilities escapeCharacterEntities:pastedString.
                    ].
                    pastedString do:[:eachChar |
                        v consumingView dispatchEvent:(WindowEvent keyPress:eachChar x:0 y:0 view:v).
                        v consumingView dispatchEvent:(WindowEvent keyRelease:eachChar x:0 y:0 view:v).
                    ].
               ].
           ].
    ].

    updateCodePoint := 
        [
            |selectedCodePoint selectedChar 
             isLetter isDigit isUppercase isLowercase 
             codeString decoded decodedString|

            selectedCodePoint := v selectedCodePoint.
            selectedCodePoint isNil ifTrue:[
                codePointLabel label:clickLabel
            ] ifFalse:[
                selectedChar := Character value:selectedCodePoint.
                (#(unicode #'iso10646-1' #'iso8859-1' ascii) includes:(v font encoding))
                ifTrue:[
                    isLetter := selectedChar isNationalLetter.
                    isDigit := selectedChar isNationalDigit.
                    isUppercase := selectedChar isUppercase.
                    isLowercase := selectedChar isLowercase.
                ] ifFalse:[
                    isLetter := isDigit := isUppercase := isLowercase := false.     "/ actually: unknown
                ].
                aCharacterEncoderOrNil isNil ifTrue:[
                    decodedString := ''.
                    codeString := 'u' , ((selectedCodePoint printStringRadix:16) leftPaddedTo:4 with:$0)
                ] ifFalse:[
                    decoded := aCharacterEncoderOrNil decode:selectedCodePoint.
                    decodedString := 'u' , ((decoded printStringRadix:16) leftPaddedTo:4 with:$0).
                    codeString := ((selectedCodePoint printStringRadix:16) leftPaddedTo:4 with:$0)
                ].
                codePointLabel 
                    label:
                        ('Selected: %1 %2 %3 %4 %5 %6'
                            bindWith:codeString
                            with:((selectedCodePoint printString) leftPaddedTo:5)
                            with:(isUppercase ifTrue:'Uc' ifFalse:[isLowercase ifTrue:'lc' ifFalse:''])
                            with:(isLetter ifTrue:'Letter' ifFalse:[(isDigit ifTrue:'Digit' ifFalse:'')])
                            with:decodedString
                        ).
            ].
            codePointLabel repairDamage.

            v consumingView notNil ifTrue:[
                selectedCodePoint notNil ifTrue:[
                    insertCharacter value:(Character value:selectedCodePoint).
                    v selectedCodePointHolder setValue:nil.
                ]
            ]
        ].

    update := 
        [
            |uOffs selectedCodePoint lbl nm unicodeBlock|

            uOffs := v codePage * 16r0100.

            aCharacterEncoderOrNil isNil ifTrue:[
                lbl := 'u%1..u%2 %4'.
                unicodeBlock := (Character value:uOffs) unicodeBlock.
            ] ifFalse:[
                nm := aCharacterEncoderOrNil userFriendlyNameOfEncoding.
                lbl := '%1..%2 / %3 %4'.
                unicodeBlock := ''.
            ].
            rangeLabel label:(lbl 
                        bindWith:((uOffs printStringRadix:16) leftPaddedTo:4 with:$0)
                        with:(((uOffs + 16rFF) printStringRadix:16) leftPaddedTo:4 with:$0)
                        with:nm
                        with:unicodeBlock).
            rangeLabel repairDamage.
        ].

    minPage ~~ maxPage ifTrue:[
        enable := [
                v codePage > minPage ifTrue:[ 
                    bPrev enable. bFirst enable. 
                ] ifFalse:[
                    bPrev disable. bFirst disable. 
                ].
                v codePage < maxPage ifTrue:[ 
                    bNext enable. bLast enable. 
                ] ifFalse:[
                    bNext disable. bLast disable. 
                ].
            ].
    ].

    next := 
        [
            v codePage:(v codePage + 1). 
            enable value.
            update value.
        ].

    prev := 
        [
            v codePage:(v codePage - 1). 
            enable value.
            update value.
        ].

    first := 
        [
            v codePage:minPage. 
            enable value.
            update value.
        ].

    last := 
        [
            v codePage:maxPage. 
            enable value.
            update value.
        ].

    minPage ~~ maxPage ifTrue:[
        comboList := ComboListView extent:(16@16) in:panel1.
        comboList menuButton origin:0@0.
        "/ comboList extent:(16@16).
        comboList label:''.
        "/ comboList helpKey:#commonUnicodeBlocks.
        comboList helpText:(self resources string:'Common Unicode blocks').
        codePageList := 
            #(
                (16r00   'Latin')          
                (16r03   'Greek')  
                (16r05   'Hebrew')  
                (16r06   'Arabic')
                (16r22   'Math')   
                (16r29   'Math Symbols') 
                (16r2A   'Math Operators') 
                (16r21   'Arrows & Math Letters') 
                (16r23   'Technical') 
                (16r20   'Currency') 
                (16r33   'Units') 
                (16r27   'Dingbat')
                (16r25   'Block Graphic') 
            ).
        comboList 
            list:   (codePageList collect:#second);
            values: (codePageList collect:#first). 
        comboList action:
            [:cp | 
                v codePage:cp. 
                enable value.
                update value.
            ].

        bFirst := Button label:(ToolbarIconLibrary start16x16Icon) in:panel1.
        bFirst action:first.

        bPrev := Button label:(ToolbarIconLibrary back16x16Icon) in:panel1.
        bPrev controller beTriggerOnDown.
        bPrev action:prev.
        bPrev disable.
        bPrev autoRepeat:true.

        bNext := Button label:(ToolbarIconLibrary forward16x16Icon) in:panel1.
        bNext controller beTriggerOnDown.
        bNext action:next.
        bNext autoRepeat:true.

        bLast := Button label:(ToolbarIconLibrary finish16x16Icon) in:panel1.
        bLast action:last.
        bLast disable.
    ].

    rangeLabel := Label label:'RangeStart .. RangeStop' in:panel2.

    aConsumingView notNil ifTrue:[
        panel3 add:(bHtml := CheckBox label:'HTML' model:htmlHolder).
        "/ bHtml helpKey:#insertHTML.
        bHtml helpText:(self resources string:'Insert HTML-encoding (instead of character)').
    ].

    codePointLabel := Label label:clickLabel in:panel3.
    codePointLabel foregroundColor:(Color blue).

    update value.
    enable value.

    v selectedCodePointHolder onChangeEvaluate:updateCodePoint.
    v codePageHolder onChangeEvaluate:update.

    "/  w := v preferredWidth max:(panel preferredWidth).
    "/  h := v preferredHeight + (panel preferredHeight).
    "/  v extent:(w @ h).
    ^ v

    "
     self openOn:(View defaultFont).
     self openOn:(Font family:'courier' face:'medium' style:'roman' size:12 encoding:'iso10646-1').
    "

    "Modified: / 11-10-2006 / 22:30:32 / cg"
    "Modified: / 19-01-2018 / 12:16:03 / stefan"
    "Modified: / 08-01-2019 / 18:27:12 / Claus Gittinger"
!

openOn:aFont label:viewLabel clickLabel:clickLabel
    ^ self 
        openOn:aFont 
        label:viewLabel 
        clickLabel:clickLabel 
        asInputFor:nil
!

openOn:aFont label:viewLabel clickLabel:clickLabel asInputFor:aView
    ^ self
        openOn:aFont 
        label:viewLabel 
        clickLabel:clickLabel 
        asInputFor:aView 
        encoder:nil

    "
     self openOn:(View defaultFont).
     self openOn:(Font family:'courier' face:'medium' style:'roman' size:12 encoding:'iso10646-1').
    "

    "Modified: / 08-01-2019 / 18:17:33 / Claus Gittinger"
!

openOn:aFont label:viewLabel clickLabel:clickLabel asInputFor:aConsumingView encoder:aCharacterEncoderOrNil
    |top v minPage maxPage deviceFont minCode maxCode
     htmlHolder|

    htmlHolder := false asValue.

    aCharacterEncoderOrNil isNil ifTrue:[
        deviceFont := aFont onDevice:Screen current.
        minCode := deviceFont minCode.
        maxCode := deviceFont maxCode.
    ] ifFalse:[
        minCode := aCharacterEncoderOrNil minCode.
        maxCode := aCharacterEncoderOrNil maxCode.
    ].

    minPage := minCode >> 8.
    maxPage := maxCode >> 8.

    top := StandardSystemView new.
    top label:viewLabel.

    v := self
            openOn:aFont in:top 
            label:viewLabel clickLabel:clickLabel 
            asInputFor:aConsumingView encoder:aCharacterEncoderOrNil.

    "/ top extent:(top preferredExtent).
    top extent:(v preferredExtent max:(400 @ top preferredHeight)).
    true ifTrue:[
        aConsumingView notNil ifTrue:[
            top application:(aConsumingView application).
            top beSlave.
        ].
        top open.
    ] ifFalse:[
        top openModal
    ].        
    ^ v

    "
     self openOn:(View defaultFont).
     self openOn:(Font family:'courier' face:'medium' style:'roman' size:12 encoding:'iso10646-1').
    "

    "Modified: / 11-10-2006 / 22:30:32 / cg"
    "Modified: / 08-01-2019 / 14:24:02 / Claus Gittinger"
! !

!CharacterSetView methodsFor:'accessing'!

codePage
    ^ codePageHolder value
!

codePage:pageNr
    codePageHolder value:pageNr.
!

codePageHolder
    ^ codePageHolder
!

consumingView
    ^ masterViewOrNil

    "Created: / 08-01-2019 / 18:16:44 / Claus Gittinger"
!

encoder:aCharacterEncoder
    encoderOrNil := aCharacterEncoder
!

selectedCodePoint
    ^ selectedCodePointHolder value
!

selectedCodePointHolder
    ^ selectedCodePointHolder
! !

!CharacterSetView methodsFor:'change & update'!

update:something with:aParameter from:changedObject
    |newFont|

    something == #font ifTrue:[
        newFont := masterViewOrNil font.
        self font:newFont.
        super characterEncoding:newFont encoding.
        ^ self.
    ].
    super update:something with:aParameter from:changedObject
! !

!CharacterSetView methodsFor:'drawing'!

redraw
    |wCol hRow dY codePage selectedCodePoint encoder|

    (encoder := encoderOrNil) notNil ifTrue:[
        (encoder isBehavior) ifTrue:[
            encoder := encoder new.
        ]    
    ].
    
    codePage := self codePage.
    selectedCodePoint := self selectedCodePoint.

    wCol := width / 16.
    hRow := height / 16.

    dY := (hRow - (gc font height)) // 2 + (gc font ascent).

    0 to:15 do:[:row |
        |y0 y1 y rowBase|

        rowBase := row * 16r10.
        y := y0 := row * hRow.
        y := y rounded asInteger.
        y := y + dY.

        y1 := (row+1) * hRow.
        y1 := y1 rounded asInteger.

        0 to:15 do:[:col |
            |x0 x1 x codePoint decodedCodePoint s|

            codePoint := decodedCodePoint := (codePage * 16r100) + (rowBase + col).
            encoder notNil ifTrue:[
                [
                    decodedCodePoint := (encoder decode:codePoint) ? 0
                ] on:DecodingError do:[:ex|
                    decodedCodePoint := ex defaultValue.
                ].
            ].
            s := (Character value:decodedCodePoint) asString.

            x := x0 := (col * wCol) rounded asInteger.
            x := x rounded asInteger.
            x := x + (wCol / 2).
            x := x - ((gc font widthOf:s) // 2).

            x1 := ((col+1) * wCol) rounded asInteger.
            x1 := x1 rounded asInteger.

            codePoint == selectedCodePoint ifTrue:[
                gc paint:(Color red).
                gc fillRectangle:((x0+1)@(y0+1) corner:(x1)@(y1)).
                gc paint:(self whiteColor).
                gc displayString:s x:x y:y.
                gc paint:(self blackColor).
            ] ifFalse:[
                gc displayString:s x:x y:y.
            ].
        ].
    ].

    gc paint:(Color veryLightGrey).
    0 to:16 do:[:col |
        |x|

        x := (col * wCol) rounded asInteger.
        gc displayLineFromX:x y:0 toX:x y:height-1.
    ].

    0 to:15 do:[:row |
        |y|

        y := (row * hRow) rounded asInteger.
        gc displayLineFromX:0 y:y toX:width y:y.
    ].
    gc paint:(Color black).

    "
     (self extent:300@600) open
    "

    "Modified: / 12-11-2017 / 13:47:16 / cg"
!

sizeChanged:how
    super sizeChanged:how.

    self clear.
    self invalidate.
! !

!CharacterSetView methodsFor:'event handling'!

buttonPress:button x:x y:y
    |wCol hRow row col code|

    wCol := width / 16.
    hRow := height / 16.

    row := y // hRow.
    col := x // wCol.

    code := (self codePage*16r0100) + (row * 16) + col.
    selectedCodePointHolder value:code.
!

codePageChanged
    realized ifTrue:[
        self invalidate.
        "/ self clear.
        "/ self redraw.
    ].

    "Modified: / 08-01-2019 / 11:21:18 / Claus Gittinger"
!

keyPress:key x:x y:y
    <resource: #keyboard (#NextPage #PreviousPage 
                          #CursorRight #CursorLeft
                          #CursorDown #CursorUp #Escape)>

    |cp ncp|

    key == #NextPage ifTrue:[
        codePageHolder value < 16r100 ifTrue: [
            codePageHolder value:(codePageHolder value + 1).
            selectedCodePointHolder value: nil.
        ].
        ^ self 
    ].
    key == #PreviousPage ifTrue:[  
        codePageHolder value > 0 ifTrue: [
            codePageHolder value:(codePageHolder value - 1).
            selectedCodePointHolder value: nil.
        ].
        ^ self 
    ].

    key == #Escape ifTrue:[
        masterViewOrNil notNil ifTrue:[
            self topView closeRequest.
            masterViewOrNil requestFocus.
        ].
        ^ self.
    ].

    key isCharacter ifTrue:[
        "/ others are forwarded to the controlled view
        selectedCodePointHolder value:key codePoint.
    ] ifFalse:[
        (masterViewOrNil notNil and:[masterViewOrNil shown]) ifTrue:[
            masterViewOrNil keyPress:key x:0 y:0.
            masterViewOrNil keyRelease:key x:0 y:0.
            ^ self.
        ] ifFalse:[
            cp := selectedCodePointHolder value ? 0. 
            key == #CursorRight ifTrue:[
                ncp := (cp + 1).
            ].
            key == #CursorLeft ifTrue:[
                ncp := (cp - 1).
            ].
            key == #CursorDown ifTrue:[
                ncp := (cp + 16).
            ].
            key == #CursorUp ifTrue:[
                ncp := (cp - 16).
            ].
            ncp notNil ifTrue:[
                (ncp between:0 and:16rFFFF) ifTrue:[
                    codePageHolder value:(ncp bitShift:-8).
                    selectedCodePointHolder value:ncp.
                ].
                ^ self.
            ].
        ].
    ].        

    super keyPress:key x:x y:y

    "Modified: / 12-07-2012 / 12:25:50 / cg"
    "Modified: / 08-01-2019 / 18:26:17 / Claus Gittinger"
!

selectedCodePointChanged
    realized ifTrue:[
        self clear.
        self redraw.
    ].
! !

!CharacterSetView methodsFor:'initialization & release'!

consumingView:aView
    masterViewOrNil := aView.

    "Created: / 08-01-2019 / 11:23:30 / Claus Gittinger"
    "Modified: / 08-01-2019 / 18:20:33 / Claus Gittinger"
!

destroy
    masterViewOrNil notNil ifTrue:[
        masterViewOrNil removeDependent:self.
        masterViewOrNil := nil.
    ].
    super destroy.
!

initialize
    super initialize.
    
    codePageHolder := 0 asValue.
    codePageHolder onChangeSend:#codePageChanged to:self.

    selectedCodePointHolder := ValueHolder new.
    selectedCodePointHolder onChangeSend:#selectedCodePointChanged to:self.

    "Modified (format): / 08-01-2019 / 14:26:23 / Claus Gittinger"
!

useSameFontAs:aView
    masterViewOrNil := aView.
    masterViewOrNil addDependent:self
! !

!CharacterSetView methodsFor:'queries'!

computePreferredExtent
    ^ ((4 + (gc font widthOf:'W') + 4) * 16)
       @
      ((4 + (gc font heightOf:'W') + 4) * 16)

    "Created: / 09-11-2018 / 19:47:35 / Claus Gittinger"
! !

!CharacterSetView class methodsFor:'documentation'!

version
    ^ '$Header$'
!

version_CVS
    ^ '$Header$'
! !