diff options
Diffstat (limited to 'src/hexEditor.cpp')
-rwxr-xr-x | src/hexEditor.cpp | 1112 |
1 files changed, 1112 insertions, 0 deletions
diff --git a/src/hexEditor.cpp b/src/hexEditor.cpp new file mode 100755 index 0000000..48cfe8b --- /dev/null +++ b/src/hexEditor.cpp @@ -0,0 +1,1112 @@ +/* $Id: hexEditor.cpp,v 1.14 2006-11-05 04:42:43 ganzhorn Exp $ + * This file is part of lfhex. + * Copyright (C) 2006 Salem Ganzhorn <eyekode@yahoo.com> + * + * 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 version 2. + * + * 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 <math.h> +#include <assert.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <ctype.h> +#include <algorithm> +#include <iostream> + +#include <QApplication> +#include <QMessageBox> +#include <QFileDialog> +#include <QProgressDialog> +#include <QPainter> +#include <QPixmap> +#include <QFocusEvent> +#include <QPaintEvent> +#include <QKeyEvent> +#include <QResizeEvent> +#include <QMouseEvent> + +#include "hexEditor.hpp" +#include "translate.hpp" +#include "local.h" + +extern int errno; + +HexEditor::HexEditor( QWidget * parent ) + : QWidget(parent) +{ + _cols = 5; + _rows = 10; + _charsPerByte = 2; + _base = 16; + _topLeft = 0; + _topMargin = _wordSpacing = 6; + _bytesPerWord = 1; + _lastValidWord = -1; + _selection[SelectionStart] = _selection[SelectionEnd] = -1; + + setFocusPolicy(Qt::StrongFocus); + // the first setBytesPerWord should not come before the first setFont() + QFont font("fixed"); + font.setFixedPitch(1); + setFont( font ); + setBackgroundRole( QPalette::Base ); +} + +HexEditor::~HexEditor() +{ + _reader.close(); +} + +bool HexEditor::isModified() const +{ + return ( _reader.is_open() && _delta.numEdits() ); +} + +bool HexEditor::save( QString filename ) +{ + if( !isModified() ) { + QString errMsg = "Error, file is not open or has not been modified. " + "Aborting save."; + QMessageBox::warning(this,PROGRAM_STRING,errMsg); + return false; + } + + if( filename != "" ) { + // copy open reader file to filename + QString oldFilename = _reader.filename(); + + if( oldFilename == "" ) + return false; + + _reader.close(); + FILE* fin = fopen(C_STR(oldFilename),"r"); + if( !fin ) { + QString errMsg = "Error opening \"" + oldFilename + "\" for reading: " + + strerror(errno); + QMessageBox::warning(this,PROGRAM_STRING,errMsg); + return false; + } + FILE* fout = fopen(C_STR(filename),"w"); + if( !fout ) { + QString errMsg = "Error opening \"" + filename+ "\" for writing: " + + strerror(errno); + QMessageBox::warning(this,PROGRAM_STRING,errMsg); + fclose(fin); + return false; + } + int length; + uchar bytes[4096]; + while( (length = fread(bytes,1,4096,fin)) ) { + fwrite( bytes, 1, length, fout ); + } + fclose(fin); + fclose(fout); + } else { + filename = _reader.filename(); + _reader.close(); + } + + if( filename == "" ) + return false; + + if( !_delta.numEdits() ) + return false; + + if( !writeDeltas(C_STR(filename),_delta) ) { + _reader.open(C_STR(filename)); + return false; + } + _delta.clear(); + _reader.open(C_STR(filename)); + setTopLeft(_topLeft); + return true; +} + +QString HexEditor::filename() const +{ + return _reader.filename(); +} + +bool HexEditor::open( const QString & filename ) +{ + if( closeFile() == QMessageBox::Cancel ) + return false; + + if(!_reader.open(C_STR(filename))) { + QMessageBox::critical( this, "HexEdit", + "Error loading \"" + filename + "\"\n" + + _reader.lastError(), + QMessageBox::Ok,0); + return false; + } + _delta.clear(); // reset modification tree + + // set the new range for the scrollbar + _cursor.setRange(0,_reader.size()); + _cursor.setCharsPerByte(_charsPerByte); + setSelection(SelectionStart,-1); + setSelection(SelectionEnd,-1); + emit rangeChanged(0,_reader.size()/bytesPerLine()); + calculateFontMetrics(); // update labels + setTopLeft(0); // reset the GUI + return true; +} + +int HexEditor::closeFile(bool force) +{ + int retval = QMessageBox::No; + if( _delta.numEdits() ) { + QString message = + "Save changes to \"" + QString(_reader.filename()) + "\"?"; + if( force ) { + retval = QMessageBox::warning( this, + PROGRAM_STRING, + message, + QMessageBox::Yes, + QMessageBox::No ); + } else { + retval = QMessageBox::warning( this, + PROGRAM_STRING, + message, + QMessageBox::Yes, + QMessageBox::No, + QMessageBox::Cancel); + } + } + if( retval == QMessageBox::Yes ) + save(); + return retval; +} + +void HexEditor::setBytesPerWord( int nbytes ) +{ + _bytesPerWord = nbytes; + calculateFontMetrics(); + QResizeEvent *re = new QResizeEvent(QSize(size()),QSize(size())); + resizeEvent(re); + delete re; +} +int HexEditor::fontHeight() const +{ + return _fontHeight; +} +int HexEditor::lineSpacing() const +{ + return _lineSpacing; +} +int HexEditor::fontMaxWidth() const +{ + return _fontMaxWidth; +} +void HexEditor::calculateFontMetrics() +{ + _lineSpacing = fontMetrics().lineSpacing(); + _fontMaxWidth = fontMetrics().maxWidth(); + _wordWidth = _fontMaxWidth*charsPerWord(); + _fontHeight = fontMetrics().height(); + // see how many bytes are needed to show the size of this file + // log base 16 -> log16(x) = log(x)/log(16) + if( _reader.size() ) { + double width = log(static_cast<float>(_reader.size()))/log(16.0)+2; + _offsetLabelBytes = static_cast<int>( width ); + } else { + _offsetLabelBytes = 0; + } + _leftMargin = _topMargin + _fontMaxWidth*(_offsetLabelBytes + 2); + // make sure bboxes are updated with new offset subdivision + QResizeEvent *re = new QResizeEvent(QSize(size()),QSize(size())); + resizeEvent(re); + delete re; +} +void HexEditor::setFont(const QFont& font ) +{ + if( !font.fixedPitch() ) { + cerr << "setFont() failed, font was not fixedPitch()." << endl; + return; + } + QWidget::setFont(font); + calculateFontMetrics(); +} +// set the top left editor to offset in reader +void HexEditor::setTopLeft( off_t offset ) +{ + static bool inTopLeft; + if( inTopLeft ) { + // don't nest + return; + } + inTopLeft = true; + try { + if( offset < 0 ) { + _topLeft = 0; + } else if( offset > _reader.size() ) { + _topLeft = _reader.size(); + } else { + _topLeft = offset; + } + // only let _topLeft be an integer multiple of the line length (round down) + _topLeft = (_topLeft/bytesPerLine()) * bytesPerLine(); + // update the labels + // setOffsetLabels(_topLeft); + + const Delta * delta; + + _reader.seek(_topLeft); + _reader.read(_data,bytesPerPage()); + + // update data from delta map + for( offset = _delta.lower_bound(_topLeft); + (offset != -1) && (offset < _topLeft + bytesPerPage()); + offset = _delta.lower_bound(offset) ) { + delta = _delta.search(offset); + _data[offset++-_topLeft] = delta->newData()[0]; + } + + repaint(); + emit topLeftChanged(_topLeft); + } catch( const exception &e ) { + inTopLeft = false; + throw e; + } + inTopLeft = false; +} + +//void HexEditor::setOffsetLabels( off_t topLeft ) +//{ + // need to impliment manually printing labels +//} + +int HexEditor::topMargin() const +{ + return _topMargin; +} +int HexEditor::leftMargin() const +{ + return _leftMargin; +} +// +// access fn's for offset manip +// +int HexEditor::bytesPerPage() const +{ + return _rows*_cols*bytesPerWord(); +} +int HexEditor::bytesPerWord() const +{ + return _bytesPerWord; +} +int HexEditor::bytesPerLine() const +{ + return bytesPerWord()*wordsPerLine(); +} +int HexEditor::wordsPerLine() const +{ + return _cols; +} +int HexEditor::linesPerPage() const +{ + return _rows; +} +int HexEditor::wordsPerPage() const +{ + return _rows*_cols; +} +int HexEditor::charsPerByte() const +{ + return _charsPerByte; +} +int HexEditor::charsPerWord() const +{ + return _charsPerByte*bytesPerWord(); +} +int HexEditor::charsPerLine() const +{ + return _charsPerByte*(bytesPerLine()); +} +// translate local byte offsets to global byte offsets +off_t HexEditor::globalOffset( off_t local ) const +{ + return local+_topLeft; +} +// translate global byte offsets to viewport byte offsets +off_t HexEditor::localOffset( off_t global ) const +{ + return global-_topLeft; +} + +int HexEditor::offsetToPercent( + off_t offset + ) +{ + // round up + return _reader.size() ? (int)ceil(100.0*offset/_reader.size()) : 0; +} + +// public slots: +bool HexEditor::browseLoadFile() +{ + QString filename = QFileDialog::getOpenFileName(); + if( filename.isNull() ) + return false; + return open(filename); +} + +QRect HexEditor::charBBox( off_t charIdx ) const { + // byteIdx = charIdx/charsPerByte + // wordIdx = byteIdx/bytesPerWord + int wordIdx = (charIdx/charsPerByte())/bytesPerWord(); + int localCharIdx = charIdx % charsPerWord(); + return QRect( _wordBBox[wordIdx].left() + localCharIdx*fontMaxWidth() + + wordSpacing()/2, + _wordBBox[wordIdx].top(), + fontMaxWidth(), + fontHeight() ); +} + +QRect HexEditor::byteBBox( off_t byteIdx ) const { + // wordIdx = byteIdx/bytesPerWord + int wordIdx = byteIdx/bytesPerWord(); + int localByteIdx = byteIdx % bytesPerWord(); + return QRect( _wordBBox[wordIdx].left() + localByteIdx*2*fontMaxWidth() + + wordSpacing()/2, + _wordBBox[wordIdx].top(), + fontMaxWidth()*2, + lineSpacing() ); +} + +void HexEditor::setTopLeftToPercent( int percent ) +{ + setTopLeft( (_reader.size()/100)*percent ); +} + +// +// slot for setting cursor offset. +// +void HexEditor::setOffset( off_t offset ) +{ + off_t oldWordOffset = localWordOffset(); + _cursor.setOffset( offset, 0 ); + // updateWord clamps the wordIdx to [0,_rows*_cols) + updateWord( oldWordOffset ); + emit offsetChanged( _cursor.byteOffset() ); +} + +void HexEditor::nextLine() +{ + setTopLeft(_topLeft+bytesPerLine()); +} +void HexEditor::prevLine() +{ + setTopLeft(_topLeft-bytesPerLine()); +} +void HexEditor::nextPage() +{ + setTopLeft(_topLeft+bytesPerPage()); +} +void HexEditor::prevPage() +{ + setTopLeft(_topLeft-bytesPerPage()); +} + +off_t HexEditor::localByteOffsetAtXY(off_t x, off_t y) +{ + off_t wordIdx; + off_t wordLength = wordSpacing()+wordWidth(); + off_t line = min(y/lineSpacing(),(off_t)linesPerPage()); + + // constrain x to be less than the right side of the last char on a line + x = max( (off_t)0, x - leftMargin()); + x = min(wordsPerLine()*wordLength - 1 ,x); + // constrain y to be > topMargin() and less than bottom of last line + y = max( (off_t)0, y - topMargin()); + line = min(y/lineSpacing(), (off_t)linesPerPage()-1); + wordIdx = line*wordsPerLine() + x/wordLength; + + off_t byteOffsetInWord = (x%wordLength)*bytesPerWord()/wordLength; + // = wordIdx*bytesPerWord + byteOffsetInWord + return min( (off_t) bytesPerPage()-1, + wordIdx*bytesPerWord() + byteOffsetInWord); + +} +// +// event handler implimentation: +// +void HexEditor::setCursorFromXY(int x,int y) +{ + off_t oldWordIdx = localWordOffset(); + + _cursor.setOffset( _topLeft+localByteOffsetAtXY(x,y) ,0 ); + + // update where the cursor used to be, and where it is now + if( oldWordIdx != localWordOffset() ) { + updateWord( oldWordIdx ); + } + updateWord( localWordOffset() ); + emit offsetChanged(_cursor.byteOffset()); +} + +void HexEditor::mousePressEvent( QMouseEvent* e ) +{ + setCursorFromXY(e->x(),e->y()); + + off_t byte_offset = localByteOffset(); + QRect bbox = byteBBox(byte_offset); + if( e->x() > bbox.right() ) { + byte_offset++; + } + setSelection( SelectionStart, globalOffset( byte_offset )); +} + +void HexEditor::mouseMoveEvent( QMouseEvent* e ) +{ + setCursorFromXY(e->x(),e->y()); + + off_t byte_offset = localByteOffset(); + QRect bbox = byteBBox(byte_offset); + if(e->x() > bbox.right() ) { + byte_offset++; + } + + setSelection( SelectionEnd, globalOffset( byte_offset )); +} + +void HexEditor::mouseReleaseEvent( QMouseEvent* e ) +{ + setCursorFromXY(e->x(),e->y()); + + off_t byte_offset = localByteOffset(); + QRect bbox = byteBBox(byte_offset); + if(e->x() > bbox.right() ) { + byte_offset++; + } + + setSelection( SelectionEnd, globalOffset( byte_offset )); +} + +off_t HexEditor::selectionStart() const +{ + if( _selection[SelectionStart] == -1 || _selection[SelectionEnd] == -1 ) + return -1; + return min(_selection[SelectionStart],_selection[SelectionEnd]); +} +// note: end is open. range is: [start,end) + +off_t HexEditor::selectionEnd() const +{ + if( _selection[SelectionStart] == -1 || _selection[SelectionEnd] == -1 ) + return -1; + return max(_selection[SelectionStart],_selection[SelectionEnd]); +} + +void HexEditor::setSelection(SelectionPos_e pos, off_t offset) +{ + if( !_reader.is_open() ) { + return; + } + if( pos == SelectionStart ) { + _selection[SelectionEnd] = -1; + } + _selection[pos] = offset; + + if( _selection[SelectionStart] < 0 ) { + emit selectionChanged(""); + } else { + if( selectionEnd() > -1 && selectionEnd() <= _reader.size() ) { + QString data; + const Delta* delta; + for(off_t i = selectionStart(); + i < selectionEnd(); + ++i) { + if( (delta = _delta.search(i)) ) + data += Translate::ByteToHex(delta->newData()[0]); + else + data += Translate::ByteToHex(_reader[i]); + } + emit selectionChanged( data ); + } else { + emit selectionChanged( "" ); + } + } + + repaint(); +} + +// +// Editor implimentation +// +void HexEditor::keyPressEvent( QKeyEvent *e ) +{ + int key = e->key(); + + switch(_base) { + case -1: + // ascii + // range is taken from qnamespace.h + if( key >= Qt::Key_Space && key <= Qt::Key_AsciiTilde ) { + seeCursor(); + vector<uchar> oldData; + vector<uchar> newData; + vector<uchar> chars; + + oldData.push_back( *(_data.begin()+localByteOffset()) ); + Translate::ByteToChar(chars,oldData); + chars[0] = key; + Translate::CharToByte(newData,chars); + _delta.insert( _cursor.byteOffset(), + oldData, + newData); + _data[_cursor.byteOffset()-_topLeft] = newData[0]; + cursorRight(); + setSelection(SelectionStart,-1); + return; + } + break; + case 16: + if ( key >= 'A' && key <= 'F' ) { + key = tolower(key); + } + if ( (key >= 'a' && key <= 'f') || + (key >= '0' && key <= '9') ) { + // + // make sure we can see where the cursor is + // + seeCursor(); + vector<uchar> oldData; + vector<uchar> newData; + vector<uchar> hex; + + oldData.push_back( *(_data.begin()+localByteOffset()) ); + Translate::ByteToHex(hex,oldData); + hex[_cursor.charOffset()] = key; + Translate::HexToByte(newData,hex); + + _delta.insert( _cursor.byteOffset(), + oldData, + newData ); + _data[_cursor.byteOffset()-_topLeft] = newData[0]; + // move to the next char + cursorRight(); + setSelection(SelectionStart,-1); + return; + } + break; + case 8: + if( key >= '0' && key < '8' ) { + // valid octal key + seeCursor(); + vector<uchar> oldData; + vector<uchar> newData; + vector<uchar> octal; + + oldData.push_back( *(_data.begin()+localByteOffset()) ); + Translate::ByteToOctal(octal,oldData); + octal[_cursor.charOffset()] = key; + Translate::OctalToByte(newData,octal); + + _delta.insert( _cursor.byteOffset(), + oldData, + newData ); + _data[_cursor.byteOffset()-_topLeft] = newData[0]; + cursorRight(); + setSelection(SelectionStart,-1); + return; + } + break; + case 2: + if( key >= '0' && key < '2' ) { + // valid binary key + seeCursor(); + vector<uchar> oldData; + vector<uchar> newData; + vector<uchar> binary; + + oldData.push_back( *(_data.begin()+localByteOffset()) ); + Translate::ByteToBinary(binary,oldData); + binary[_cursor.charOffset()] = key; + Translate::BinaryToByte(newData,binary); + + _delta.insert( _cursor.byteOffset(), + oldData, + newData ); + _data[_cursor.byteOffset()-_topLeft] = newData[0]; + cursorRight(); + setSelection(SelectionStart,-1); + return; + } + break; + } + switch ( e->key() ) { + case Qt::Key_Left: + cursorLeft(); + break; + case Qt::Key_Right: + cursorRight(); + break; + case Qt::Key_Up: + cursorUp(); + break; + case Qt::Key_Down: + cursorDown(); + break; + case Qt::Key_PageUp: + prevPage(); + break; + case Qt::Key_PageDown: + nextPage(); + break; + case Qt::Key_End: + setTopLeft( _reader.size() - bytesPerPage()/2 ); + break; + case Qt::Key_Home: + setTopLeft(0); + break; + default: + e->ignore(); + break; + } +} + +void HexEditor::resizeEvent( QResizeEvent * e ) +{ + int height= lineSpacing(); + int totalWordWidth = wordWidth() + wordSpacing(); + int linewidth = e->size().width(); + + // don't let _rows or _cols drop below 1 + _rows = max(1,(e->size().height() - _topMargin)/height); + _cols = max(1,(e->size().width() - _leftMargin)/totalWordWidth); + + // now update the line && word bbox vectors + _lineBBox.reserve(_rows); + _wordBBox.reserve(_rows*_cols); + int top,left; + for(int r = 0; r < _rows; r++) { + top = r*height + _topMargin; + for(int c = 0; c < _cols; c++) { + left = totalWordWidth*c + _leftMargin; + _wordBBox[r*_cols+c] = QRect(left, //left + top, //top + totalWordWidth, //width + height); //height + } + _lineBBox[r] = QRect(_leftMargin,top,linewidth,height); + } + // calculate offset label bounding box + _labelBBox.setRect(0, // x + 0, // y + _leftMargin, // width + e->size().height()); // height + + // do this to recalculate the amount of displayed data. + setTopLeft(_topLeft); + emit rangeChanged(0,_reader.size()/bytesPerLine()); +} +// +// Reimplimented to be more efficient then repainting the whole screen on +// focus events. +// +void HexEditor::focusInEvent( QFocusEvent* ) +{ + updateWord( localWordOffset() ); +} +void HexEditor::focusOutEvent( QFocusEvent* ) +{ + updateWord( localWordOffset() ); +} +// generate's paint events for wordIdx (global wordIdx) +// is safe to call with any wordIdx +void HexEditor::updateWord( off_t wordIdx ) +{ + if( wordIdx > -1 && wordIdx < _rows*_cols ) + repaint(_wordBBox[wordIdx]); +} + +void HexEditor::paintLabels( QPainter* paintPtr) +{ + // ignore redraw range for first aproximation: + int y = _wordBBox[0].bottom(); + unsigned int i; + off_t offset = _topLeft; + uchar *ucptr; + QString label; + + for(int row = 0; row < _rows;++row) { + label = ""; +#ifdef WORDS_BIGENDIAN + for(i=0, ucptr = (uchar*)&offset; i<sizeof(off_t);++i) { + label += Translate::ByteToHex(*ucptr++); + } +#else + // make sure we write offset labels in big-endian (easier to read) + ucptr = (uchar*)(&offset) + sizeof(off_t)-1; + for(i=0;i<sizeof(off_t);++i) { + label += Translate::ByteToHex(*ucptr--); + } +#endif + label = label.mid(sizeof(off_t)*2-_offsetLabelBytes); + paintPtr->drawText( 5, y, label ); + offset+=bytesPerLine(); + y+=lineSpacing(); + } + // draw dividing line between offset labels and data + int x = leftMargin()-fontMaxWidth(); + paintPtr->drawLine(x,topMargin(), + x,height()-topMargin()); +} +// +// painting optimizations, each time resize is called, it calculates +// the bounding boxes for each word and each line. +// This data can then be retrieved with wordBBox() +// We can then test intersections to see which words need to be redrawn +// +void HexEditor::paintEvent( QPaintEvent* e) +{ +/* QPixmap pixmap(this->width(),this->height()); + pixmap.fill(backgroundRole());*/ + QPainter paint( this );//&pixmap); + + // set up painter;/ + paint.setFont(font()); + const QPalette& p = qApp->palette(); + paint.setBrush(p.background()); + + if( _labelBBox.intersects(e->rect()) ) { + paintLabels(&paint); + } + + QString text; + if( !getDisplayText(text) ) { + cerr << "[error] - internal inconsitency. Please file bug report." + << endl; + return; + } + + // both cursor and selection is drawn underneath the text + drawCursor( paint ); + drawSelection( paint ); + + // Find the stop/start row/col idx's for the repaint + int totalWordWidth = wordWidth()+wordSpacing(); + int row_start = max(0,(e->rect().top()-topMargin())/lineSpacing() ); + int col_start = max(0,(e->rect().left()-leftMargin())/totalWordWidth); + int row_stop = min(_rows-1,e->rect().bottom() / lineSpacing()); + int col_stop = min(_cols-1,e->rect().right() / totalWordWidth); + + // draw text in repaint event + drawTextRegion( paint, text, row_start, row_stop, col_start, col_stop ); +} + +bool HexEditor::getDisplayText( QString& text ) +{ + // get data to draw + switch (_base) { + case 16: + Translate::ByteToHex(text,_data); + break; + case 8: + Translate::ByteToOctal(text,_data); + break; + case 2: + Translate::ByteToBinary(text,_data); + break; + case -1: + Translate::ByteToChar(text,_data); + break; + default: + // error state + return false; + } + return true; +} + +bool HexEditor::wordModified ( off_t wordIdx ) const +{ + off_t lboffset = wordIdx*bytesPerWord()+_topLeft; + off_t nearest_offset = _delta.lower_bound(lboffset); + return ( nearest_offset != -1 && nearest_offset < lboffset+bytesPerWord() ); +} + +// +// accessors for local offsets +// +off_t HexEditor::localByteOffset() const +{ + return _cursor.byteOffset() - _topLeft; +} +off_t HexEditor::localWordOffset() const +{ + return localByteOffset()/bytesPerWord(); +} +// in offset relative to _data[0] +off_t HexEditor::localCharOffset() const +{ + return localByteOffset()*charsPerByte() + _cursor.charOffset(); +} +off_t HexEditor::localLineOffset() const +{ + return localWordOffset()/wordsPerLine(); +} +int HexEditor::wordWidth() const +{ + return _fontMaxWidth*charsPerWord(); +} +int HexEditor::wordSpacing() const +{ + return _wordSpacing; +} +// +// cursor movement members +// +void HexEditor::seeCursor() +{ + // see if it is already visible + if ( _cursor.byteOffset() >= _topLeft && + _cursor.byteOffset() <= _topLeft+bytesPerPage()-1 ) { + updateWord( localWordOffset() ); + return; + } else { + // setTopLeft so cursor is in middle line of screen + setTopLeft( max(_cursor.byteOffset() - bytesPerPage()/2, (off_t)0) ); + } +} + +void HexEditor::cursorLeft() +{ + off_t oldWordIdx = localWordOffset(); + // move the cursor + _cursor.decrByChar(1); + // make sure the user can see the cursor + seeCursor(); + // redraw where the cursor used to be + if( localWordOffset() != oldWordIdx ) + updateWord( oldWordIdx ); + emit offsetChanged( _cursor.byteOffset() ); +} +void HexEditor::cursorRight() +{ + off_t oldWordIdx = localWordOffset(); + _cursor.incrByChar(1); + seeCursor(); + if( localWordOffset() != oldWordIdx ) + updateWord( oldWordIdx ); + emit offsetChanged( _cursor.byteOffset() ); +} +void HexEditor::cursorUp() +{ + off_t oldWordIdx = localWordOffset(); + _cursor.decrByByte( bytesPerLine() ); + seeCursor(); + if( localWordOffset() != oldWordIdx ) + updateWord( oldWordIdx ); + emit offsetChanged( _cursor.byteOffset() ); +} +void HexEditor::cursorDown() +{ + off_t oldWordIdx = localWordOffset(); + _cursor.incrByByte( bytesPerLine() ); + seeCursor(); + if( localWordOffset() != oldWordIdx ) + updateWord( oldWordIdx ); + emit offsetChanged( _cursor.byteOffset() ); +} + +// slots for undo/redo. +// note: it is necessary to reset topLeft to force a reread of the data. +// +void HexEditor::redo() +{ + _delta.redo(); + setTopLeft(_topLeft); + off_t start = selectionStart(); + off_t end = selectionEnd(); + setSelection(SelectionStart,start); + setSelection(SelectionEnd,end); +} + +void HexEditor::undo() +{ + _delta.undo(); + setTopLeft(_topLeft); + off_t start = selectionStart(); + off_t end = selectionEnd(); + setSelection(SelectionStart,start); + setSelection(SelectionEnd,end); +} + +void HexEditor::search( const QString& hexText, bool forwards ) +{ + QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); + if( !hexText.length() || _reader.filename() == "" ) + return; + + if( -1 != _delta.lower_bound(0) ) { + QMessageBox::information(this,PROGRAM_STRING, + "Cannot search file with unsaved modifications."); + return; + } + + vector<uchar> data; + Translate::HexToByte(data,hexText); +// QProgressDialog progress( "Searching...","Cancel", 0, +// _reader.size(),this ); +// progress.setWindowModality( Qt::WindowModal ); +// progress.setWindowTitle(PROGRAM_STRING); +// progress.setValue(0); + off_t pos; + if( forwards ) { + pos = _reader.findIndex(_cursor.byteOffset(),data,_reader.size()-1); + } else { + pos = _reader.rFindIndex(_cursor.byteOffset(),data,0); + } + if( pos < _reader.size() ) { + showMatch(pos,data.size()); + setOffset( forwards?selectionEnd():selectionStart()-1 ); + seeCursor(); + } else { + QMessageBox::information(this,PROGRAM_STRING, + "Could not find search data 0x" + hexText); + } + QApplication::restoreOverrideCursor(); +} +void HexEditor::setBaseASCII() { + setBase(-1); +} +void HexEditor::setBaseHex() +{ + setBase(16); +} +void HexEditor::setBaseOctal() +{ + setBase(8); +} +void HexEditor::setBaseBinary() +{ + setBase(2); +} +void HexEditor::setBase(int base) +{ + switch(base) { + case -1: + // setup ascii editing mode + _charsPerByte = 1; + break; + case 2: + // setup binary editing mode + _charsPerByte = 8; + break; + case 8: + // setup octal editing mode + _charsPerByte = 3; + break; + case 16: + // setup hex editing mode + _charsPerByte = 2; + break; + default: + // just ignore unsupported bases for now + return; + } + _base = base; + _cursor.setCharsPerByte(_charsPerByte); + // refresh the display + // note: cannot call resize() because it will ignore resize events + // if the size has not changed. + QResizeEvent *re = new QResizeEvent(QSize(size()),QSize(size())); + resizeEvent(re); + delete re; + // make sure we can still see the cursor + // switching from a larger base to a smaller base has the + // possibility of pushing the cursor off the screen + seeCursor(); +} + +off_t HexEditor::offset() const +{ + return _cursor.byteOffset(); +} + +Reader * HexEditor::reader() +{ + return &_reader; +} + +void HexEditor::showMatch( off_t pos, int len ) +{ + setSelection( SelectionStart, pos ); + setSelection( SelectionEnd, pos + len ); +} + +void HexEditor::drawTextRegion( QPainter& paint, const QString& text, + int row_start, int row_stop, + int col_start, int col_stop ) +{ + paint.setPen(qApp->palette().foreground().color()); + for(int r = row_start; r <= row_stop; r++) { + for(int c = col_start; c <= col_stop; c++) { + int widx = r*_cols+c; + if ( wordModified( widx ) ) { + paint.setPen(qApp->palette().link().color()); + paint.drawText( _wordBBox[widx].left() + wordSpacing()/2, + _wordBBox[widx].bottom(), + text.mid(widx*charsPerWord(),charsPerWord()) ); + paint.setPen(qApp->palette().foreground().color()); + } else { + paint.drawText( _wordBBox[widx].left() + wordSpacing()/2, + _wordBBox[widx].bottom(), + text.mid(widx*charsPerWord(),charsPerWord()) ); + } + } + } +} + +void HexEditor::drawSelection( QPainter& paint ) +{ + // draw selection + off_t start = max( (off_t)0, selectionStart() - _topLeft); + if( start < bytesPerPage() ) { + off_t stop = min(selectionEnd() - _topLeft, (off_t)bytesPerPage()); + paint.setPen(Qt::NoPen); + paint.setBrush( qApp->palette().highlight() ); + // foreach line with selection + stop--; + while( start <= stop ) { + // linestop = min(stop,endofline) + off_t linestop = + min(stop, start+bytesPerLine()-1 -(start % bytesPerLine())); + QRect bbox = byteBBox(start); + bbox.setRight( byteBBox(linestop).right() ); + paint.drawRect( bbox ); + start = linestop+1; + } + } +} + +void HexEditor::drawCursor( QPainter& paint ) +{ + QBrush b = qApp->palette().mid(); + if( localWordOffset() > -1 && localWordOffset() < wordsPerPage() ) { + if( hasFocus() ) { + // draw a solid cursor + paint.setPen(Qt::NoPen); + paint.setBrush(b); + } else { + // just draw the outline + paint.setPen(b.color()); + paint.setBrush(Qt::NoBrush); + } + QRect box = charBBox( localCharOffset() ); + paint.drawRect( box ); + } +} |