/*************************************************************************** kbuffercolumn.cpp - description ------------------- begin : Mit Mai 14 2003 copyright : (C) 2003 by Friedrich W. H. Kossebau email : Friedrich.W.H@Kossebau.de ***************************************************************************/ /*************************************************************************** * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License version 2 as published by the Free Software Foundation. * * * ***************************************************************************/ //#include // qt specific #include // lib specific #include "kcolumnsview.h" #include "kbuffercursor.h" #include "kbuffercolumn.h" #include "kbufferlayout.h" #include "kbufferranges.h" #include "helper.h" #include "kcharcodec.h" using namespace KHE; static const unsigned int StartsBefore = 1; static const unsigned int EndsLater = 2; static const char EmptyByte = ' '; static const KPixelX DefaultCursorWidth = 2; static const KPixelX DefaultByteSpacingWidth = 3; static const KPixelX DefaultGroupSpacingWidth = 9; static const int DefaultNoOfGroupedBytes = 4; TDEBufferColumn::TDEBufferColumn( KColumnsView *CV, KDataBuffer *B, TDEBufferLayout *L, TDEBufferRanges *R ) : KColumn( CV ), Buffer( B ), Layout( L ), Ranges( R ), DigitWidth( 0 ), DigitBaseLine( 0 ), VerticalGrid( false ), ByteWidth( 0 ), ByteSpacingWidth( DefaultByteSpacingWidth ), GroupSpacingWidth( DefaultGroupSpacingWidth ), NoOfGroupedBytes( DefaultNoOfGroupedBytes ), PosX( 0L ), PosRightX( 0L ), LastPos( 0 ) { } TDEBufferColumn::~TDEBufferColumn() { delete [] PosX; delete [] PosRightX; } void TDEBufferColumn::set( KDataBuffer *B ) { Buffer= B; } void TDEBufferColumn::resetXBuffer() { delete [] PosX; delete [] PosRightX; LastPos = Layout->noOfBytesPerLine()-1; PosX = new KPixelX[LastPos+1]; PosRightX = new KPixelX[LastPos+1]; if( PosX ) recalcX(); } void TDEBufferColumn::setMetrics( KPixelX DW, KPixelY DBL ) { DigitBaseLine = DBL; setDigitWidth( DW ); } bool TDEBufferColumn::setDigitWidth( KPixelX DW ) { // no changes? if( DigitWidth == DW ) return false; DigitWidth = DW; // recalculate depend sizes recalcByteWidth(); if( PosX ) recalcX(); return true; } bool TDEBufferColumn::setSpacing( KPixelX BSW, int NoGB, KPixelX GSW ) { // no changes? if( ByteSpacingWidth == BSW && NoOfGroupedBytes == NoGB && GroupSpacingWidth == GSW ) return false; ByteSpacingWidth = BSW; NoOfGroupedBytes = NoGB; GroupSpacingWidth = GSW; // recalculate depend sizes recalcVerticalGridX(); if( PosX ) recalcX(); return true; } bool TDEBufferColumn::setByteSpacingWidth( KPixelX BSW ) { // no changes? if( ByteSpacingWidth == BSW ) return false; ByteSpacingWidth = BSW; // recalculate depend sizes recalcVerticalGridX(); if( PosX ) recalcX(); return true; } bool TDEBufferColumn::setNoOfGroupedBytes( int NoGB ) { // no changes? if( NoOfGroupedBytes == NoGB ) return false; NoOfGroupedBytes = NoGB; if( PosX ) recalcX(); return true; } bool TDEBufferColumn::setGroupSpacingWidth( KPixelX GSW ) { // no changes? if( GroupSpacingWidth == GSW ) return false; GroupSpacingWidth = GSW; // recalculate depend sizes recalcVerticalGridX(); if( PosX ) recalcX(); return true; } void TDEBufferColumn::recalcByteWidth() { ByteWidth = DigitWidth; recalcVerticalGridX(); } void TDEBufferColumn::recalcVerticalGridX() { VerticalGridX = ByteWidth-1 + GroupSpacingWidth/2; } void TDEBufferColumn::recalcX() { SpacingTrigger = noOfGroupedBytes() > 0 ? noOfGroupedBytes()-1 : LastPos+1; // last ensures to never trigger the spacing KPixelX NewWidth = 0; int p = 0; int gs = 0; KPixelX *PX = PosX; KPixelX *PRX = PosRightX; for( ; PX<&PosX[LastPos+1]; ++PX, ++PRX, ++p, ++gs ) { *PX = NewWidth; NewWidth += ByteWidth; *PRX = NewWidth-1; // is there a space behind the actual byte (if it is not the last)? if( gs == SpacingTrigger ) { NewWidth += GroupSpacingWidth; gs = -1; } else NewWidth += ByteSpacingWidth; } setWidth( PosRightX[LastPos]+1 ); } // TODO: why are inlined functions not available as symbols when defined before their use //TODO: works not precisly for the byte rects but includes spacing and left and right /*inline*/ int TDEBufferColumn::posOfX( KPixelX PX ) const { if( !PosX ) return NoByteFound; // translate PX -= x(); // search backwards for the first byte that is equalleft to x for( int p=LastPos; p>=0; --p ) if( PosX[p] <= PX ) return p; return 0; //NoByteFound; } int TDEBufferColumn::magPosOfX( KPixelX PX ) const { if( !PosX ) return NoByteFound; // translate PX -= x(); // search backwards for the first byte that is equalleft to x for( int p=LastPos; p>=0; --p ) if( PosX[p] <= PX ) { // are we close to the right? if( PosRightX[p]-PX < DigitWidth/2 ) // TODO: perhaps cache also the middle xpos's ++p; return p; } return 0; //NoByteFound; } KSection TDEBufferColumn::posOfX( KPixelX PX, KPixelX PW ) const { if( !PosX ) return KSection(); // translate PX -= x(); int PRX = PX + PW - 1; KSection P; // search backwards for the first byte that is equalleft to x for( int p=LastPos; p>=0; --p ) if( PosX[p] <= PRX ) { P.setEnd( p ); for( ; p>=0; --p ) if( PosX[p] <= PX ) { P.setStart( p ); break; } break; } return P; } KPixelX TDEBufferColumn::xOfPos( int Pos ) const { return x() + (PosX?PosX[Pos]:0); } KPixelX TDEBufferColumn::rightXOfPos( int Pos ) const { return x() + (PosRightX?PosRightX[Pos]:0); } int TDEBufferColumn::posOfRelX( KPixelX PX ) const { if( !PosX ) return NoByteFound; // search backwards for the first byte that is equalleft to x for( int p=LastPos; p>=0; --p ) if( PosX[p] <= PX ) return p; return 0; //NoByteFound; } KSection TDEBufferColumn::posOfRelX( KPixelX PX, KPixelX PW ) const { if( !PosX ) return KSection(); int PRX = PX + PW - 1; KSection P; // search backwards for the first byte that is equalleft to x for( int p=LastPos; p>=0; --p ) if( PosX[p] <= PRX ) { P.setEnd( p ); for( ; p>=0; --p ) if( PosX[p] <= PX ) { P.setStart( p ); break; } break; } return P; } KPixelX TDEBufferColumn::relXOfPos( int Pos ) const { return PosX ? PosX[Pos] : 0; } KPixelX TDEBufferColumn::relRightXOfPos( int Pos ) const { return PosRightX ? PosRightX[Pos] : 0; } KPixelXs TDEBufferColumn::wideXPixelsOfPos( KSection Positions ) const { return KPixelXs( Positions.start()>0?rightXOfPos(Positions.start()-1)+1:xOfPos(Positions.start()), Positions.end()0?relRightXOfPos(Positions.start()-1)+1:relXOfPos(Positions.start()), Positions.end()hasContent(Line) ) // return; paintPositions( P, Line, PaintPositions ); } void TDEBufferColumn::paintPositions( TQPainter *P, int Line, KSection Pos ) { const TQColorGroup &CG = View->colorGroup(); // clear background unsigned int BlankFlag = (Pos.start()!=0?StartsBefore:0) | (Pos.end()!=LastPos?EndsLater:0); paintRange( P, CG.base(), Pos, BlankFlag ); // Go through the lines TODO: handle first and last line more effeciently // check for leading and trailing spaces KSection Positions( Layout->firstPos(TDEBufferCoord( Pos.start(), Line )), Layout->lastPos( TDEBufferCoord( Pos.end(), Line )) ); // no bytes to paint? if( !Layout->hasContent(Line) ) return; // check for leading and trailing spaces KSection Indizes( Layout->indexAtCoord(TDEBufferCoord( Positions.start(), Line )), Positions.width(), false ); unsigned int SelectionFlag; unsigned int MarkingFlag; KSection Selection; KSection Marking; bool HasMarking = Ranges->hasMarking(); bool HasSelection = Ranges->hasSelection(); while( Positions.isValid() ) { KSection PositionsPart( Positions ); // set of positions to paint next KSection IndizesPart( Indizes ); // set of indizes to paint next // falls Marking nicht mehr gebuffert und noch zu erwarten if( HasMarking && Marking.endsBefore(IndizesPart.start()) ) { // erhebe nächste Markierung im Bereich HasMarking = isMarked( IndizesPart, &Marking, &MarkingFlag ); } // falls Selection nicht mehr gebuffert und noch zu erwarten if( HasSelection && Selection.endsBefore(IndizesPart.start()) ) { // erhebe nächste Selection im Bereich HasSelection = isSelected( IndizesPart, &Selection, &SelectionFlag ); } if( Marking.start() == IndizesPart.start() ) { IndizesPart.setEnd( Marking.end() ); PositionsPart.setEndByWidth( Marking.width() ); if( PositionsPart.end() == Layout->lastPos(Line) ) MarkingFlag &= ~EndsLater; if( PositionsPart.start() == Layout->firstPos(Line)) MarkingFlag &= ~StartsBefore; paintMarking( P, PositionsPart, IndizesPart.start(), MarkingFlag ); } else if( Selection.includes(IndizesPart.start()) ) { if( Selection.startsBehind(IndizesPart.start()) ) SelectionFlag |= StartsBefore; bool MarkingBeforeEnd = HasMarking && Marking.start() <= Selection.end(); IndizesPart.setEnd( MarkingBeforeEnd ? Marking.start()-1 : Selection.end() ); PositionsPart.setEndByWidth( IndizesPart.width() ); if( MarkingBeforeEnd ) SelectionFlag |= EndsLater; if( PositionsPart.end() == Layout->lastPos(Line) ) SelectionFlag &= ~EndsLater; if( PositionsPart.start() == Layout->firstPos(Line) ) SelectionFlag &= ~StartsBefore; paintSelection( P, PositionsPart, IndizesPart.start(), SelectionFlag ); } else { // calc end of plain text if( HasMarking ) IndizesPart.setEnd( Marking.start()-1 ); if( HasSelection ) IndizesPart.restrictEndTo( Selection.start()-1 ); PositionsPart.setEndByWidth( IndizesPart.width() ); paintPlain( P, PositionsPart, IndizesPart.start() ); } Indizes.setStartBehind( IndizesPart ); Positions.setStartBehind( PositionsPart ); } // we don't paint grids as default, perhaps we never will though it works // paintGrid( P, Positions ); // TODO: get some unmodified Positions (see three lines before) } void TDEBufferColumn::paintPlain( TQPainter *P, KSection Positions, int Index ) { // paint all the bytes affected for( int p=Positions.start(); p<=Positions.end(); ++p,++Index ) { KPixelX x = relXOfPos( p ); // draw the byte P->translate( x, 0 ); char Byte = Buffer->datum( Index ); KHEChar B = Codec->decode( Byte ); drawByte( P, Byte, B, colorForChar(B) ); P->translate( -x, 0 ); } } void TDEBufferColumn::paintSelection( TQPainter *P, KSection Positions, int Index, int Flag ) { const TQColorGroup &CG = View->colorGroup(); paintRange( P, CG.highlight(), Positions, Flag ); const TQColor &HTC = CG.highlightedText(); // paint all the bytes affected for( int p=Positions.start(); p<=Positions.end(); ++p,++Index ) { KPixelX x = relXOfPos( p ); // draw the byte P->translate( x, 0 ); char Byte = Buffer->datum( Index ); KHEChar B = Codec->decode( Byte ); drawByte( P, Byte, B, HTC ); P->translate( -x, 0 ); } } void TDEBufferColumn::paintMarking( TQPainter *P, KSection Positions, int Index, int Flag ) { const TQColorGroup &CG = View->colorGroup(); paintRange( P, CG.text(), Positions, Flag ); const TQColor &BC = CG.base(); // paint all the bytes affected for( int p=Positions.start(); p<=Positions.end(); ++p,++Index ) { KPixelX x = relXOfPos( p ); // draw the byte P->translate( x, 0 ); char Byte = Buffer->datum( Index ); KHEChar B = Codec->decode( Byte ); drawByte( P, Byte, B, BC ); P->translate( -x, 0 ); } } // TODO: smarter calculation void TDEBufferColumn::paintGrid( TQPainter *P, KSection Range ) { int st = 0; // counter for spacing triggering P->setPen( TQt::black ); // paint all the bytes affected for( int p=Range.start(); p<=Range.end(); ++p,++st ) { KPixelX x = relXOfPos( p ); // draw the byte P->translate( x, 0 ); // spacing behind byte and vertical grid enabled? if( st == SpacingTrigger && p != LastPos ) P->drawLine(VerticalGridX, 0,VerticalGridX, LineHeight-1 ) ; P->translate( -x, 0 ); } } void TDEBufferColumn::paintRange( TQPainter *P, const TQColor &Color, KSection Positions, int Flag ) { KPixelX RangeX = Flag & StartsBefore ? relRightXOfPos( Positions.start()-1 ) + 1 : relXOfPos( Positions.start() ); KPixelX RangeW = (Flag & EndsLater ? relXOfPos( Positions.end()+1 ): relRightXOfPos( Positions.end() ) + 1) - RangeX; P->fillRect( RangeX,0,RangeW,LineHeight, TQBrush(Color,TQt::SolidPattern) ); } void TDEBufferColumn::paintByte( TQPainter *P, int Index ) { char Byte = ( Index > -1 ) ? Buffer->datum( Index ) : EmptyByte; KHEChar B = Codec->decode( Byte ); const TQColorGroup &CG = View->colorGroup(); TQColor Color = CG.text(); TQBrush Brush( CG.base(), TQt::SolidPattern ); if( Index > -1 ) { if( Ranges->markingIncludes(Index) ) { Brush.setColor( CG.text() ); Color = CG.base(); } else if( Ranges->selectionIncludes(Index) ) { Brush.setColor( CG.highlight() ); Color = CG.highlightedText(); } else { Brush.setColor( CG.base() ); Color = colorForChar( B ); } } P->fillRect( 0,0,ByteWidth,LineHeight, Brush ); if( Index > -1 ) drawByte( P, Byte, B, Color ); } void TDEBufferColumn::paintFramedByte( TQPainter *P, int Index, KFrameStyle FrameStyle ) { paintByte( P, Index ); char Byte = ( Index > -1 ) ? Buffer->datum( Index ) : EmptyByte; KHEChar B = Codec->decode( Byte ); P->setPen( colorForChar(B) ); if( FrameStyle == Frame ) P->drawRect( 0, 0, ByteWidth, LineHeight ); else if( FrameStyle == Left ) P->drawLine( 0, 0, 0, LineHeight-1 ); else P->drawLine( ByteWidth-1,0,ByteWidth-1,LineHeight-1 ); } void TDEBufferColumn::paintCursor( TQPainter *P, int Index ) { char Byte = ( Index > -1 ) ? Buffer->datum( Index ) : EmptyByte; KHEChar B = Codec->decode( Byte ); P->fillRect( 0, 0, ByteWidth, LineHeight, TQBrush(colorForChar(B),TQt::SolidPattern) ); } void TDEBufferColumn::drawByte( TQPainter *P, char /*Byte*/, KHEChar B, const TQColor &Color ) const { P->setPen( Color ); P->drawText( 0, DigitBaseLine, TQString(B) ); } bool TDEBufferColumn::isSelected( KSection Range, KSection *Selection, unsigned int *Flag ) const { KSection S; unsigned int F = 0; const KSection *OS = Ranges->firstOverlappingSelection( Range ); if( !OS ) return false; S = *OS; // does selection start before asked range? if( Range.start() > S.start() ) { S.setStart( Range.start() ); F |= StartsBefore; } // does selection go on behind asked range? if( Range.end() < S.end() ) { S.setEnd( Range.end() ); F |= EndsLater; } *Selection = S; *Flag = F; return true; } bool TDEBufferColumn::isMarked( KSection Range, KSection *Marking, unsigned int *Flag ) const { KSection M; unsigned int F = 0; const KSection *OM = Ranges->overlappingMarking( Range ); if( !OM ) return false; M = *OM; // does selection start before asked range? if( Range.start() > M.start() ) { M.setStart( Range.start() ); F |= StartsBefore; } // does selection go on behind asked range? if( Range.end() < M.end() ) { M.setEnd( Range.end() ); F |= EndsLater; } *Marking = M; *Flag = F; return true; }