Page 1 of 2

Coloured multine browse with icons, sample from the DevCon

Posted: Thu May 07, 2015 1:25 am
by Tom
The attached sample presented on the 2015 eXpress++ DevCon can be compiled and linked with 1.9 SL1 or 2.0 (use "od.bat" for this if you like). It shows a DCBROWSE that uses ownerdrawing in a subclass, where a text containing (multiple) CR/LF is translated into multiple coloured lines reflecting the color codeblocks and icons added to the cell text are shown below the text. This is not a very generic sample, but it can be used as a base for almost anything you want to paint in a browse cell. Switch the text colors for the links, add boxes or whatever.

If the browse comes up, it will not use ownerdrawing. Click on "Use ownerdrawing", which toggles an iVar of the subclass. The browse is repainted using "InvalidateRect" (a refresh is not needed, since the cell contens remains the same!). Please don't deliver the icons I added. Those are from a library I bought. Thanks.

The screenshot shows what you should see if "Use ownerdrawing" was clicked.

Re: Coloured multine browse with icons, sample from the DevC

Posted: Thu May 07, 2015 3:46 am
by obelix
Many thanks, Tom

Re: Coloured multine browse with icons, sample from the DevC

Posted: Thu May 07, 2015 4:45 am
by RDalzell
Tom,

Looks great as always and as expected by you.

Now if we could get a glimpse of Bobby's colored tab pages within the printed report the moons would align.

Rick

Re: Coloured multine browse with icons, sample from the DevC

Posted: Thu May 07, 2015 8:15 am
by rdonnay
This is a very elegant method of using owner-drawing and it inspired me to create a "generic" owner-drawing method for DC_XbpBrowse() that will use either an array or an object that is put into each cell in DCBROWSECOL. In addition, I would like to "style" it using CSS style rules.

Thanks again, Tom, for donating this sample.

Re: Coloured multine browse with icons, sample from the DevC

Posted: Fri May 08, 2015 5:49 am
by SvenVazan
Fantastic - thanks Tom.

Re: Coloured multine browse with icons, sample from the DevC

Posted: Mon May 11, 2015 5:46 pm
by sdenjupol148
Hey Rick,

I will work on a sample of how it's done.

Bobby :techie-hiding: Drakos

Re: Coloured multine browse with icons, sample from the DevC

Posted: Mon May 11, 2015 6:59 pm
by RDalzell
Thanks Bobby.

Re: Coloured multine browse with icons, sample from the DevC

Posted: Wed May 13, 2015 7:56 pm
by rdonnay
Tom -

I have been experimenting with ways to make this more generic.

You can now give it an array of data as an option. This eliminates the need to concatenate everything with CRLF and make a tokenized string.

Also, the fonts can be defined in the DCBROWSE command.

Notice that you only need to use the OWNERDRAW clause on DCBROWSECOL commands.
My next experiment will be to use objects instead of arrays in the DCBROWSECOL DATA clauses.

Code: Select all

#INCLUDE "dcdialog.CH"

#define FONTBOLD      "Bold"    // change to "Fett" for German versions
#define FONTITALIC    "Italic"  // change to "Kursiv" for German versions
#define CRLF          Chr(13)+Chr(10)
#define GENDER_MALE   'agent.ico'
#define GENDER_FEMALE 'she_user.ico'
#define GOOD_VALUE    'online.ico'
#define BAD_VALUE     'offline.ico'

#pragma library("dclipx.lib")
#pragma library("dclip1.lib") // for DC_LoadRdds()

FUNCTION Main()

LOCAL GetList := {}, GetOptions := {}, oBrowse, ;
   aPres := ;
         { { XBP_PP_COL_DA_ROWHEIGHT, 48 }, ;  // rows are 48 pixels high
           { XBP_PP_COL_DA_CELLHEIGHT, 48 } ,;
           { XBP_PP_COL_DA_HILITE_BGCLR, GRA_CLR_PALEGRAY },;   // Hilite BG color
           { XBP_PP_COL_DA_CELLFRAMELAYOUT, XBPFRAME_BOX } ,;
           { XBP_PP_COL_DA_FRAMELAYOUT,     XBPFRAME_NONE} ,;
           { XBP_PP_COL_DA_ROWSEPARATOR, XBPCOL_SEP_LINE },  /* Row Sep  */     ;
           { XBP_PP_COL_DA_COLSEPARATOR, XBPCOL_SEP_LINE } },  /* Col Sep  */     ;
   lOwnerDraw := .F.

DCGETOPTIONS AUTORESIZE

DC_LoadRdds()

DC_BrowseColor( { nil, GRA_CLR_WHITE, nil, GraMakeRGBColor( {214,243,244} ) } )

USE customer VIA 'FOXCDX'

@ 0,0 DCBROWSE oBrowse ALIAS 'CUSTOMER' SIZE 96,25 PRESENTATION aPres FIT ;
      SUBCLASS 'XbpOwnerDrawBrowse()' USEVISUALSTYLE NOHSCROLL ;
      PREEVAL {|o|o:stdFontCompoundName := '10.Tahoma', ;
                  o:boldFontCompoundName := '10.Tahoma '+FONTBOLD, ;
                  o:boldItalicFontCompoundName := '12.Tahoma '+FONTBOLD+' '+FONTITALIC}

DCBROWSECOL DATA {||{CUSTOMER->cust_nmbr}} ;
                    HEADER 'ID' WIDTH 5 PARENT oBrowse ;
                    OWNERDRAW

DCBROWSECOL DATA {||{CUSTOMER->bill_name, ;
                     CUSTOMER->bill_strt, ;
                     Trim(CUSTOMER->bill_city)+' '+CUSTOMER->bill_zip}} ;
                    HEADER 'Name/Address'  WIDTH 25 PARENT oBrowse ;
                    COLOR {||{IF('Software'$CUSTOMER->bill_name,GraMakeRGBColor( { 253, 120, 41 } ),GRA_CLR_DARKGREEN),oBrowse:rowColor()[2]}} ;
                    OWNERDRAW

DCBROWSECOL DATA {||{CUSTOMER->contact, ;
                     CUSTOMER->email, ;
                     '[' + IF(CUSTOMER->cont_gend=='m',GENDER_MALE,GENDER_FEMALE) + ']'}} ;
                    HEADER 'Contact'  WIDTH 15 PARENT oBrowse ;
                    COLOR {||{GRA_CLR_DARKRED,oBrowse:rowColor()[2]}} ;
                    OWNERDRAW

DCBROWSECOL DATA {||{CustInfo(CUSTOMER->cust_val), ;
                     IF(CUSTOMER->cust_val==0,'['+BAD_VALUE+']',Replicate('['+GOOD_VALUE+']',CUSTOMER->cust_val))}} ;
                    HEADER 'Info/Rating'     WIDTH 10 PARENT oBrowse ;
                    COLOR {||{GRA_CLR_DARKBLUE,GRA_CLR_YELLOW}} ;
                    OWNERDRAW

DCBROWSECOL FIELD CUSTOMER->phone  HEADER 'Phone' WIDTH  10 PARENT oBrowse

@ 26,0 DCCHECKBOX lOwnerDraw PROMPT 'Use ownerdrawing' ;
       ACTION {||oBrowse:lOwnerDraw := lOwnerDraw,oBrowse:InvalidateRect()}

DCREAD GUI FIT TITLE 'Ownerdrawing sample' ADDBUTTONS OPTIONS GetOptions

CLOSE ALL
RETURN nil

STATIC FUNCTION CustInfo(nValue)
DO CASE
  CASE nValue == 0
  RETURN 'bad'
  CASE nValue == 1
  RETURN 'ok'
  CASE nValue == 2
  RETURN 'good'
  CASE nValue == 3
  RETURN 'very good'
  CASE nValue == 4
  RETURN 'excellent'
  CASE nValue == 5
  RETURN 'best ever'
ENDCASE
RETURN 'not known'

PROC appsys ; RETURN

* ============

CLASS XbpOwnerDrawBrowse FROM DC_XbpBrowse

PROTECTED:
   VAR oIcon, aLineAttrs, aBoxAttrs, oStdFont, oBoldFont, oBoldItalicFont

EXPORTED:
   VAR lOwnerDraw
   VAR stdFontCompoundName, boldFontCompoundName, boldItalicFontCompoundName

   METHOD CustomDrawCell

   INLINE METHOD destroy
      ::DC_XbpBrowse:destroy()
   RETURN self

   INLINE METHOD init( oParent, oOwner, aPos, aSize, aPP, lVisible, oGetList )

      ::DC_XbpBrowse:init( oParent, oOwner, aPos, aSize, aPP, lVisible, oGetList )
      ::drawMode:= XBP_DRAW_OWNER
      ::oIcon := XbpIcon():New():Create()
      ::lOwnerDraw := .F.
      ::aLineAttrs := ARRAY( GRA_AL_COUNT )
      ::aBoxAttrs  := ARRAY( GRA_AA_COUNT )
      ::aLineAttrs [ GRA_AL_COLOR ] := GRA_CLR_BLACK
      ::aLineAttrs [ GRA_AL_TYPE ]  := GRA_LINETYPE_SOLID
      ::stdFontCompoundName := '10.Tahoma'
      ::boldFontCompoundName := '10.Tahoma '+FONTBOLD
      ::boldItalicFontCompoundName := '10.Tahoma '+FONTBOLD+' '+FONTITALIC

   RETURN self

   INLINE METHOD create( oParent, oOwner, aPos, aSize, aPP, lVisible )
   *******************************************************************
      ::oStdFont := XbpFont():New():Create(::stdFontCompoundName)
      ::oBoldFont := XbpFont():New():Create(::boldFontCompoundName)
      ::oBoldItalicFont := XbpFont():New():Create(::boldItalicFontCompoundName)
      ::DC_XbpBrowse:create( oParent, oOwner, aPos, aSize, aPP, lVisible )

   RETURN self

ENDCLASS

METHOD XbpOwnerDrawBrowse:customDrawCell( oPS, aInfo )
   ******************************************
     LOCAL xData, aPP, nFound, nAlign, nLine, nFgCol := GRA_CLR_BLACK, nBgCol := GRA_CLR_WHITE,;
           aText, aIcons, nIcons, nHeight

    IF !::lOwnerDraw // ownerdrawing is switched off, let the system draw
      RETURN .T.
    ENDIF

    * get column data
    xData:= aInfo[ XBP_DRAWINFO_AREA ]:getCell( aInfo[ XBP_DRAWINFO_ITEM ] )
    IF xData == NIL // empty row
      RETURN .F.
    ENDIF

    * get cell alignment
    aPP := aInfo[ XBP_DRAWINFO_COLUMN ]:presArray
    nFound := AScan(aPP,{|a|Valtype(a[1])=='N'.AND. ;
                     (a[1]==XBPCOL_DA_CELLALIGNMENT .OR. ;
                      a[1]==XBP_PP_COL_DA_CELLALIGNMENT) } )

    IF nFound > 0
      nAlign := aPP[nFound,2]
    ELSE
      nAlign := XBPALIGN_VCENTER
    ENDIF

    * get cell height
    nHeight := Int((aInfo[ XBP_DRAWINFO_RECT, 4 ]-aInfo[ XBP_DRAWINFO_RECT, 2 ])/3)

    * get cell colors
    aInfo[ XBP_DRAWINFO_AREA ]:getCellColor( aInfo[ XBP_DRAWINFO_ITEM ],@nFgCol, @nBgCol )

    IF nFgCol # nil
      oPS:setColor( nFgCol, nBgCol )
    ENDIF

    IF Valtype(xData) # 'A'
      xData := Var2Char(xData)
    ENDIF

    IF Valtype(xData) == 'C' .AND. !CRLF $ xData
      * no line break, just paint the text
      GraCaptionStr( oPS, aInfo[ XBP_DRAWINFO_RECT ], { aInfo[ XBP_DRAWINFO_RECT, 3 ], aInfo[ XBP_DRAWINFO_RECT, 4 ] }, xData, nAlign )
    ELSE
      IF Valtype(xData) == 'C'
        aText := DC_TokenArray(xData,CRLF)
      ELSE
        aText := xData
      ENDIF
      FOR nLine := 1 TO Len(aText)
        IF nLine = 1
          IF BAnd(aInfo[XBP_DRAWINFO_STATE],XBP_DRAWSTATE_SELECTED) = 0 // cell is NOT selected
            oPS:SetFont(::oBoldFont)
          ELSE
            oPS:SetFont(::oBoldItalicFont)
          ENDIF
        ELSE
          oPS:SetFont(::oStdFont)
        ENDIF
        IF Left(aText[nLine],1) == '[' .AND. Right(aText[nLine],1) == ']' // paint at least one icon!
          aIcons := DC_TokenArray(aText[nLine],']') // array of icon filenames will contain at least one
          FOR nIcons := 1 TO Len(aIcons)
            aIcons[nIcons] := StrTran(StrTran(aIcons[nIcons],'[',''),']','') // remove brackets
            ::oIcon:LoadFile(aIcons[nIcons],nHeight,nHeight) // load icon in line height
            ::oIcon:Draw(oPs,{ aInfo[ XBP_DRAWINFO_RECT, 1 ]+(nHeight*(nIcons-1)), aInfo[ XBP_DRAWINFO_RECT, 2 ], aInfo[ XBP_DRAWINFO_RECT, 1 ]+(nHeight*nIcons), aInfo[ XBP_DRAWINFO_RECT, 2 ]+nHeight })
          NEXT
          ELSE
          GraCaptionStr( oPS, { aInfo[ XBP_DRAWINFO_RECT,1 ], aInfo[ XBP_DRAWINFO_RECT,4 ]-(nHeight*(nLine-1))},;
                              { aInfo[ XBP_DRAWINFO_RECT, 3 ], aInfo[ XBP_DRAWINFO_RECT, 4 ]-(nHeight*nLine) }, aText[nLine], nAlign )
        ENDIF
      NEXT
    ENDIF

RETURN .F.

Re: Coloured multine browse with icons, sample from the DevC

Posted: Thu May 14, 2015 6:55 pm
by rdonnay
I added a new feature to DCBROWSE and DCBROWSECOL that now lets you use a single record object to access the data in a browse. The navigation code blocks of the browse force a DC_DbScatter() of the record into the record object before each row is painted.

Copy the attached _DCXBROW.PRG to \expd20\source\dclipx and DCDIALOG.CH to \expd20\include.
Run BUILD19_SL1.BAT or BUILD20.BAT to rebuild DCLIPX.dll.

Here is the usage:

Code: Select all

oRecord := CUSTOMER->(DC_DbRecord():new())

@ 0,0 DCBROWSE oBrowse ALIAS 'CUSTOMER' SIZE 96,25 PRESENTATION aPres FIT ;
      SUBCLASS 'XbpOwnerDrawBrowse()' USEVISUALSTYLE NOHSCROLL ;
      RECORDOBJECT oRecord ;
      PREEVAL {|o|o:stdFontCompoundName := '10.Tahoma', ;
                  o:boldFontCompoundName := '10.Tahoma '+FONTBOLD, ;
                  o:boldItalicFontCompoundName := '12.Tahoma '+FONTBOLD+' '+FONTITALIC}

DCBROWSECOL DATA {|o|{o:cust_nmbr}} ;
                    HEADER 'ID' WIDTH 5 PARENT oBrowse ;
                    OWNERDRAW

DCBROWSECOL DATA {|o|{o:bill_name, ;
                      o:bill_strt, ;
                      Trim(o:bill_city)+' '+o:bill_zip}} ;
                    HEADER 'Name/Address'  WIDTH 25 PARENT oBrowse ;
                    COLOR {||{IF('Software'$CUSTOMER->bill_name,GraMakeRGBColor( { 253, 120, 41 } ),GRA_CLR_DARKGREEN),oBrowse:rowColor()[2]}} ;
                    OWNERDRAW

DCBROWSECOL DATA {|o|{o:contact, ;
                      o:email, ;
                     '[' + IF(o:cont_gend=='m',GENDER_MALE,GENDER_FEMALE) + ']'}} ;
                    HEADER 'Contact'  WIDTH 15 PARENT oBrowse ;
                    COLOR {||{GRA_CLR_DARKRED,oBrowse:rowColor()[2]}} ;
                    OWNERDRAW

DCBROWSECOL DATA {|o|{CustInfo(o:cust_val), ;
                     IF(o:cust_val==0,'['+BAD_VALUE+']',Replicate('['+GOOD_VALUE+']',o:cust_val))}} ;
                    HEADER 'Info/Rating'     WIDTH 10 PARENT oBrowse ;
                    COLOR {||{GRA_CLR_DARKBLUE,GRA_CLR_YELLOW}} ;
                    OWNERDRAW

DCBROWSECOL DATA {|o|o:phone}  HEADER 'Phone' WIDTH  10 PARENT oBrowse

Re: Coloured multine browse with icons, sample from the DevC

Posted: Thu May 14, 2015 7:36 pm
by rdonnay
Here is one more fix:
The ::DC_XbpBrowse:init() must be called last to insure that the PREEVAL overrides the font settings.

Code: Select all

   INLINE METHOD init( oParent, oOwner, aPos, aSize, aPP, lVisible, oGetList )

      ::drawMode:= XBP_DRAW_OWNER
      ::oIcon := XbpIcon():New():Create()
      ::lOwnerDraw := .F.
      ::aLineAttrs := ARRAY( GRA_AL_COUNT )
      ::aBoxAttrs  := ARRAY( GRA_AA_COUNT )
      ::aLineAttrs [ GRA_AL_COLOR ] := GRA_CLR_BLACK
      ::aLineAttrs [ GRA_AL_TYPE ]  := GRA_LINETYPE_SOLID
      ::stdFontCompoundName := '10.Tahoma'
      ::boldFontCompoundName := '10.Tahoma '+FONTBOLD
      ::boldItalicFontCompoundName := '10.Tahoma '+FONTBOLD+' '+FONTITALIC
      ::DC_XbpBrowse:init( oParent, oOwner, aPos, aSize, aPP, lVisible, oGetList )

   RETURN self