/* * khexedit - Versatile hex editor * Copyright (C) 1999 Espen Sand, espensa@online.no * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include #include #include #include #include #include #include #include #include "hexdrag.h" #include "hexerror.h" #include "hexviewwidget.h" // // The usage of the WNorthWestGravity flag should make screen update // faster (less paintEvents). I have had to add update() some places // to ensure proper redrawing. Undefine if something goes wrong // (i.e. not updated ) // #define USE_NORTHWEST_GRAVITY 1 // // I don't want to decide what is best: Drag starts when mouse is moved or // after a timeout. // #define USE_DRAG_MOVEMENT 1 CDragManager::CDragManager( void ) { mActivateMode = Movement; mPending = false; mTimerId = 0; } void CDragManager::setActivateMode( EDragActivateMode mode ) { clear(); mActivateMode = mode; // Movement or Timer } void CDragManager::setup( int x, int y ) { if( mActivateMode == Movement ) { mOrigin.setX(x); mOrigin.setY(y); } else { setupTimer(); } mPending = true; } bool CDragManager::start( TQMouseEvent *e ) { if( mPending == false ) { return( false ); } if( mActivateMode == Movement ) { if( (mOrigin - e->pos()).manhattanLength() > KGlobalSettings::dndEventDelay() ) { mPending = false; emit startDrag( e->state() & ShiftButton ? true : false ); } return( true ); } else // Timer { if( mTimerId != 0 ) { removeTimer(); mPending = false; emit startDrag( e->state() & ShiftButton ? true : false ); return( true ); } else { // Should never happen! mPending = false; return( false ); } } } bool CDragManager::clear( void ) { if( mPending == false ) { return( false ); } if( mActivateMode == Timer ) { removeTimer(); } mPending = false; return( true ); } void CDragManager::timerEvent( TQTimerEvent *e ) { if( e->timerId() == mTimerId ) { removeTimer(); if( mPending == true ) { mPending = false; emit startDrag( ( kapp->keyboardModifiers() & KApplication::ShiftModifier ) ); } } } void CDragManager::removeTimer( void ) { if( mTimerId != 0 ) { killTimer( mTimerId ); mTimerId = 0; } } void CDragManager::setupTimer( void ) { if( mTimerId != 0 ) { killTimer( mTimerId ); mTimerId = 0; } mTimerId = startTimer( 500 ); } // // This widget will use the entire space of the parent widget // CHexViewWidget::CHexViewWidget( TQWidget *parent, const char *name, CHexBuffer *hexBuffer ) : TQFrame( parent, name, #ifdef USE_NORTHWEST_GRAVITY TQt::WStaticContents #else 0 #endif ), mScrollBarSize( 16 ) { if( parent == 0 || hexBuffer == 0 ) { return; } // // TQt 2.0: // ------- // I use the "CScrollBar" because sometimes (very seldom) when I // do a mHorzScroll->hide() the mHorzScroll->isVisible() remains true // in updateView() for a short while. I need the correct visibility // because I have to redraw the area - due to the "WNorthWestGravity" usage. // // I tried to do a // "while( mHorzScroll->isVisible() ) { mHorzScroll->hide(); }" // but then the loop never ended. The "CScrollBar" emits a "hidden()" // signal whenever is receives a TQHideEvent. // mVertScroll = new CScrollBar( Qt::Vertical, this ); if( mVertScroll == 0 ) { return; } mHorzScroll = new CScrollBar( Qt::Horizontal, this ); if( mHorzScroll == 0 ) { return; } mCorner = new TQWidget( this ); if( mCorner == 0 ) { return; } connect( mHorzScroll, TQT_SIGNAL(valueChanged(int)), TQT_SLOT(changeXPos(int)) ); connect( mVertScroll, TQT_SIGNAL(valueChanged(int)), TQT_SLOT(changeYPos(int)) ); connect( mHorzScroll, TQT_SIGNAL(hidden()), TQT_SLOT(update()) ); connect( mVertScroll, TQT_SIGNAL(hidden()), TQT_SLOT(update()) ); mHorzScroll->hide(); mVertScroll->hide(); mDragManager = new CDragManager(); if( mDragManager == 0 ) { return; } #ifdef USE_DRAG_MOVEMENT mDragManager->setActivateMode( CDragManager::Movement ); #else mDragManager->setActivateMode( CDragManager::Timer ); #endif connect( mDragManager, TQT_SIGNAL(startDrag(bool)), TQT_SLOT(startDrag(bool)) ); setFrameStyle( TQFrame::WinPanel|TQFrame::Sunken ); setWFlags( WResizeNoErase ); setFocusPolicy( TQ_StrongFocus ); mHexBuffer = hexBuffer; mHexBuffer->cursorReset(); mEditMode = mHexBuffer->editMode(); mShowCursor = false; mCursorTimerId = 0; mDocumentMenu = 0; setTextBufferSize(); // Make sure there is a pixmap buffer setStartX(0); setStartY(0); setAcceptDrops(true); setDropHighlight(false); // Init state + frame tqshape setBackgroundColor( mHexBuffer->backgroundColor() ); } CHexViewWidget::~CHexViewWidget( void ) { delete mVertScroll; delete mHorzScroll; delete mCorner; delete mDragManager; } int CHexViewWidget::readFile( TQFile &file, const TQString &url, CProgress &p ) { int errCode = mHexBuffer->readFile( file, url, p ); if( errCode != Err_Success ) { return( errCode ); } initFile(); return( Err_Success ); } int CHexViewWidget::insertFile( TQFile &file, CProgress &p ) { int errCode = mHexBuffer->insertFile( file, p ); if( errCode != Err_Success ) { return( errCode ); } updateWindow( true, true ); emit dataChanged(); emit cursorChanged( mHexBuffer->cursorState() ); emit layoutChanged( mLayout ); return( Err_Success ); } int CHexViewWidget::newFile( const TQString &url ) { int errCode = mHexBuffer->newFile( url ); if( errCode != Err_Success ) { return( errCode ); } initFile(); return( Err_Success ); } int CHexViewWidget::writeFile( TQFile &file, CProgress &p ) { int errCode = mHexBuffer->writeFile( file, p ); if( errCode == Err_Success ) { emit fileState( mHexBuffer->fileState() ); } return( errCode ); } void CHexViewWidget::closeFile( void ) { emit fileClosed( mHexBuffer->url() ); mHexBuffer->closeFile(); initFile(); } void CHexViewWidget::initFile( void ) { setStartX(0); setStartY(0); mHexBuffer->cursorReset(); mHexBuffer->setLayout( mLayout ); mHexBuffer->setFont( mFontInfo ); setEditMode( mEditMode ); setColor( mColor, false ); setCursor( mCursor, false ); setMisc( mMisc ); setBackgroundColor( mHexBuffer->backgroundColor() ); setBackgroundMode( NoBackground ); updateView( true, false ); resizeEvent( 0 ); emit dataChanged(); emit cursorChanged( mHexBuffer->cursorState() ); emit fileState( mHexBuffer->fileState() ); emit encodingChanged( mHexBuffer->encoding() ); emit fileName( mHexBuffer->url(), mHexBuffer->hasFileName() ); emit bookmarkChanged( mHexBuffer->bookmarkList() ); } void CHexViewWidget::setBuffer( CHexBuffer *hexBuffer ) { if( hexBuffer == 0 || mHexBuffer == hexBuffer ) { return; } unselect(); unmark(); mHexBuffer = hexBuffer; mHexBuffer->setLayout( mLayout ); mHexBuffer->setFont( mFontInfo ); setEditMode( mEditMode ); setColor( mColor, false ); setCursor( mCursor, false ); setMisc( mMisc ); if( mLayout.lockLine == false ) { mHexBuffer->matchWidth( width() ); } setBackgroundColor( hexBuffer->backgroundColor() ); setBackgroundMode( NoBackground ); setEditMode( mEditMode ); updateWindow(); emit dataChanged(); emit cursorChanged( mHexBuffer->cursorState() ); emit fileState( mHexBuffer->fileState() ); emit encodingChanged( mHexBuffer->encoding() ); emit layoutChanged( mLayout ); emit inputModeChanged( mHexBuffer->inputMode() ); emit fileName( mHexBuffer->url(), mHexBuffer->hasFileName() ); emit bookmarkChanged( mHexBuffer->bookmarkList() ); } void CHexViewWidget::changeXPos( int p ) { int dx = startX() - p; setStartX(p); if( TQABS(dx) < width() ) { scroll( dx, 0, contentsRect() ); } else { TQWidget::update(); } // // If the start position has become 0, then update the view. This // will remove the scrollbar (if it is visible) if the textarea width // is wider than the text. The scrollbar will then disappear under the // mouse pointer. // if( startX() == 0 ) { updateView( false, false ); } } void CHexViewWidget::changeYPos( int p ) { int dy = startY() - p; setStartY(p); if( TQABS( dy ) < height() ) { scroll( 0, dy, contentsRect() ); } else { TQWidget::update(); } // // If the start position has become 0, then update the view. This // will remove the scrollbar (if it is visible) if the textarea height // is taller than the text. The scrollbar will then disappear under the // mouse pointer. // if( startY() == 0 ) { updateView( false, false ); } } void CHexViewWidget::clipboardChanged( void ) { disconnect(TQApplication::tqclipboard(),TQT_SIGNAL(dataChanged()), this,TQT_SLOT(clipboardChanged())); unselect(); } void CHexViewWidget::paletteChanged( void ) { setColor( mColor, true ); } void CHexViewWidget::fontChanged( void ) { //setFont( kapp->fixedFont, true ); } void CHexViewWidget::filter( SFilterControl &fc ) { int errCode = mHexBuffer->filter( fc ); if( errCode == Err_Success ) { tqrepaint(); emit dataChanged(); emit cursorChanged( mHexBuffer->cursorState() ); } } void CHexViewWidget::insert( SInsertData &id ) { if( id.onCursor == false ) { mHexBuffer->cursorGoto( id.offset, 7 ); } SCursorConfig cc; updateCursor( cc, true ); if( id.size == 0 ) { return; } TQByteArray buf( id.size ); if( buf.isNull() == true ) { return; } buf.fill( 0 ); if( id.pattern.size() > 0 ) { uint size = id.pattern.size()>buf.size() ? buf.size() : id.pattern.size(); if( id.repeatPattern == false ) { memcpy( &buf[0], &id.pattern[0], size ); if( size < buf.size() ) { memset( &buf[size], id.pattern[id.pattern.size()-1], buf.size()-size ); } } else { for( uint i=0; i < buf.size(); i+= size ) { uint s = i+size > buf.size() ? buf.size()-i : size; memcpy( &buf[i], &id.pattern[0], s ); } } } insert( buf ); } void CHexViewWidget::insert( const TQByteArray &buf ) { if( mHexBuffer->documentPresent() == false ) { emit pleaseOpenNewFile(); if( mHexBuffer->documentPresent() == false ) { return; } } uint offset = mHexBuffer->cursorOffset(); int errCode = mHexBuffer->inputAtCursor( buf, 0 ); if( errCode == Err_Success ) { updateWindow( offset, true ); emit dataChanged(); } } void CHexViewWidget::append( const TQByteArray &buf ) { if( mHexBuffer->documentPresent() == false ) { insert( buf ); } else { SCursorConfig cc; cc.emulateControlButton( true ); cursorEnd( cc ); int errCode = mHexBuffer->inputAtCursor( buf, 0 ); if( errCode == Err_Success ) { updateWindow( true, true ); emit dataChanged(); } } } void CHexViewWidget::valueOnCursor( TQByteArray &buf, uint size ) { mHexBuffer->valueOnCursor( buf, size ); } void CHexViewWidget::updateView( bool redraw, bool fixCursor ) { int f2 = frameWidth() * 2; int scrollBarCount = 0; // Number of visible scrollbars int editWidth = 0; int editHeight = 0; for( uint i=0; i < 2; i++ ) { editWidth = width() - f2; // Total available width editHeight = height() - f2; // Total available height int textWidth = dataWidth(); int textHeight = mHexBuffer->totalHeight(); // // This will move the start position of the horizontal scrollbar // to the left (if possible) if the text width is smaller than the // edit width. // if( startX() > 0 ) { int size = mVertScroll->isVisible() == true ? mScrollBarSize : 0; if( startX() + editWidth - size > textWidth ) { int position = textWidth - editWidth + size; setStartX( position > 0 ? position : 0 ); #ifdef USE_NORTHWEST_GRAVITY redraw = true; #endif } } int tooMuchX = textWidth - editWidth; bool horzScrollbarVisible = startX() > 0 || tooMuchX > 0 ? true : false; if( horzScrollbarVisible == true ) { editHeight -= mScrollBarSize; } // // This will move the start position of the vertical scrollbar // to the top (if possible) if the text height is smaller than the // edit height. // if( startY() > 0 ) { if( startY() + editHeight > textHeight ) { int position = textHeight - editHeight; setStartY( position > 0 ? position : 0 ); #ifdef USE_NORTHWEST_GRAVITY redraw = true; #endif } } int tooMuchY = textHeight - editHeight; int startLine = startY() / textHeight; if( startLine > 0 || tooMuchY > 0 ) { editWidth -= mScrollBarSize; tooMuchX += mScrollBarSize; if( horzScrollbarVisible == false && tooMuchX > 0 ) { //Qt::Horizontal scrollbar will be visible after all. editHeight -= mScrollBarSize; tooMuchY += mScrollBarSize; } } if( tooMuchX < startX() ) { tooMuchX = startX(); } if( tooMuchY < startY() ) { tooMuchY = startY(); } scrollBarCount = 0; if( tooMuchX > 0 && documentPresent() == true ) { mHorzScroll->blockSignals( true ); mHorzScroll->setGeometry( 0, editHeight+f2, editWidth+f2,mScrollBarSize); mHorzScroll->setRange( 0, tooMuchX ); mHorzScroll->setValue( startX() ); mHorzScroll->setSteps(mHexBuffer->lineHeight(),editWidth-mScrollBarSize); mHorzScroll->blockSignals( false ); if( mHorzScroll->isVisible() == false ) { mHorzScroll->show(); } scrollBarCount ++; } else { if( mHorzScroll->isVisible() == true ) { mHorzScroll->hide(); } } if( tooMuchY > 0 && documentPresent() == true ) { mVertScroll->blockSignals( true ); mVertScroll->setGeometry( editWidth+f2, 0, mScrollBarSize,editHeight+f2); mVertScroll->setRange( 0, tooMuchY ); mVertScroll->setValue( startY() ); mVertScroll->setSteps(mHexBuffer->lineHeight(), editHeight-mScrollBarSize ); mVertScroll->blockSignals( false ); if( mVertScroll->isVisible() == false ) { mVertScroll->show(); } scrollBarCount ++; } else { if( mVertScroll->isVisible() == true ) { mVertScroll->hide(); } } if( fixCursor == true ) { int position = mHexBuffer->cursorFixedPosition( startY(), height() ); if( position != startY() ) { setStartY( position ); fixCursor = false; continue; } } break; } if( scrollBarCount == 2 ) { mCorner->setGeometry( editWidth+f2, editHeight+f2, mScrollBarSize, mScrollBarSize ); mCorner->show(); } else { mCorner->hide(); } updateFrameSize(); if( redraw == true ) { TQWidget::update(); } } void CHexViewWidget::setPalette( const TQPalette &p ) { TQWidget::setPalette( p ); mCorner->setPalette( p ); mVertScroll->setPalette( p ); mHorzScroll->setPalette( p ); } void CHexViewWidget::setLayout( SDisplayLayout &tqlayout ) { mLayout = tqlayout; mHexBuffer->setLayout( mLayout ); updateWindow(); emit layoutChanged( mLayout ); emit cursorChanged( mHexBuffer->cursorState() ); emit textWidth( defaultWidth() ); } void CHexViewWidget::setInputMode( SDisplayInputMode &input ) { mHexBuffer->setInputMode( input ); emit inputModeChanged( mHexBuffer->inputMode() ); } void CHexViewWidget::setCursor( const SDisplayCursor &cursor, bool /*updateDisplay*/ ) { mCursor = cursor; mHexBuffer->setCursorShapeModifier( cursor.alwaysBlockShape, cursor.thickInsertShape ); setupCursorTimer(); redrawFromOffset( mHexBuffer->cursorOffset(), false ); } void CHexViewWidget::setColor( const SDisplayColor &color, bool updateDisplay ) { mColor = color; mHexBuffer->setColor( mColor ); if( updateDisplay == true ) { tqrepaint(); } } void CHexViewWidget::setFont( const SDisplayFontInfo &fontInfo, bool updateDisplay ) { mFontInfo = fontInfo; mHexBuffer->setFont( mFontInfo ); emit textWidth( defaultWidth() ); if( updateDisplay == true ) { updateWindow(); } } void CHexViewWidget::setMisc( SDisplayMisc &misc ) { mMisc = misc; mHexBuffer->setUndoLevel( misc.undoLevel ); mHexBuffer->setSoundState( misc.inputSound, misc.fatalSound ); mHexBuffer->setBookmarkVisibility( misc.bookmarkOffsetColumn, misc.bookmarkEditor ); if( mHexBuffer->documentPresent() == true ) { TQWidget::update(); } } void CHexViewWidget::setInsertMode( bool insertMode ) { setEditMode( insertMode == true ? CHexBuffer::EditInsert : CHexBuffer::EditReplace ); } int CHexViewWidget::setEncoding( CConversion::EMode mode, CProgress &p ) { int errCode = mHexBuffer->setEncoding( mode, p ); if( errCode == Err_Success ) { tqrepaint(); emit cursorChanged( mHexBuffer->cursorState() ); emit encodingChanged( mHexBuffer->encoding() ); } return( errCode ); } void CHexViewWidget::reportEncoding( void ) { emit encodingChanged( mHexBuffer->encoding() ); } void CHexViewWidget::selectAll( void ) { setSelection( 0, true ); setSelection( mHexBuffer->documentSize(), false ); autoCopy(); emit cursorChanged( mHexBuffer->cursorState() ); } void CHexViewWidget::unselect( void ) { setSelection( 0, true ); emit cursorChanged( mHexBuffer->cursorState() ); } void CHexViewWidget::unmark( void ) { setMark( 0, 0, false ); } int CHexViewWidget::findFirst( SSearchControl &sc ) { int errCode = mHexBuffer->findFirst( sc ); if( errCode == Err_Success ) { updateWindow( true, false ); } return( errCode ); } int CHexViewWidget::findNext( SSearchControl &sc ) { int errCode = mHexBuffer->findNext( sc ); if( errCode == Err_Success ) { updateWindow( true, false ); } return( errCode ); } int CHexViewWidget::findWrap( SSearchControl &sc ) { int errCode = mHexBuffer->findWrap( sc ); if( errCode == Err_Success ) { updateWindow( true, false ); } return( errCode ); } int CHexViewWidget::replaceAll( SSearchControl &sc, bool init ) { int errCode = mHexBuffer->replaceAll( sc, init ); if( errCode == Err_Success ) { updateWindow( true, false ); emit dataChanged(); } return( errCode ); } int CHexViewWidget::replaceMarked( SSearchControl &sc ) { int errCode = mHexBuffer->replaceMarked( sc ); if( errCode == Err_Success ) { updateWindow( true, false ); emit dataChanged(); } return( errCode ); } int CHexViewWidget::collectStrings( CStringCollectControl &sc ) { int errCode = mHexBuffer->collectStrings( sc ); return( errCode ); } int CHexViewWidget::collectStatistic( SStatisticControl &sc, CProgress &p ) { int errCode = mHexBuffer->collectStatistic( sc, p ); return( errCode ); } void CHexViewWidget::gotoOffset( uint offset, uint bit, bool fromCursor, bool forward ) { bool reverse = forward == true ? false : true; mHexBuffer->cursorGoto( offset, bit, reverse, fromCursor ); updateWindow( true, false ); } void CHexViewWidget::gotoOffset( uint offset ) { gotoOffset( offset, 7, true, true ); } int CHexViewWidget::print( CHexPrinter &printer, CProgress &p ) { return( mHexBuffer->print( printer, p ) ); } uint CHexViewWidget::numPage( CHexPrinter &printer ) { return( mHexBuffer->numPage( printer ) ); } int CHexViewWidget::exportText( const SExportText &ex, CProgress &p ) { return( mHexBuffer->exportText( ex, p ) ); } int CHexViewWidget::exportHtml( const SExportHtml &ex, CProgress &p ) { return( mHexBuffer->exportHtml( ex, p ) ); } int CHexViewWidget::exportCArray( const SExportCArray &ex, CProgress &p ) { return( mHexBuffer->exportCArray( ex, p ) ); } void CHexViewWidget::startDrag( bool asText ) { TQByteArray buf; if( asText == true ) { if( mHexBuffer->copySelectedText( buf ) != Err_Success ) { return; } TQDragObject *d = new TQTextDrag( buf.data(), this ); d->dragCopy(); } else { if( mHexBuffer->copySelectedData( buf ) != Err_Success ) { return; } TQDragObject *d = new CHexDrag( buf, this ); d->dragCopy(); } } void CHexViewWidget::copy( void ) { TQByteArray buf; if( mHexBuffer->copySelectedData( buf ) != Err_Success ) { return; } disconnect(TQApplication::tqclipboard(),TQT_SIGNAL(dataChanged()), this,TQT_SLOT(clipboardChanged())); // // Note: Do no give the CHexDrag a parent != 0. The clipboard // owns the current dragdata and will destroy it on exit or // when it receives a new object. If the CHexDrag has a parent // != 0, the CHexDrag object will be destroyed when the parent // is destroyed. We will then have a double destroy situation // when the app. is closed (=> segfault). // TQApplication::tqclipboard()->setData(new CHexDrag( buf )); connect(TQApplication::tqclipboard(),TQT_SIGNAL(dataChanged()), this,TQT_SLOT(clipboardChanged())); } void CHexViewWidget::copyText( int columnSegment ) { TQByteArray buf; if( mHexBuffer->copySelectedText( buf, columnSegment ) != Err_Success ) { return; } disconnect(TQApplication::tqclipboard(),TQT_SIGNAL(dataChanged()), this,TQT_SLOT(clipboardChanged())); TQApplication::tqclipboard()->setText( buf.data() ); connect(TQApplication::tqclipboard(),TQT_SIGNAL(dataChanged()), this,TQT_SLOT(clipboardChanged())); } void CHexViewWidget::paste( void ) { TQMimeSource *data = TQApplication::tqclipboard()->data(); if( data != 0 ) { TQByteArray buf; if( CHexDrag::decode( data, buf ) == true ) { insert( buf ); return; } TQString text; if( TQTextDrag::decode( data, text ) == true ) { TQByteArray buf; if( mClipConvert.decode( buf, text ) == true ) { insert( buf ); } return; } } } void CHexViewWidget::cut( void ) { copy(); // Always make a copy to the clipboard of what we remove. bool success = mHexBuffer->cutSelection(); if( success == false ) { return; } updateWindow( false, true ); emit dataChanged(); } void CHexViewWidget::undo( void ) { bool success = mHexBuffer->undo(); if( success == false ) { return; } updateWindow( true, true ); emit dataChanged(); } void CHexViewWidget::redo( void ) { bool success = mHexBuffer->redo(); if( success == false ) { return; } updateWindow( true, true ); emit dataChanged(); } void CHexViewWidget::addBookmark( int position ) { int errCode = mHexBuffer->addBookmark( position ); if( errCode != Err_Success ) { if( errCode == Err_ListFull ) { replaceBookmark(); } return; } redrawFromOffset( mHexBuffer->cursorOffset(), false ); emit bookmarkChanged( mHexBuffer->bookmarkList() ); } int CHexViewWidget::bookmarkMenu( const TQString &title ) { TQPtrList &list = mHexBuffer->bookmarkList(); if( list.count() == 0 ) { return( -1 ); } TQString text; KPopupMenu *popup = new KPopupMenu( 0 ); popup->insertTitle( title ); for( uint i=0; i < list.count(); i++ ) { const SCursorOffset *p = list.at( i ); if( p == 0 ) { continue; } text.sprintf("%04X:%04X", p->offset>>16, p->offset&0x0000FFFF ); text.prepend( TQString("[%1] %2: ").tqarg(i+1).tqarg(i18n("Offset")) ); popup->insertItem( text, i ); } TQSize s(popup->sizeHint()); TQPoint center( (width()-s.width())/2, (height()-s.height())/2 ); int position = popup->exec( mapToGlobal(center) ); delete popup; return( position ); } void CHexViewWidget::removeBookmark( bool all ) { if( all == true ) { bool success = mHexBuffer->removeBookmark( -1 ); if( success == false ) { return; } TQWidget::update(); // Redraw visisble area. } else { int position = bookmarkMenu( i18n("Remove Bookmark") ); if( position < 0 ) { return; } const SCursorOffset *p = mHexBuffer->bookmarkList().at(position); uint offset = p ? p->offset : 0; bool success = mHexBuffer->removeBookmark( position ); if( success == false ) { return; } redrawFromOffset( offset, false ); } emit bookmarkChanged( mHexBuffer->bookmarkList() ); } void CHexViewWidget::replaceBookmark( void ) { TQPtrList &list = mHexBuffer->bookmarkList(); if( list.count() == 0 ) { return; } int position = bookmarkMenu( i18n("Replace Bookmark") ); if( position < 0 ) { return; } addBookmark( position ); } void CHexViewWidget::gotoBookmark( uint position ) { TQPtrList &list = mHexBuffer->bookmarkList(); if( position >= list.count() ) { return; } SCursorOffset *p = list.at( position ); if( p == 0 ) { return; } mHexBuffer->cursorGoto( p->offset, p->bit ); updateWindow(); } void CHexViewWidget::gotoNextBookmark( bool next ) { TQPtrList &list = mHexBuffer->bookmarkList(); uint offset = mHexBuffer->cursorOffset(); uint diff = ~0; SCursorOffset *match = 0; // // Note: the list is unsorted. // if( next == true ) { for( SCursorOffset *co = list.first(); co != 0; co = list.next() ) { if( co->offset > offset ) { if( co->offset-offset < diff ) { diff = co->offset-offset; match = co; } } } } else { for( SCursorOffset *co = list.first(); co != 0; co = list.next() ) { if( co->offset < offset ) { if( offset-co->offset < diff ) { diff = offset-co->offset; match = co; } } } } if( match == 0 ) { if( next == true ) { // Wrap: Locate entry with smallest offset. offset = ~0; for( SCursorOffset *co = list.first(); co != 0; co = list.next() ) { if( co->offset < offset ) { offset = co->offset; match = co; } } } else { // Wrap: Locate entry with largest offset. offset=0; for( SCursorOffset *co = list.first(); co != 0; co = list.next() ) { if( co->offset > offset ) { offset = co->offset; match = co; } } } } if( match != 0 ) { mHexBuffer->cursorGoto( match->offset, match->bit ); updateWindow(); } } // // Used to test the speed of drawing // #include #include void CHexViewWidget::benchmark( void ) { struct timeval t1, t2; uint loop = 10; gettimeofday( &t1, 0 ); for( uint i=0; i< loop; i++ ) { paintText( contentsRect(), false ); } gettimeofday( &t2, 0 ); uint area = width() * height(); uint last = (t2.tv_sec-t1.tv_sec) * 1000000 + (t2.tv_usec - t1.tv_usec); kdDebug(1501) << "Duration: " << (float)last/ 1000000.0 << endl; kdDebug(1501) << "Duration/loop: " << (float)last/ (1000000.0*(float)loop) << endl; kdDebug(1501) << "Area: " << area << endl; kdDebug(1501) << "Loop: " << loop << endl; } void CHexViewWidget::resizeEvent( TQResizeEvent * ) { setTextBufferSize(); if( mLayout.lockLine == true ) { updateView( false, false ); #ifdef USE_NORTHWEST_GRAVITY paintFrame(); #endif } else { bool state = mVertScroll->isVisible(); int size = (state == true ? mScrollBarSize : 0) + frameWidth()*2; #ifdef USE_NORTHWEST_GRAVITY int w = dataWidth(); #endif bool bufferChanged = mHexBuffer->matchWidth( width() - size ); updateView( false, bufferChanged ); if( mVertScroll->isVisible() != state ) { size = (mVertScroll->isVisible() ? mScrollBarSize : 0) + frameWidth()*2; bufferChanged = mHexBuffer->matchWidth( width() - size ); updateView( false, bufferChanged ); } #ifdef USE_NORTHWEST_GRAVITY if( w != dataWidth() ) { TQWidget::update(); } else { paintFrame(); } #endif } } void CHexViewWidget::paintEvent( TQPaintEvent *e ) { paintText( e->rect(), true ); } void CHexViewWidget::updateFrameSize( void ) { int w = width() - (mVertScroll->isVisible() ? mScrollBarSize : 0); if( w < 0 ) { w = 0; } int h = height() - (mHorzScroll->isVisible() ? mScrollBarSize : 0); if( h < 0 ) { h = 0; } setFrameRect( TQRect(0,0,w,h) ); } void CHexViewWidget::paintFrame( void ) { TQPainter paint; paint.begin( this ); drawFrame( &paint ); paint.end(); } void CHexViewWidget::drawFrame( TQPainter *p ) { // // 2000-01-10 Espen Sand // I want to display the frame with a custom color whenever the widget // accepts a drop. The setPalette() function causes quite a bit of flicker // in the scrollbars (even when PropagationMode is NoChildren), so I // draw the frame manually when it can accept a drop. Note that the // code below is for the frame tqshape "TQFrame::WinPanel|TQFrame::Plain" // if( mDropHighlight == true ) { qDrawPlainRect( p, frameRect(), TQColor("SteelBlue2"), lineWidth() ); } else { TQFrame::drawFrame(p); // Use standard drawFrame } } void CHexViewWidget::keyPressEvent( TQKeyEvent *e ) { SCursorConfig cc; cc.state = e->state(); // // Some special actions that we have to trap here // if( e->state() & ControlButton ) { switch( e->key() ) { case Key_Space: e->accept(); toggleEditor(); return; break; case Key_1: e->accept(); cursorStep( cc, 1 ); return; break; case Key_2: e->accept(); cursorStep( cc, 2 ); return; break; case Key_4: e->accept(); cursorStep( cc, 4 ); return; break; case Key_8: e->accept(); cursorStep( cc, 8 ); return; break; } } if( e->state() & AltButton ) { if( e->key() == Key_Left || e->key() == Key_Right ) { emit pleaseStepFile( e->key() == Key_Left ? true : false ); e->accept(); } else if( e->key() == Key_Up || e->key() == Key_Down ) { gotoNextBookmark( e->key() == Key_Down ? true : false ); e->accept(); } else { e->ignore(); } return; } switch ( e->key() ) { case Key_Left: cursorLeft( cc ); break; case Key_Right: cursorRight( cc ); break; case Key_Up: cursorUp( cc ); break; case Key_Down: cursorDown( cc ); break; case Key_Home: cursorHome( cc ); break; case Key_End: cursorEnd( cc ); break; case Key_Next: cursorPageDown( cc ); break; case Key_Prior: cursorPageUp( cc ); break; case Key_Insert: cursorInsert( cc ); break; case Key_Delete: cursorDelete( cc ); break; case Key_Backspace: cursorBackspace( cc ); break; default: if( (e->text()[0]).isPrint() == true ) { cursorInput( TQString(e->text())[0] ); } break; } e->accept(); } void CHexViewWidget::keyReleaseEvent( TQKeyEvent *e ) { if( ( e->state() & ShiftButton ) && shiftButtonState() == false ) { // // The shift button was pressed when event was triggered, but is // now released. I use this as a sign to copy selected data to the // clipboard. // autoCopy(); } } void CHexViewWidget::mousePressEvent( TQMouseEvent *e ) { // // The RMB popup menu is managed by the KContextMenuManager // if( e->button() == Qt::LeftButton ) { if( e->state() & ControlButton ) { if( KContextMenuManager::showOnButtonPress() == true && mDocumentMenu != 0 ) { mDocumentMenu->popup( e->globalPos() ); } } else { bool cellLevel = mMisc.cursorJump == false; setCursorPosition( e->x(), e->y(), true, cellLevel ); } } else if( e->button() == Qt::MidButton ) { paste(); } } void CHexViewWidget::mouseMoveEvent( TQMouseEvent *e ) { if( e->state() & Qt::LeftButton ) { if( mDragManager->start( e ) == false ) { bool cellLevel = mMisc.cursorJump == false||e->state() & ControlButton; setCursorPosition( e->x(), e->y(), false, cellLevel ); } } } void CHexViewWidget::mouseReleaseEvent( TQMouseEvent *e ) { // // The RMB popup menu is managed by the KContextMenuManager // if( e->button() == Qt::LeftButton ) { if( e->state() & ControlButton ) { if( KContextMenuManager::showOnButtonPress() == false && mDocumentMenu != 0 ) { mDocumentMenu->popup( e->globalPos() ); } } else { if( mDragManager->clear() == true ) { // Remove any selection SCursorConfig cc; cc.setKeepSelection( false ); updateCursor( cc, true ); } else { mHexBuffer->cursorResetEditArea(); autoCopy(); } } } } void CHexViewWidget::wheelEvent( TQWheelEvent *e ) { if( mVertScroll->isVisible() == true ) { TQApplication::sendEvent( mVertScroll, e ); } } void CHexViewWidget::dragEnterEvent( TQDragEnterEvent *e ) { if( TQTextDrag::canDecode(e) || CHexDrag::canDecode(e) || KURLDrag::canDecode(e)) { e->accept(); setDropHighlight( true ); } } void CHexViewWidget::dragLeaveEvent( TQDragLeaveEvent * ) { setDropHighlight( false ); } void CHexViewWidget::dragMoveEvent( TQDragMoveEvent *e ) { // // Move the cursor if we are dragging (readable) text or binary // data. Note: the TQTextDrag::canDecode() will return true if we // are dragging a file so we have to test for KURLDrag::canDecode() // first. // if( KURLDrag::canDecode(e) == true ) { return; } if( TQTextDrag::canDecode(e) == true || CHexDrag::canDecode(e) == true ) { int x = startX() + e->pos().x(); int y = startY() + e->pos().y(); if( mHexBuffer->setCursorPosition( x, y, false, false ) == true ) { SCursorConfig cc; cc.setKeepSelection( true ); updateCursor( cc, false, false ); } } } void CHexViewWidget::dropEvent( TQDropEvent *e ) { TQMimeSource &m = *(TQDropEvent*)e; setDropHighlight( false ); KURL::List list; if( KURLDrag::decode( &m, list ) == true ) { // // This widget can not itself open a file so it will simply pass // the request to a parent that can (hopefully) do this // for( KURL::List::ConstIterator it = list.begin(); it != list.end(); it++ ) { emit pleaseOpenFile( (*it).url(), true, 0 ); } return; } TQByteArray buf; if( CHexDrag::decode( &m, buf ) == true ) { insert( buf ); return; } TQString text; if( TQTextDrag::decode( &m, text ) == true ) { bool success = mClipConvert.decode( buf, text ); if( success == true ) { insert( buf ); } return; } } void CHexViewWidget::showEvent( TQShowEvent * ) { // Currently we do nothing here. } void CHexViewWidget::timerEvent( TQTimerEvent *e ) { if( e->timerId() == mCursorTimerId ) { if( hasFocus() == true ) { if( mCursor.alwaysVisible == true ) { mShowCursor = true; } else { mShowCursor = mShowCursor == true ? false : true; } } else if( mCursor.focusMode == SDisplayCursor::hide ) { mShowCursor = false; } else if( mCursor.focusMode == SDisplayCursor::stopBlinking ) { mShowCursor = true; } else { mShowCursor = mShowCursor == true ? false : true; } mHexBuffer->setShowCursor( mShowCursor ); paintCursor( CHexBuffer::cursor_curr ); } } void CHexViewWidget::focusInEvent( TQFocusEvent * ) { setupCursorTimer(); paintCursor( CHexBuffer::cursor_curr ); } void CHexViewWidget::focusOutEvent( TQFocusEvent * ) { if( mCursor.focusMode != SDisplayCursor::ignore ) { setupCursorTimer(); paintCursor( CHexBuffer::cursor_curr ); } } void CHexViewWidget::setSelection( uint offset, bool init ) { bool selectionChanged = mHexBuffer->selectionSet( offset, init ); if( selectionChanged == true ) { uint off1, off2; mHexBuffer->selectionStartChange( off1, off2 ); if( off1 != off2 ) { redrawInterval( off1, off2 ); } mHexBuffer->selectionStopChange( off1, off2 ); if( off1 != off2 ) { redrawInterval( off1, off2 ); } } mHexBuffer->selectionSyncronize(); } void CHexViewWidget::setMark( uint offset, uint size, bool moveCursor ) { bool changed; if( size == 0 ) { changed = mHexBuffer->markRemove(); } else { mHexBuffer->markSet( offset, size ); if( moveCursor == true ) { changed = false; gotoOffset( offset, 7, false, true ); } else { changed = true; } } if( changed == true ) { uint off1, off2; mHexBuffer->markStartChange( off1, off2 ); if( off1 != off2 ) { redrawInterval( off1, off2 ); } mHexBuffer->markStopChange( off1, off2 ); if( off1 != off2 ) { redrawInterval( off1, off2 ); } } mHexBuffer->markSyncronize(); } void CHexViewWidget::setCursorPosition(int x, int y, bool init, bool cellLevel) { x += startX(); y += startY(); if( mHexBuffer->setCursorPosition( x, y, init, cellLevel ) == false ) { if( init == true ) { unselect(); unmark(); } } else if( init == false ) { SCursorConfig cc; cc.setKeepSelection( true ); updateCursor( cc, false ); } else { SCursorConfig cc; if( mHexBuffer->cursorInsideSelection() == true ) { mDragManager->setup( x - startX(), y - startY() ); cc.setKeepSelection( true ); updateCursor( cc, true, false ); } else { cc.setKeepSelection( false ); updateCursor( cc, true ); } } } void CHexViewWidget::redrawInterval( uint startOffset, uint stopOffset ) { // // Can be improved, I tqrepaint the entire line even if the offsets // only specify one byte. // uint lineStart = mHexBuffer->calculateLine( startOffset ); uint lineStop = mHexBuffer->calculateLine( stopOffset ); if( lineStart <= lineStop ) { redrawLines( lineStart, lineStop - lineStart + 1 ); } else { redrawLines( lineStop, lineStart - lineStop + 1 ); } } void CHexViewWidget::redrawLines( uint docLine, int numLine ) { int lineHeight = mHexBuffer->lineHeight(); int lineOffset = startY() / lineHeight; // FIXME: startY() should return uint if( (uint)lineOffset > docLine ) { numLine -= (lineOffset-docLine); if( numLine <= 0 ) { return; } docLine = lineOffset; } int t = docLine * lineHeight - startY() + frameWidth(); if( mEditMode == CHexBuffer::EditInsert ) { TQRect r = contentsRect(); r.setTop( t ); paintText( contentsRect().intersect( r ), false ); } else { int h = (numLine + (startY() % lineHeight ? 1 : 0)) * lineHeight; TQRect r( contentsRect().left(), t, contentsRect().width(), h ); paintText( contentsRect().intersect( r ), false ); } } void CHexViewWidget::redrawFromOffset( uint offset, bool finishWindow ) { int lineHeight = mHexBuffer->lineHeight(); uint docLine = mHexBuffer->calculateLine( offset ); int t = docLine * lineHeight - startY() + frameWidth(); if( finishWindow == true ) { TQRect r = contentsRect(); r.setTop( t ); paintText( contentsRect().intersect( r ), false ); } else { int h = t + lineHeight; TQRect r( contentsRect().left(), t, contentsRect().width(), h ); paintText( contentsRect().intersect( r ), false ); } } void CHexViewWidget::paintText( const TQRect &rect, bool expand ) { TQRect r = rect; if( expand == true ) { #ifdef USE_NORTHWEST_GRAVITY r.setLeft( r.left() - frameWidth() ); r.setTop( r.top() - frameWidth() ); #endif } if( contentsRect().contains( r ) == false ) { paintFrame(); if( r.left() < frameWidth() ) { r.setLeft( frameWidth() ); } if( r.top() < frameWidth() ) { r.setTop( frameWidth() ); } } int maxX = width() - frameWidth() - 1 - (mVertScroll->isVisible() ? mScrollBarSize : 0); int maxY = height() - frameWidth() - 1 - (mHorzScroll->isVisible() ? mScrollBarSize : 0); if( r.right() > maxX ) { r.setRight( maxX ); } if( r.bottom() > maxY ) { r.setBottom( maxY ); } TQPainter paint( &mTextBuffer ); paint.setFont( mHexBuffer->font() ); int lineHeight = mHexBuffer->lineHeight(); int docLine = (startY() + r.y() - frameWidth()) / lineHeight; if( docLine < 0 ) { docLine = 0; } int y = docLine * lineHeight - startY(); int yMax = r.height(); int xMax = r.x() + r.width(); y += frameWidth(); int s = 0; int d = r.y()-y; int h; while( yMax > 0 ) { mHexBuffer->drawText( paint, docLine, startX()-frameWidth(), r.x(), xMax ); if( d != 0 ) { h = lineHeight - d; if( h > yMax ) { h = yMax; } } else { h = yMax > lineHeight ? lineHeight : yMax; } bitBlt( this, r.x(), r.y()+s, &mTextBuffer, r.x(), d, r.width(), h ); s += h; yMax -= h; docLine += 1; d = 0; } paint.end(); } void CHexViewWidget::paintCursor( int cursorMode ) { TQPainter paint; paint.begin( &mTextBuffer ); paint.setFont( mHexBuffer->font() ); int f = frameWidth(); if( cursorMode == CHexBuffer::cursor_prev ) { int line = mHexBuffer->prevCursorLine(); SCursorPosition p; mHexBuffer->prevCursor( CHexBuffer::edit_primary, p ); mHexBuffer->drawText( paint, line, startX(), p.x, p.x + p.w ); if( p.y + p.h + f > contentsRect().bottom() ) p.h = contentsRect().bottom() - p.y - f + 1; bitBlt( this, p.x+f, p.y+f, &mTextBuffer, p.x, 0, p.w, p.h ); mHexBuffer->prevCursor( CHexBuffer::edit_secondary, p ); mHexBuffer->drawText( paint, line, startX(), p.x, p.x + p.w ); if( p.y + p.h + f > contentsRect().bottom() ) p.h = contentsRect().bottom() - p.y - f + 1; bitBlt( this, p.x+f, p.y+f, &mTextBuffer, p.x, 0, p.w, p.h ); } else { int line = mHexBuffer->cursorLine(); SCursorPosition p; mHexBuffer->currCursor( CHexBuffer::edit_primary, p ); mHexBuffer->drawText( paint, line, startX(), p.x, p.x + p.w ); if( p.y + p.h + f > contentsRect().bottom() ) p.h = contentsRect().bottom() - p.y - f + 1; bitBlt( this, p.x+f, p.y+f, &mTextBuffer, p.x, 0, p.w, p.h ); mHexBuffer->currCursor( CHexBuffer::edit_secondary, p ); mHexBuffer->drawText( paint, line, startX(), p.x, p.x + p.w ); if( p.y + p.h + f > contentsRect().bottom() ) p.h = contentsRect().bottom() - p.y - f + 1; bitBlt( this, p.x+f, p.y+f, &mTextBuffer, p.x, 0, p.w, p.h ); } paint.end(); } void CHexViewWidget::updateCursor( SCursorConfig &cc, bool always, bool touchSelection ) { if( mHexBuffer->cursorChanged() == false && always == false ) { return; } // // Make blinking (and perhaps invisible) cursor visible // setupCursorTimer(); // // Clear cursor at old location // paintCursor( CHexBuffer::cursor_prev ); // // Compute the new position of the vertical scroll bar. // int position, h; if( cc.controlButton() == true ) { // // The cursor should stay fixed (if possible) in the window while // the text is scrolled (e.g., PageUp/Down behavior). The position // of the vertical scrollbar must change just as much as the cursor // has changed in the vertical direction. // h = frameWidth()*2; h += mHorzScroll->isVisible() == false ? 0 : mScrollBarSize; position = mHexBuffer->cursorFixedPosition( startY(), height()-h ); changeYPos( position ); } else { h = frameWidth()*2; h += mHorzScroll->isVisible() == false ? 0 : mScrollBarSize; position = mHexBuffer->cursorChangePosition( startY(), height()-h ); changeYPos( position ); } // // Paint cursor at new location and update the vertical scroll bar. // paintCursor( CHexBuffer::cursor_curr ); mVertScroll->blockSignals( true ); mVertScroll->setValue( position ); mVertScroll->blockSignals( false ); if( touchSelection == true ) { setSelection( mHexBuffer->cursorOffset(), cc.removeSelection() ); unmark(); } emit cursorChanged( mHexBuffer->cursorState() ); } void CHexViewWidget::toggleEditor( void ) { bool success = mHexBuffer->toggleEditor(); if( success == false ) { return; } SCursorConfig cc; updateCursor( cc, true ); redrawFromOffset( mHexBuffer->cursorOffset(), false ); } void CHexViewWidget::cursorStep( SCursorConfig &cc, uint stepSize ) { mHexBuffer->cursorStep( stepSize, cc.altButton() ? false : true, true ); cc.emulateControlButton( false ); updateCursor( cc ); } void CHexViewWidget::cursorLeft( SCursorConfig &cc ) { bool cellLevel = mMisc.cursorJump == false || cc.controlButton(); cc.emulateControlButton( false ); mHexBuffer->cursorLeft( cellLevel ); updateCursor( cc, cellLevel ); } void CHexViewWidget::cursorRight( SCursorConfig &cc ) { bool cellLevel = mMisc.cursorJump == false || cc.controlButton(); cc.emulateControlButton( false ); mHexBuffer->cursorRight( cellLevel ); updateCursor( cc, cellLevel ); } void CHexViewWidget::cursorUp( SCursorConfig &cc ) { mHexBuffer->cursorUp( 1 ); updateCursor( cc ); } void CHexViewWidget::cursorDown( SCursorConfig &cc ) { mHexBuffer->cursorDown( 1 ); updateCursor( cc ); } void CHexViewWidget::cursorHome( SCursorConfig &cc ) { mHexBuffer->cursorHome( cc.controlButton() ); updateCursor( cc ); } void CHexViewWidget::cursorEnd( SCursorConfig &cc ) { mHexBuffer->cursorEnd( cc.controlButton() ); updateCursor( cc ); } void CHexViewWidget::cursorPageDown( SCursorConfig &cc ) { mHexBuffer->cursorDown( height() / mHexBuffer->lineHeight() ); cc.emulateControlButton( true ); updateCursor( cc ); } void CHexViewWidget::cursorPageUp( SCursorConfig &cc ) { mHexBuffer->cursorUp( height() / mHexBuffer->lineHeight() ); cc.emulateControlButton( true ); updateCursor( cc ); } void CHexViewWidget::cursorInsert( SCursorConfig &/*cc*/ ) { // Toggle mode setEditMode( mEditMode == CHexBuffer::EditInsert ? CHexBuffer::EditReplace : CHexBuffer::EditInsert ); } void CHexViewWidget::cursorDelete( SCursorConfig &/*cc*/ ) { int numLine = mHexBuffer->numLines(); bool success = mHexBuffer->removeAtCursor( false ); if( success == false ) { return; } updateWindow( numLine == mHexBuffer->numLines() ? false : true, true ); emit dataChanged(); } void CHexViewWidget::cursorBackspace( SCursorConfig &/*cc*/ ) { int numLine = mHexBuffer->numLines(); bool success = mHexBuffer->removeAtCursor( true ); if( success == false ) { return; } updateWindow( numLine == mHexBuffer->numLines() ? false : true, true ); emit dataChanged(); } void CHexViewWidget::cursorInput( TQChar c ) { uint cursorLine = mHexBuffer->cursorLine(); bool success = mHexBuffer->inputAtCursor( c ); if( success == false ) { return; } updateWindow( cursorLine ); emit dataChanged(); } void CHexViewWidget::setEditMode( CHexBuffer::EEditMode mode ) { mEditMode = mode; mHexBuffer->setEditMode( mEditMode, mCursor.alwaysBlockShape, mCursor.thickInsertShape ); setupCursorTimer(); // // This will redraw the current line (which contains the cursor) // redrawFromOffset( mHexBuffer->cursorOffset(), false ); emit editMode( mEditMode ); } void CHexViewWidget::setDropHighlight( bool dropHighlight ) { mDropHighlight = dropHighlight; if( mDropHighlight == true ) { // // 2000-01-10 Espen Sand // Highlight. I have reimplemented TQFrame::drawFrame(TQPainter *) // to support a custom frame color. I assume the frame tqshape is // "TQFrame::WinPanel|TQFrame::Plain" in that function. // setFrameStyle( TQFrame::WinPanel|TQFrame::Plain ); } else { setFrameStyle( TQFrame::WinPanel|TQFrame::Sunken ); } } #include "hexviewwidget.moc"