You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdeutils/khexedit/lib/kbuffercolumn.cpp

701 lines
17 KiB

/***************************************************************************
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 <kdebug.h>
// qt specific
#include <tqpainter.h>
// 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;
KBufferColumn::KBufferColumn( KColumnsView *CV, KDataBuffer *B, KBufferLayout *L, KBufferRanges *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 )
{
}
KBufferColumn::~KBufferColumn()
{
delete [] PosX;
delete [] PosRightX;
}
void KBufferColumn::set( KDataBuffer *B )
{
Buffer= B;
}
void KBufferColumn::resetXBuffer()
{
delete [] PosX;
delete [] PosRightX;
LastPos = Layout->noOfBytesPerLine()-1;
PosX = new KPixelX[LastPos+1];
PosRightX = new KPixelX[LastPos+1];
if( PosX )
recalcX();
}
void KBufferColumn::setMetrics( KPixelX DW, KPixelY DBL )
{
DigitBaseLine = DBL;
setDigitWidth( DW );
}
bool KBufferColumn::setDigitWidth( KPixelX DW )
{
// no changes?
if( DigitWidth == DW )
return false;
DigitWidth = DW;
// recalculate depend sizes
recalcByteWidth();
if( PosX )
recalcX();
return true;
}
bool KBufferColumn::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 KBufferColumn::setByteSpacingWidth( KPixelX BSW )
{
// no changes?
if( ByteSpacingWidth == BSW )
return false;
ByteSpacingWidth = BSW;
// recalculate depend sizes
recalcVerticalGridX();
if( PosX )
recalcX();
return true;
}
bool KBufferColumn::setNoOfGroupedBytes( int NoGB )
{
// no changes?
if( NoOfGroupedBytes == NoGB )
return false;
NoOfGroupedBytes = NoGB;
if( PosX )
recalcX();
return true;
}
bool KBufferColumn::setGroupSpacingWidth( KPixelX GSW )
{
// no changes?
if( GroupSpacingWidth == GSW )
return false;
GroupSpacingWidth = GSW;
// recalculate depend sizes
recalcVerticalGridX();
if( PosX )
recalcX();
return true;
}
void KBufferColumn::recalcByteWidth()
{
ByteWidth = DigitWidth;
recalcVerticalGridX();
}
void KBufferColumn::recalcVerticalGridX()
{
VerticalGridX = ByteWidth-1 + GroupSpacingWidth/2;
}
void KBufferColumn::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 KBufferColumn::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 KBufferColumn::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 KBufferColumn::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 KBufferColumn::xOfPos( int Pos ) const { return x() + (PosX?PosX[Pos]:0); }
KPixelX KBufferColumn::rightXOfPos( int Pos ) const { return x() + (PosRightX?PosRightX[Pos]:0); }
int KBufferColumn::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 KBufferColumn::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 KBufferColumn::relXOfPos( int Pos ) const { return PosX ? PosX[Pos] : 0; }
KPixelX KBufferColumn::relRightXOfPos( int Pos ) const { return PosRightX ? PosRightX[Pos] : 0; }
KPixelXs KBufferColumn::wideXPixelsOfPos( KSection Positions ) const
{
return KPixelXs( Positions.start()>0?rightXOfPos(Positions.start()-1)+1:xOfPos(Positions.start()),
Positions.end()<LastPos?xOfPos(Positions.end()+1)-1:rightXOfPos(Positions.end()) );
}
KPixelXs KBufferColumn::relWideXPixelsOfPos( KSection Positions ) const
{
return KPixelXs( Positions.start()>0?relRightXOfPos(Positions.start()-1)+1:relXOfPos(Positions.start()),
Positions.end()<LastPos?relXOfPos(Positions.end()+1)-1:relRightXOfPos(Positions.end()) );
}
void KBufferColumn::preparePainting( KPixelXs Xs )
{
Xs.restrictTo( XSpan );
// translate
Xs.moveBy( -x() );
// store the values
PaintX = Xs.start();
PaintW = Xs.width();
// get line positions to paint
PaintPositions = posOfRelX( PaintX, PaintW );
}
void KBufferColumn::paintFirstLine( TQPainter *P, KPixelXs Xs, int FirstLine )
{
preparePainting( Xs );
PaintLine = FirstLine;
// kdDebug(1501) << "paintFirstLine:"<<cx<<","<<cw<<"|" <<PaintX<<","<<PaintW << ")\n";
// paintPositions( P, PaintLine++, PaintPositions );
paintLine( P, PaintLine++ );
}
void KBufferColumn::paintNextLine( TQPainter *P )
{
// paintPositions( P, PaintLine++, PaintPositions );
paintLine( P, PaintLine++ );
}
void KBufferColumn::paintLine( TQPainter *P, int Line ) // TODO: could be removed???
{
// kdDebug(1501) << "paintLine line: "<<Line<<" Start: "<<PaintPositions.start()<<" End: "<<PaintPositions.end() << "\n";
// no bytes to paint?
// if( !Layout->hasContent(Line) )
// return;
paintPositions( P, Line, PaintPositions );
}
void KBufferColumn::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(KBufferCoord( Pos.start(), Line )),
Layout->lastPos( KBufferCoord( Pos.end(), Line )) );
// no bytes to paint?
if( !Layout->hasContent(Line) )
return;
// check for leading and trailing spaces
KSection Indizes( Layout->indexAtCoord(KBufferCoord( 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 KBufferColumn::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 KBufferColumn::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 KBufferColumn::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 KBufferColumn::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 KBufferColumn::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 KBufferColumn::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 KBufferColumn::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 KBufferColumn::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 KBufferColumn::drawByte( TQPainter *P, char /*Byte*/, KHEChar B, const TQColor &Color ) const
{
P->setPen( Color );
P->drawText( 0, DigitBaseLine, TQString(B) );
}
bool KBufferColumn::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 KBufferColumn::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;
}