/*
 *   khexedit - Versatile hex editor
 *   Copyright (C) 1999-2000 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <iostream.h>

#include <qpixmap.h>
#include <qtimer.h>
#include <qtooltip.h>

#include <kaction.h>
#include <kapp.h>
#include <kiconloader.h>
#include <kglobal.h>
#include <klocale.h>
#include <kmenubar.h>
#include <kmessagebox.h>
#include <kstdaccel.h>
#include <kstdaction.h>
#include <ktoolbarbutton.h>
#include <kpopupmenu.h>
#include <kdebug.h>

#include "aboutdialog.h"
#include "dialog.h"
#include "draglabel.h"
#include "hexerror.h"
#include "toplevel.h"


QList<KHexEdit> KHexEdit::mWindowList;

KHexEdit::KHexEdit( void )
  : KMainWindow(0, "toplevel"), mStartupOffset(0), mAboutKHexEdit(0), 
  mIsModified(0)
{
  mWindowList.append( this );

  //
  // Create main widget and register it.
  //
  mManager = new CHexManagerWidget( this, "manager" );
  setCentralWidget( mManager );

  //
  // I use an eventfilter to catch all drag/drop events for the
  // area outside the editor window. It will only accept QUriDrag.
  // (The editor window will catch QUriDrag, QTextDrag and CHexDrag)
  //
  installEventFilter( this );
  setAcceptDrops(true);

  //
  // Prepare menus and status bar
  //
  setupActions();
  setupStatusBar();

  connect( hexView(), SIGNAL( cursorChanged( SCursorState & ) ),
	   this, SLOT( cursorChanged( SCursorState & ) ) );
  connect( hexView(), SIGNAL( editMode( CHexBuffer::EEditMode ) ),
	   this, SLOT( editMode( CHexBuffer::EEditMode ) ) );
  connect( hexView(), SIGNAL( encodingChanged( const SEncodeState &)),
	   this, SLOT( encodingChanged( const SEncodeState & )) );
  connect( hexView(), SIGNAL( textWidth( uint ) ),
	   this, SLOT( textWidth( uint ) ) );
  connect( hexView(), SIGNAL( fileState( SFileState & ) ),
	   this, SLOT( fileState( SFileState & ) ) );
  connect( hexView(), SIGNAL( layoutChanged( const SDisplayLayout & ) ),
	   this, SLOT( layoutChanged( const SDisplayLayout & ) ) );
  connect( hexView(), SIGNAL( inputModeChanged( const SDisplayInputMode & ) ),
	   this, SLOT( inputModeChanged( const SDisplayInputMode & ) ) );
  connect( hexView(), SIGNAL( bookmarkChanged( QList<SCursorOffset> &)),
	   this, SLOT( bookmarkChanged( QList<SCursorOffset> & ) ) );
  connect( hexView(), SIGNAL( fileName( const QString &, bool ) ),
	   this, SLOT( fileActive( const QString &, bool ) ) );
  connect( hexView(), SIGNAL( fileRename( const QString &, const QString & )),
	   this, SLOT( fileRename( const QString &, const QString & ) ) );
  connect( hexView(), SIGNAL( fileClosed( const QString & ) ),
	   this, SLOT( fileClosed( const QString & ) ) );
  connect( editor(), SIGNAL( errorLoadFile( const QString & ) ),
	   this, SLOT( removeRecentFile( const QString & ) ) );
  connect( editor(), SIGNAL( operationChanged( bool ) ),
	   this, SLOT( operationChanged( bool ) ) );
  connect( editor(), SIGNAL( removeRecentFiles() ),
	   this, SLOT( removeRecentFiles() ) );
  connect( mManager, SIGNAL( conversionClosed() ),
	   this, SLOT(conversionClosed()) );
  connect( mManager, SIGNAL( searchBarClosed() ),
	   this, SLOT(searchBarClosed()) );

  //
  // Read configuration from file and set the default editor size.
  // Open files if this is the first toplevel window.
  //
  readConfiguration();
  setStartupGeometry();
  initialize( mWindowList.count() == 1 ? true : false );
}


KHexEdit::~KHexEdit( void )
{
  delete mAboutKHexEdit;
  delete mManager;
}


void KHexEdit::setupActions( void )
{
  mAction.newFile    = KStdAction::openNew( editor(), SLOT(newFile()), this );
  mAction.open       = KStdAction::open( editor(), SLOT(open()), this );
  mAction.insert     = new KAction( i18n("&Insert..."), 0,
    editor(), SLOT(insertFile()), this );
  mAction.openRecent = KStdAction::openRecent(0, 0, this);
  connect( mAction.openRecent->popupMenu(), SIGNAL(activated(int )),
     this, SLOT( recentFile(int) ) );

  mAction.save       = KStdAction::save( editor(), SLOT(save()), this );
  mAction.saveAs     = KStdAction::saveAs( editor(), SLOT(saveAs()), this );
  mAction.revert     = KStdAction::revert( editor(), SLOT(reload()), this );
  //mAction.revert     = KStdAction::revert( this, SLOT(resizeTest()), this );

  mAction.close      = KStdAction::close( editor(), SLOT(close()), this );
  mAction.print      = KStdAction::print( editor(), SLOT(print()), this );
  mAction.exportData = new KAction( i18n("E&xport..."), 0,
    editor(), SLOT(exportDialog()), this );
  mAction.cancel     = new KAction( i18n("&Cancel Operation"),
    "stop", 0, editor(), SLOT(stop()), this );
  mAction.readOnly   = new KToggleAction( i18n("&Read only"),
    0, editor(), SLOT(toggleWriteProtection()),this);
  mAction.resizeLock = new KToggleAction( i18n("&Allow Resize"),
   0, editor(), SLOT(toggleResizeLock()),this);
  mAction.newWindow  = new KAction( i18n("N&ew Window"),
    0, this, SLOT(newWindow()),this);
  mAction.closeWindow= new KAction( i18n("Close &Window"),
    0, this, SLOT(closeWindow()),this);
  mAction.quit       = KStdAction::quit( this, SLOT(closeProgram()), this );

  mAction.undo       = KStdAction::undo(  editor(), SLOT(undo()), this );
  mAction.redo       = KStdAction::redo(  editor(), SLOT(redo()), this );
  mAction.cut        = KStdAction::cut(  editor(), SLOT(cut()), this );
  mAction.copy       = KStdAction::copy(  editor(), SLOT(copy()), this );
  mAction.paste      = KStdAction::paste(  editor(), SLOT(paste()), this );
  mAction.selectAll  = KStdAction::selectAll(editor(), SLOT(selectAll()),this);
  mAction.unselect   = new KAction( i18n("&Unselect"), 0,
    editor(), SLOT(unselect()), this );
  mAction.find       = KStdAction::find( editor(), SLOT(find()), this );
  mAction.findNext   = KStdAction::findNext(editor(), SLOT(findNext()), this );
  mAction.findPrev = KStdAction::findPrev(editor(),SLOT(findPrevious()),this );
  mAction.replace    = KStdAction::replace( editor(), SLOT(replace()), this );
  mAction.gotoOffset = new KAction( i18n("&Goto Offset..."), 0,
    editor(), SLOT(gotoOffset()),this );
  mAction.insertPattern = new KAction( i18n("&Insert Pattern..."), 0,
    editor(), SLOT(insertPattern()), this );

  mAction.copyAsText = new KAction( i18n("Copy as &text"), 0,
    editor(), SLOT(copyText()), this );
  mAction.pasteToNewFile = new KAction( i18n("Paste into new &file"), 0,
    editor(), SLOT(pasteNewFile()), this );
  mAction.pasteToNewWindow = new KAction( i18n("Paste into new &window"), 0,
    this, SLOT(pasteNewWindow()), this );

  mAction.insert->setAccel( CTRL+Key_I );
  mAction.gotoOffset->setAccel( CTRL+Key_G );
  mAction.insertPattern->setAccel( KStdAccel::key(KStdAccel::Insert) );

  mAction.hexadecimal = new KRadioAction( i18n("&Hexadecimal"),
    0, this, SLOT(setDisplayMode()), this, "mode" );
  mAction.decimal     = new KRadioAction( i18n("&Decimal"),
    0, this, SLOT(setDisplayMode()),this, "mode" );
  mAction.octal       = new KRadioAction( i18n("&Octal"),
    0, this, SLOT(setDisplayMode()),this, "mode" );
  mAction.binary      = new KRadioAction( i18n("&Binary"),
    0, this, SLOT(setDisplayMode()),this, "mode" );
  mAction.textOnly    = new KRadioAction( i18n("&Text"),
    0, this, SLOT(setDisplayMode()),this, "mode" );
  mAction.hexadecimal->setExclusiveGroup( "displayMode" );
  mAction.decimal->setExclusiveGroup( "displayMode" );
  mAction.octal->setExclusiveGroup( "displayMode" );
  mAction.binary->setExclusiveGroup( "displayMode" );
  mAction.textOnly->setExclusiveGroup( "displayMode" );

  mAction.showOffsetColumn = new KToggleAction( i18n("Show o&ffset column"),
    0, editor(), SLOT(toggleOffsetColumnVisibility()),this);
  mAction.showTextColumn = new KToggleAction( i18n("Show te&xt field"),
    0, editor(), SLOT(toggleTextColumnVisibility()),this);
  mAction.offsetAsDecimal = new KToggleAction( i18n("Off&set as decimal"),
    0, editor(), SLOT(toggleOffsetAsDecimal()),this);
  mAction.dataUppercase = new KToggleAction( i18n("&Upper case (data)"),
    0, editor(), SLOT(toggleDataUppercase()),this);
  mAction.offsetUppercase = new KToggleAction( i18n("Upper &case (offset)"),
    0, editor(), SLOT(toggleOffsetUppercase()),this);

  mAction.defaultEncoding = new KRadioAction( i18n("&Default encoding", "&Default"),
    0, this, SLOT( setEncoding()), this );
  mAction.usAsciiEncoding = new KRadioAction( i18n("US-&ASCII (7 bit)"),
    0, this, SLOT( setEncoding()), this );
  mAction.ebcdicEncoding  = new KRadioAction( i18n("&EBCDIC"),
    0, this, SLOT( setEncoding()), this );
  mAction.customEncoding  = new KAction( i18n("&Custom..."),
    0, editor(), SLOT( encoding()), this );
  mAction.defaultEncoding->setExclusiveGroup( "encodingMode" );
  mAction.usAsciiEncoding->setExclusiveGroup( "encodingMode" );
  mAction.ebcdicEncoding->setExclusiveGroup( "encodingMode" );

  mAction.strings = new KAction( i18n("&Extract Strings..."), 0,
    editor(), SLOT(strings()), this );
  mAction.recordViewer = new KAction( i18n("&Record Viewer..."), 0,
    editor(), SLOT(recordView()), this );
  mAction.filter = new KAction( i18n("&Binary Filter..."), 0,
    editor(), SLOT(filter()), this );
  mAction.characterTable = new KAction( i18n("&Character Table..."), 0,
    editor(), SLOT(chart()), this );
  mAction.converter = new KAction( i18n("C&onverter..."), 0,
    editor(), SLOT(converter()), this );
  mAction.statistics = new KAction( i18n("&Statistics..."), 0,
    editor(), SLOT(statistics()), this );

  mAction.addBookmark = KStdAction::addBookmark( editor(),
    SLOT(addBookmark()), this );
  mAction.replaceBookmark = new KAction( i18n("&Replace Bookmark"), CTRL+Key_E,
    editor(), SLOT(replaceBookmark()), this );
  mAction.removeBookmark = new KAction( i18n("R&emove Bookmark"), CTRL+Key_U,
    editor(), SLOT(removeBookmark()), this );
  mAction.removeAllBookmark = new KAction( i18n("Re&move All"), 0,
    editor(), SLOT(removeAllBookmark()), this );
  mAction.nextBookmark = new KAction( i18n("Goto &Next Bookmark"),
				      ALT+Key_Down, editor(),
				      SLOT(gotoNextBookmark()), this );
  mAction.prevBookmark = new KAction( i18n("Goto &Previous Bookmark"),
				      ALT+Key_Up, editor(),
				      SLOT(gotoPrevBookmark()), this );

  mAction.showToolBar = KStdAction::showToolbar( this, SLOT(showToolBar()),
    this);
  mAction.showStatusBar = KStdAction::showStatusbar(this,SLOT(showStatusBar()),
    this);
  mAction.showFullPath  = new KToggleAction( i18n("Show F&ull Path"),
    0, this, SLOT(showFullPath()), this );

  mAction.tabHide = new KRadioAction( i18n("&Hide"),
    0, this, SLOT(showDocumentTabs()), this );
  mAction.tabShowAboveEditor = new KRadioAction( i18n("&Above editor"),
    0, this, SLOT(showDocumentTabs()), this );
  mAction.tabShowBelowEditor = new KRadioAction( i18n("&Below editor"),
    0, this, SLOT(showDocumentTabs()), this );
  mAction.tabHide->setExclusiveGroup( "editorTab" );
  mAction.tabShowAboveEditor->setExclusiveGroup( "editorTab" );
  mAction.tabShowBelowEditor->setExclusiveGroup( "editorTab" );

  mAction.conversionHide  = new KRadioAction( i18n("&Hide"),
    0, this, SLOT(showConversionField()), this );
  mAction.conversionFloat = new KRadioAction( i18n("&Floating"),
    0, this, SLOT(showConversionField()), this );
  mAction.conversionEmbed = new KRadioAction( i18n("&Embed in Main Window"),
    0, this, SLOT(showConversionField()), this );
  mAction.conversionHide->setExclusiveGroup( "conversionField" );
  mAction.conversionFloat->setExclusiveGroup( "conversionField" );
  mAction.conversionEmbed->setExclusiveGroup( "conversionField" );

  mAction.searchHide = new KRadioAction( i18n("&Hide"),
    0, this, SLOT(showSearchBar()), this );
  mAction.searchShowAboveEditor = new KRadioAction( i18n("&Above editor"),
    0, this, SLOT(showSearchBar()), this );
  mAction.searchShowBelowEditor = new KRadioAction( i18n("&Below editor"),
    0, this, SLOT(showSearchBar()), this );
  mAction.searchHide->setExclusiveGroup( "searchBar" );
  mAction.searchShowAboveEditor->setExclusiveGroup( "searchBar" );
  mAction.searchShowBelowEditor->setExclusiveGroup( "searchBar" );
  mAction.searchShowAboveEditor->setAccel( Key_F5 );
  mAction.searchShowBelowEditor->setAccel( Key_F6 );

  mAction.writeConfiguration =  new KAction( i18n("&Save Options"), 0,
    this, SLOT(writeConfiguration()), this );
  mAction.preferences = KStdAction::preferences(editor(),SLOT(options()),this);
  mAction.favorites = new KAction( i18n("P&rofiles..."), 0,
    editor(), SLOT(favorites()), this );

  mAction.help = KStdAction::help( this, SLOT(appHelpActivated()), this );
  mAction.separator = new KActionSeparator( this );

  //
  // Make the menu entries.
  //
  mAction.permissionMenu   = new KActionMenu( i18n("Per&missions"), this );
  mAction.editSpecialMenu  = new KActionMenu( i18n("Spec&ial"), this );
  mAction.encodingMenu     = new KActionMenu( i18n("Document &Encoding"),this);
  mAction.conversionMenu   = new KActionMenu( i18n("Conversion &Field"),this );
  mAction.documentTabsMenu = new KActionMenu( i18n("&Document Tabs"), this );
  mAction.searchBarMenu    = new KActionMenu( i18n("Searc&hbar"), this );
  mAction.fileMenu      = new KActionMenu( i18n("&File"), this );
  mAction.editMenu      = new KActionMenu( i18n("&Edit"), this );
  mAction.viewMenu      = new KActionMenu( i18n("&View"), this );
  mAction.bookmarkMenu  = new KActionMenu( i18n("&Bookmarks"), this );
  mAction.toolsMenu     = new KActionMenu( i18n("&Tools"), this );
  mAction.documentsMenu = new KActionMenu( i18n("&Documents"), this );
  mAction.settingsMenu  = new KActionMenu( i18n("&Settings"), this );
  mAction.filePopup     = new KActionMenu( "", this );
  mAction.bookmarkPopup = new KActionMenu( i18n("&Bookmarks"), this );


  // Editor popup menu
  mAction.filePopup->insert( mAction.open );
  mAction.filePopup->insert( mAction.save );
  mAction.filePopup->insert( mAction.saveAs );
  mAction.filePopup->insert( mAction.separator );
  mAction.filePopup->insert( mAction.bookmarkPopup );
  mAction.filePopup->insert( mAction.separator );
  mAction.filePopup->insert( mAction.hexadecimal );
  mAction.filePopup->insert( mAction.decimal );
  mAction.filePopup->insert( mAction.octal );
  mAction.filePopup->insert( mAction.binary );
  mAction.filePopup->insert( mAction.textOnly );
  mAction.filePopup->insert( mAction.separator );
  mAction.filePopup->insert( mAction.readOnly );
  mAction.filePopup->insert( mAction.resizeLock );

  hexView()->setPopupMenu( mAction.filePopup->popupMenu() );
  hexView()->setDocumentMenu( mAction.documentsMenu->popupMenu() );

  // File menu
  mAction.fileMenu->insert( mAction.newFile );
  mAction.fileMenu->insert( mAction.open );
  mAction.fileMenu->insert( mAction.insert );
  mAction.fileMenu->insert( mAction.openRecent );
  mAction.fileMenu->insert( mAction.revert );
  mAction.fileMenu->insert( mAction.separator );
  mAction.fileMenu->insert( mAction.save );
  mAction.fileMenu->insert( mAction.saveAs );
  mAction.fileMenu->insert( mAction.close );
  mAction.fileMenu->insert( mAction.separator );
  mAction.fileMenu->insert( mAction.print );
  mAction.fileMenu->insert( mAction.exportData );
  mAction.fileMenu->insert( mAction.separator );
  mAction.fileMenu->insert( mAction.cancel );
  // File-Permissions-Start
  mAction.permissionMenu->insert( mAction.readOnly );
  mAction.permissionMenu->insert( mAction.resizeLock );
  // File-Permissions-Stop
  mAction.fileMenu->insert( mAction.permissionMenu );
  mAction.fileMenu->insert( mAction.separator );
  mAction.fileMenu->insert( mAction.newWindow );
  mAction.fileMenu->insert( mAction.closeWindow );
  mAction.fileMenu->insert( mAction.separator );
  mAction.fileMenu->insert( mAction.quit );

  // Edit menu
  mAction.editMenu->insert( mAction.undo );
  mAction.editMenu->insert( mAction.redo );
  mAction.editMenu->insert( mAction.separator );
  mAction.editMenu->insert( mAction.cut );
  mAction.editMenu->insert( mAction.copy );
  mAction.editMenu->insert( mAction.paste );
  // Edit-special-Start
  mAction.editSpecialMenu->insert( mAction.copyAsText );
  mAction.editSpecialMenu->insert( mAction.separator );
  mAction.editSpecialMenu->insert( mAction.pasteToNewFile );
  mAction.editSpecialMenu->insert( mAction.pasteToNewWindow );
  // Edit-special-Stop
  mAction.editMenu->insert( mAction.editSpecialMenu );
  mAction.editMenu->insert( mAction.separator );
  mAction.editMenu->insert( mAction.selectAll );
  mAction.editMenu->insert( mAction.unselect );
  mAction.editMenu->insert( mAction.separator );
  mAction.editMenu->insert( mAction.find );
  mAction.editMenu->insert( mAction.findNext );
  mAction.editMenu->insert( mAction.findPrev );
  mAction.editMenu->insert( mAction.replace );
  mAction.editMenu->insert( mAction.separator );
  mAction.editMenu->insert( mAction.gotoOffset );
  mAction.editMenu->insert( mAction.insertPattern );

  // View menu
  mAction.viewMenu->insert( mAction.hexadecimal );
  mAction.viewMenu->insert( mAction.decimal );
  mAction.viewMenu->insert( mAction.octal );
  mAction.viewMenu->insert( mAction.binary );
  mAction.viewMenu->insert( mAction.textOnly );
  mAction.viewMenu->insert( mAction.separator );
  mAction.viewMenu->insert( mAction.showOffsetColumn );
  mAction.viewMenu->insert( mAction.showTextColumn );
  mAction.viewMenu->insert( mAction.separator );
  mAction.viewMenu->insert( mAction.offsetAsDecimal );
  mAction.viewMenu->insert( mAction.separator );
  mAction.viewMenu->insert( mAction.dataUppercase );
  mAction.viewMenu->insert( mAction.offsetUppercase );
  mAction.viewMenu->insert( mAction.separator );
  // View-encoding-Start
  mAction.encodingMenu->insert( mAction.defaultEncoding );
  mAction.encodingMenu->insert( mAction.usAsciiEncoding );
  mAction.encodingMenu->insert( mAction.ebcdicEncoding );
  mAction.encodingMenu->insert( mAction.separator );
  mAction.encodingMenu->insert( mAction.customEncoding );
  // View-encoding-Stop
  mAction.viewMenu->insert( mAction.encodingMenu );

  // Tools menu
  mAction.toolsMenu->insert( mAction.strings );
  //mAction.toolsMenu->insert( mAction.recordViewer );
  mAction.toolsMenu->insert( mAction.filter );
  mAction.toolsMenu->insert( mAction.characterTable );
  mAction.toolsMenu->insert( mAction.converter );
  mAction.toolsMenu->insert( mAction.separator );
  mAction.toolsMenu->insert( mAction.statistics );

  // Bookmark menu
  mAction.bookmarkMenu->insert( mAction.addBookmark );
  mAction.bookmarkMenu->insert( mAction.replaceBookmark );
  mAction.bookmarkMenu->insert( mAction.removeBookmark );
  mAction.bookmarkMenu->insert( mAction.removeAllBookmark );
  mAction.bookmarkMenu->insert( mAction.separator );
  mAction.bookmarkMenu->insert( mAction.nextBookmark );
  mAction.bookmarkMenu->insert( mAction.prevBookmark );

  // Documents menu
  connect( mAction.documentsMenu->popupMenu(), SIGNAL(activated(int)),
	   SLOT(documentMenuCB(int)) );

  mAction.settingsMenu->insert( mAction.showToolBar );
  mAction.settingsMenu->insert( mAction.showStatusBar );
  mAction.settingsMenu->insert( mAction.showFullPath );
  mAction.settingsMenu->insert( mAction.separator );
  // Options-DocumentTabs-Start
  mAction.documentTabsMenu->insert( mAction.tabHide );
  mAction.documentTabsMenu->insert( mAction.tabShowAboveEditor );
  mAction.documentTabsMenu->insert( mAction.tabShowBelowEditor );
  // Options-DocumentTabs-Stop
  mAction.settingsMenu->insert( mAction.documentTabsMenu );
  // Options-Conversion-Start
  mAction.conversionMenu->insert( mAction.conversionHide );
  mAction.conversionMenu->insert( mAction.conversionFloat );
  mAction.conversionMenu->insert( mAction.conversionEmbed );
  // Options-Conversion-Stop
  mAction.settingsMenu->insert( mAction.conversionMenu );
  // Options-SearchBar-Start
  mAction.searchBarMenu->insert( mAction.searchHide );
  mAction.searchBarMenu->insert( mAction.searchShowAboveEditor );
  mAction.searchBarMenu->insert( mAction.searchShowBelowEditor );
  // Options-SearchBar-Stop
  mAction.settingsMenu->insert( mAction.searchBarMenu );

  mAction.settingsMenu->insert( mAction.writeConfiguration );
  mAction.settingsMenu->insert( mAction.separator );
  mAction.settingsMenu->insert( mAction.preferences );
  //mAction.settingsMenu->insert( mAction.favorites );

  //
  // Make the menubar.
  //
  mAction.fileMenu->plug( menuBar() );
  mAction.editMenu->plug( menuBar() );
  mAction.viewMenu->plug( menuBar() );
  mAction.bookmarkMenu->plug( menuBar() );
  mAction.toolsMenu->plug( menuBar() );
  mAction.documentsMenu->plug( menuBar() );
  mAction.settingsMenu->plug( menuBar() );
  mAction.separator->plug( menuBar() );
  menuBar()->insertItem( i18n("&Help"), customHelpMenu() );

  //
  // Make the toolbar
  //
  toolBar(0)->setFullSize();
  mDragLabel = new CDragLabel( toolBar(0) );
  mDragLabel->setPixmap( UserIcon( "hexdrag" ) );
  mDragLabel->setDragMask( UserIcon( "hexmask" ) );
  mDragLabel->setEnabled( false ); // Enabled once we open a document
  QToolTip::add( mDragLabel, i18n("Drag document") );
  toolBar(0)->insertWidget( -1, 22, mDragLabel );

  mAction.newFile->plug( toolBar(0) );
  mAction.open->plug( toolBar(0) );
  mAction.revert->plug( toolBar(0) );
  mAction.save->plug( toolBar(0) );
  mAction.print->plug( toolBar(0) );
  mAction.find->plug( toolBar(0) );
  mAction.findNext->plug( toolBar(0) );
  mAction.findPrev->plug( toolBar(0) );
  mAction.cut->plug( toolBar(0) );
  mAction.copy->plug( toolBar(0) );
  mAction.paste->plug( toolBar(0) );
  mAction.undo->plug( toolBar(0) );
  mAction.redo->plug( toolBar(0) );
  mAction.cancel->plug( toolBar(0) );
  mAction.help->plug( toolBar(0) );

  int id = 100;
  toolBar(0)->insertButton( MainBarIcon("lock"), id, SIGNAL(clicked()), 
    editor(), SLOT(toggleWriteProtection()), true, 
    i18n("Toggle write protection") );
  toolBar(0)->alignItemRight( id );
  mWriteProtectButton = toolBar(0)->getButton(id);
}


void KHexEdit::setupStatusBar( void )
{
  CStatusBarProgress *progess = new CStatusBarProgress( statusBar() );
  statusBar()->addWidget( progess, 10 );
  connect( progess, SIGNAL(pressed()), editor(), SLOT(stop()) );
  connect( editor(), SIGNAL( setProgress( int ) ),
	   progess, SLOT( setValue( int ) ) );
  connect( editor(), SIGNAL( setProgress( int, int ) ),
	   progess, SLOT( setValue( int, int ) ) );
  connect( editor(), SIGNAL( enableProgressText( bool ) ),
	   progess, SLOT( setTextEnabled( bool ) ) );
  connect( editor(), SIGNAL( setProgressText( const QString & ) ),
	   progess, SLOT( setText( const QString & ) ) );

  statusBar()->insertFixedItem( i18n("Selection: 0000:0000 0000:0000"),
				status_Selection );
  statusBar()->insertFixedItem( i18n("M"), status_Modified );
  statusBar()->insertFixedItem( i18n("OVR"), status_Ovr );
  statusBar()->insertFixedItem( i18n("Size: FFFFFFFFFF"), status_Size );
  statusBar()->insertFixedItem( i18n("Offset: FFFFFFFFFF-F"), status_Offset );
  statusBar()->insertFixedItem( i18n("FFF"), status_Layout );
  statusBar()->insertFixedItem( i18n("RW"), status_WriteProtect );

  statusBar()->setItemAlignment( status_Selection, AlignLeft|AlignVCenter );
  statusBar()->setItemAlignment( status_Size, AlignLeft|AlignVCenter );
  statusBar()->setItemAlignment( status_Offset, AlignLeft|AlignVCenter );

  //
  // Some good default strings we can use after the fields have got
  // their fixed width.
  //
  statusBar()->changeItem( i18n("Offset:"), status_Offset );
  statusBar()->changeItem( i18n("Size:"), status_Size );
  statusBar()->changeItem( "", status_Modified );
  statusBar()->changeItem( "", status_Selection );
  connect( statusBar(),SIGNAL(pressed(int)),this,SLOT(statusBarPressed(int)) );
}


void KHexEdit::removeRecentFiles( void )
{
  mAction.openRecent->clear();
}


void KHexEdit::initialize( bool openFiles )
{
  mUndoState = CHexBuffer::UndoOk; // Reset in function below
  setUndoState( 0 );

  mSelectionOffset = 0;
  mSelectionSize   = 1;
  mSelectionAsHexadecimal = true;
  setSelectionState( 0, 0 );

  operationChanged( false );

  editor()->initialize();

  mAction.showToolBar->setChecked( mShowToolBar );
  mAction.showStatusBar->setChecked( mShowStatusBar );
  mAction.showFullPath->setChecked( mShowFullPath );
  showToolBar();
  showStatusBar();
  showFullPath();


  if( mAction.openRecent->popupMenu()->count() > 0 && openFiles == true )
  {
    if( editor()->openFile() == SDisplayMisc::allRecent )
    {
      for( uint i=0; i < mAction.openRecent->popupMenu()->count(); i++ )
      {
	mStartupFileList.append( mAction.openRecent->popupMenu()->text(i) );
	if( editor()->gotoStartupOffset() == true &&
	    mRecentOffsetList.count() > i )
	{
	  mStartupOffsetList.append( *mRecentOffsetList.at(i) );
	}
	else
	{
	  mStartupOffsetList.append( QString("0") );
	}
      }
    }
    else if( editor()->openFile() == SDisplayMisc::mostRecent )
    {
      mStartupFileList.append( mAction.openRecent->popupMenu()->text(0) );
      if( editor()->gotoStartupOffset() == true &&
	  mRecentOffsetList.count() > 0 )
      {
	mStartupOffsetList.append( *mRecentOffsetList.at(0) );
      }
      else
      {
	mStartupOffsetList.append( QString("0") );
      }
    }
  }

  //
  // Open file(s) after editor has been displayed.
  // Main reason: If anything goes wrong and an error dialog box
  // is displayed, then the editor will not be visible until the error
  // is confirmed and the (modal) dialog is closed.
  //
  QTimer::singleShot( 100, this, SLOT(delayedStartupOpen()) );
}


void KHexEdit::delayedStartupOpen( void )
{
  open( mStartupFileList, mStartupOffsetList );
  mStartupFileList.clear();
  mStartupOffsetList.clear();
}


void KHexEdit::statusBarPressed( int id )
{
  if( id == status_WriteProtect )
  {
    editor()->toggleWriteProtection();
  }
  else if( id == status_Offset )
  {
    editor()->toggleOffsetAsDecimal();
  }
  else if( id == status_Selection )
  {
    if( mSelectionSize > 0 )
    {
      mSelectionAsHexadecimal = mSelectionAsHexadecimal == true ? false : true;
      setSelectionText( mSelectionOffset, mSelectionSize );
    }
  }

}



void KHexEdit::open( QStringList &fileList, QStringList &offsetList )
{
  //
  // We load in reverse to allow the first document in
  // the list to become the active one.
  //

  uint offset;
  for( uint i = fileList.count(); i>0; i-- )
  {
    if( i <= offsetList.count() )
    {
      offset = (*offsetList.at(i-1)).toUInt(0,16);
    }
    else
    {
      offset = 0;
    }
    editor()->open( *fileList.at(i-1), true, offset );
  }
}

void KHexEdit::addRecentFile( const QString &fileName )
{
  if( fileName.isEmpty() == true )
  {
    return;
  }

  if( fileName.contains( i18n( "Untitled" ), false ) )
  {
    return;
  }

  uint recentCount = mAction.openRecent->popupMenu()->count();
  for( uint id=0; id < recentCount; id++ )
  {
    QString text = mAction.openRecent->popupMenu()->text(id);
    if( text.isNull() == true )
    {
      continue;
    }

    if( fileName == text )
    {
      mAction.openRecent->popupMenu()->removeItem(id);
    }
  }

  while( mAction.openRecent->popupMenu()->count() >= 5 )
  {
    mAction.openRecent->popupMenu()->removeItemAt(4);
  }

  mAction.openRecent->popupMenu()->insertItem( fileName, 0, 0 );
  for( uint i = 0; i < 5; i++ )
  {
    mAction.openRecent->popupMenu()->setId( i, i );
  }
}


void KHexEdit::removeRecentFile( const QString &fileName )
{
  if( fileName.isEmpty() == true )
  {
    return;
  }

  if( fileName.contains( i18n( "Untitled" ), false ) )
  {
    return;
  }

  uint recentCount = mAction.openRecent->popupMenu()->count();
  uint i;

  for( i=0; i < recentCount; i++ )
  {
    QString text = mAction.openRecent->popupMenu()->text(i);
    if( text.isNull() )
    {
      continue;
    }

    if( fileName == text )
    {
      mAction.openRecent->popupMenu()->removeItem(i);
    }
  }
  for( i = 0; i < 5; i++ )
  {
    mAction.openRecent->popupMenu()->setId( i, i );
  }
}


void KHexEdit::renameRecentFile(const QString &curName, const QString &newName)
{
  if( curName.contains( i18n( "Untitled" ), false ) )
  {
    addRecentFile( newName );
  }
  else
  {
    for( uint i=0; i <  mAction.openRecent->popupMenu()->count(); i++ )
    {
      QString text = mAction.openRecent->popupMenu()->text(i);
      if( text.isNull() ) { continue; }

      if( curName == text )
      {
	mAction.openRecent->popupMenu()->changeItem( newName, i );
	break;
      }
    }
  }
}


void KHexEdit::recentFile( int id )
{
  //
  // 2000-01-02 Espen Sand
  // For some reason I sometimes get a segfault when I open the
  // file directly from this slot. It used to work, but at some
  // point of Qt (2.x) upgrades this began to happen. To avoid
  // the problem I delay the opening until the program runs in
  // the main event loop.
  //
  mRecentFileId = id;
  QTimer::singleShot(0, this, SLOT(delayedRecentOpen()) );
}


void KHexEdit::delayedRecentOpen( void )
{
  QString url = mAction.openRecent->popupMenu()->text(mRecentFileId);
  if( url.isNull() == false )
  {
    editor()->open( url, false, 0 );
  }
}



KHexEdit *KHexEdit::newWindow( void )
{
  KHexEdit *hexEdit = new KHexEdit;
  if( hexEdit == 0 )
  {
    QString msg = i18n( "Can not create new window.\n" );
    msg += hexError( Err_NoMemory );
    KMessageBox::error( topLevelWidget(), msg );
    return(0);
  }
  hexEdit->show();
  return(hexEdit);
}



void KHexEdit::pasteNewWindow( void )
{
  KHexEdit *app = newWindow();
  if( app != 0 )
  {
    app->editor()->pasteNewFile();
  }
}



void KHexEdit::closeWindow( void )
{
  close();
}


void KHexEdit::closeProgram( void )
{
  if( mWindowList.count() > 1 )
  {
    for( KHexEdit *w = mWindowList.first(); w != 0; w = mWindowList.next() )
    {
      if( w->editor()->modified() == true )
      {
	QString msg = i18n(""
	  "There are windows with unsaved modified documents.\n"
	  "If you quit now, these modifications will be lost.\n\n"
	  "Proceed?");
	int reply = KMessageBox::warningYesNo( topLevelWidget(), msg );
	if( reply == KMessageBox::Yes )
	{
	  break;
	}
	else
	{
	  return;
	}
      }
    }

    //
    // Save the order in the Recent queue to a list. This list will be written
    // to disk.
    //
    makeExitFileList();

    //
    // Ths will write current configuration to disk.
    //
    queryExit();

    //
    // Make sure every widget is destroyed. The reason why I do this
    // (besides it is the clean way to do it) is to make sure any 
    // configuration updates in the various destructors are triggered.
    // 
    mWindowList.setAutoDelete(true);
    while( mWindowList.first() )
    {
      mWindowList.removeRef( mWindowList.first() );
    }

    kapp->quit();
  }
  else
  {
    closeWindow();
  }

}


bool KHexEdit::queryClose( void )
{
  //
  // Save the order in the Recent queue to a list. This list will be written
  // to disk.
  //
  makeExitFileList();

  bool success = editor()->closeAll();
  if( success == true )
  {
    mWindowList.removeRef( this );
  }

  return( success );
}


//
// We will always return true here.
//
bool KHexEdit::queryExit( void )
{
  writeConfiguration();
  return( true );
}


void KHexEdit::makeExitFileList( void )
{
  //
  // Note: I append five names, even if they are empty because the recent
  // menu contains fewer. This is important because otherwise we may end up
  // having duplicate entries in the config. file.
  //

  mExitFileList.clear();
  mExitOffsetList.clear();

  if( editor()->discardRecentFiles() == true )
  {
    for( uint i=0; i < 5; i++ )
    {
      mExitFileList.append(QString(""));
      mExitOffsetList.append( QString("0") );
    }
  }
  else
  {
    uint offset;
    for( uint i=0; i < 5; i++ )
    {
      mExitFileList.append(mAction.openRecent->popupMenu()->text(i));
      if( editor()->isOpen( mAction.openRecent->popupMenu()->text(i), offset )
	  == true )
      {
	mExitOffsetList.append( QString("%1").arg(offset,0,16) );
      }
      else
      {
	mExitOffsetList.append( QString("%1").arg(0,0,16) );
      }
    }
  }
}


void KHexEdit::readConfiguration( void )
{
  KConfig &config = *kapp->config();
  readConfiguration( config );
}


void KHexEdit::writeConfiguration( void )
{
  KConfig &config = *kapp->config();
  writeConfiguration( config );
}


void KHexEdit::writeConfiguration( KConfig &config )
{
  config.setGroup("General Options" );
  config.writeEntry("Width", width() );
  config.writeEntry("Height", height() );
  config.writeEntry("ShowToolBar", mShowToolBar );
  config.writeEntry("ShowStatusBar", mShowStatusBar );
  config.writeEntry("ShowFullPath",  mShowFullPath );
  config.writeEntry("ConversionPosition", (int)mManager->conversionPosition());
  config.writeEntry("TabBarPosition", (int)mManager->tabBarPosition());
  config.writeEntry("SearchBarPosition", (int)mManager->searchBarPosition());

  for( uint i=0; i < mExitFileList.count(); i++ )
  {
    config.writeEntry( QString("Recent%1").arg(i+1), *mExitFileList.at(i) );
  }
  config.writeEntry( QString("Offset"), mExitOffsetList );

  converter()->writeConfiguration( config );
  editor()->writeConfiguration( config );

  config.sync();
}


void KHexEdit::readConfiguration( KConfig &config )
{
  config.setGroup("General Options");
  mShowToolBar    = config.readBoolEntry( "ShowToolBar", true );
  mShowStatusBar  = config.readBoolEntry( "ShowStatusBar", true );
  mShowFullPath   = config.readBoolEntry( "ShowFullPath", true );
  int position    = config.readNumEntry( "ConversionPosition",
					 CHexManagerWidget::Embed );

  mAction.conversionHide->blockSignals(true);
  mAction.conversionFloat->blockSignals(true);
  mAction.conversionEmbed->blockSignals(true);
  mAction.conversionHide->setChecked(
    position == CHexManagerWidget::Hide );
  mAction.conversionFloat->setChecked(
    position == CHexManagerWidget::Float );
  mAction.conversionEmbed->setChecked(
    position == CHexManagerWidget::Embed );
  mAction.conversionHide->blockSignals(false);
  mAction.conversionFloat->blockSignals(false);
  mAction.conversionEmbed->blockSignals(false);
  showConversionField();

  position = config.readNumEntry( "TabBarPosition", CHexManagerWidget::Hide );
  mAction.tabHide->blockSignals(true);
  mAction.tabShowAboveEditor->blockSignals(true);
  mAction.tabShowBelowEditor->blockSignals(true);
  mAction.tabHide->setChecked(
    position==CHexManagerWidget::Hide );
  mAction.tabShowAboveEditor->setChecked(
    position==CHexManagerWidget::AboveEditor );
  mAction.tabShowBelowEditor->setChecked(
    position==CHexManagerWidget::BelowEditor );
  mAction.tabHide->blockSignals(false);
  mAction.tabShowAboveEditor->blockSignals(false);
  mAction.tabShowBelowEditor->blockSignals(false);
  showDocumentTabs();

  position = config.readNumEntry( "SearchBarPosition",
				  CHexManagerWidget::BelowEditor );
  mAction.searchHide->blockSignals(true);
  mAction.searchShowAboveEditor->blockSignals(true);
  mAction.searchShowBelowEditor->blockSignals(true);
  mAction.searchHide->setChecked(
    position==CHexManagerWidget::Hide );
  mAction.searchShowAboveEditor->setChecked(
    position==CHexManagerWidget::AboveEditor );
  mAction.searchShowBelowEditor->setChecked(
    position==CHexManagerWidget::BelowEditor );
  mAction.searchHide->blockSignals(false);
  mAction.searchShowAboveEditor->blockSignals(false);
  mAction.searchShowBelowEditor->blockSignals(false);
  showSearchBar();

  for( uint i = 0; i < 5; i++ )
  {
    QString fileName = config.readEntry( QString("Recent%1").arg(i+1) );
    if( fileName.isEmpty() == false )
    {
      int pos = mAction.openRecent->popupMenu()->count();
      mAction.openRecent->popupMenu()->insertItem( fileName, pos, pos );
    }
  }

  mRecentOffsetList = config.readListEntry(QString("Offset") );

  converter()->readConfiguration( config );
  editor()->readConfiguration( config );
}



void KHexEdit::setStartupGeometry( void )
{
  KConfig &config = *kapp->config();
  config.setGroup("General Options");
  int w = config.readNumEntry( "Width", -1 );
  int h = config.readNumEntry( "Height", -1 );
  if( w < 10 || h < 10 )
  {
    setGeometry( 0, 0, mManager->preferredWidth(), 500 );
  }
  else
  {
    setGeometry( 0, 0, w, h );
  }
}



void KHexEdit::operationChanged( bool state )
{
  mAction.cancel->setEnabled( state );
}



void KHexEdit::cursorChanged( SCursorState &state )
{
  QString offset( i18n("Offset:") );

  if( state.valid == false )
  {
    statusBar()->changeItem( offset, status_Offset );
  }
  else
  {
    SDisplayLayout &layout = editor()->layout();

    if( layout.offsetMode == SDisplayLayout::hexadecimal )
    {
      if( layout.offsetUpperCase == false )
      {
	offset += QString().sprintf( " %04x:%04x-%u", state.offset>>16,
				     state.offset&0x0000FFFF, state.cell );
      }
      else
      {
	offset += QString().sprintf( " %04X:%04X-%u", state.offset>>16,
				     state.offset&0x0000FFFF, state.cell );
      }
    }
    else
    {
      offset += QString().sprintf( " %010u-%u", state.offset, state.cell );
    }
    statusBar()->changeItem( offset, status_Offset );
  }

  setUndoState( state.undoState );
  setSelectionState( state.selectionOffset, state.selectionSize );
}




void KHexEdit::fileState( SFileState &state )
{
  if( state.valid == true )
  {
    statusBar()->changeItem( i18n("Size: %1").arg( state.size ), status_Size);
    statusBar()->changeItem( state.modified ? "!" : "", status_Modified);
    
    if( mIsModified != state.modified )
    {
      mIsModified = state.modified;
      setupCaption( hexView()->url() );
    }

  }
  else
  {
    statusBar()->changeItem( "", status_Size );
    statusBar()->changeItem( "", status_Modified );
  }
}



void KHexEdit::layoutChanged( const SDisplayLayout &layout )
{
  KRadioAction *radioAction;

  if( layout.primaryMode == SDisplayLayout::hexadecimal )
  {
    radioAction = mAction.hexadecimal;
  }
  else if( layout.primaryMode == SDisplayLayout::decimal )
  {
    radioAction = mAction.decimal;
  }
  else if( layout.primaryMode == SDisplayLayout::octal )
  {
    radioAction = mAction.octal;
  }
  else if( layout.primaryMode == SDisplayLayout::binary )
  {
    radioAction = mAction.binary;
  }
  else
  {
    radioAction = mAction.textOnly;
  }
  radioAction->blockSignals(true);
  radioAction->setChecked( true );
  radioAction->blockSignals(false);

  mAction.showOffsetColumn->blockSignals(true);
  mAction.showTextColumn->blockSignals(true);
  mAction.offsetAsDecimal->blockSignals(true);
  mAction.dataUppercase->blockSignals(true);
  mAction.offsetUppercase->blockSignals(true);

  mAction.showOffsetColumn->setChecked( layout.offsetVisible );
  mAction.showTextColumn->setEnabled(
    layout.primaryMode != SDisplayLayout::textOnly );
  mAction.showTextColumn->setChecked(
    layout.secondaryMode != SDisplayLayout::hide );
  mAction.offsetAsDecimal->setChecked(
    layout.offsetMode != SDisplayLayout::hexadecimal);
  mAction.dataUppercase->setChecked( layout.primaryUpperCase );
  mAction.offsetUppercase->setChecked( layout.offsetUpperCase );

  mAction.showOffsetColumn->blockSignals(false);
  mAction.showTextColumn->blockSignals(false);
  mAction.offsetAsDecimal->blockSignals(false);
  mAction.dataUppercase->blockSignals(false);
  mAction.offsetUppercase->blockSignals(false);

  if( layout.primaryMode == SDisplayLayout::hexadecimal )
  {
    statusBar()->changeItem( i18n("Hex"), status_Layout );
  }
  else if( layout.primaryMode == SDisplayLayout::decimal )
  {
    statusBar()->changeItem( i18n("Dec"), status_Layout );
  }
  else if( layout.primaryMode == SDisplayLayout::octal )
  {
    statusBar()->changeItem( i18n("Oct"), status_Layout );
  }
  else if( layout.primaryMode == SDisplayLayout::binary )
  {
    statusBar()->changeItem( i18n("Bin"), status_Layout );
  }
  else if( layout.primaryMode == SDisplayLayout::textOnly )
  {
    statusBar()->changeItem( i18n("Txt"), status_Layout );
  }
}


void KHexEdit::inputModeChanged( const SDisplayInputMode &mode )
{
  mAction.readOnly->blockSignals(true);
  mAction.resizeLock->blockSignals(true);
  mAction.readOnly->setChecked( mode.readOnly );
  mAction.resizeLock->setChecked( mode.allowResize );
  mAction.readOnly->blockSignals(false);
  mAction.resizeLock->blockSignals(false);

  statusBar()->changeItem( mode.readOnly == true ?
			   i18n("R") : i18n("RW"), status_WriteProtect );

  if( mode.readOnly == false )
  {
    mWriteProtectButton->setIcon("edit");
  }
  else
  {
    mWriteProtectButton->setIcon("lock");
  }
}


void KHexEdit::bookmarkChanged( QList<SCursorOffset> &list )
{
  QPopupMenu *menu  = mAction.bookmarkMenu->popupMenu();
  QPopupMenu *popup = mAction.bookmarkPopup->popupMenu();

  if( menu->count() == 4 && list.count() == 0 )
  {
    return;
  }

  menu->clear();
  popup->clear();

  mAction.bookmarkMenu->insert( mAction.addBookmark );
  mAction.bookmarkMenu->insert( mAction.replaceBookmark );
  mAction.bookmarkMenu->insert( mAction.removeBookmark );
  mAction.bookmarkMenu->insert( mAction.removeAllBookmark );
  mAction.bookmarkMenu->insert( mAction.separator );
  mAction.bookmarkMenu->insert( mAction.nextBookmark );
  mAction.bookmarkMenu->insert( mAction.prevBookmark );

  if( list.count() == 0 )
  {
    return;
  }
  mAction.bookmarkMenu->insert( mAction.separator );

  QString text, offset;
  int i=0;

  for( SCursorOffset *p=list.first(); p!=0; p=list.next(), i++ )
  {
    offset.sprintf("%04X:%04X", p->offset>>16, p->offset&0x0000FFFF );
    text = i18n("Offset: %1").arg(offset);

    menu->insertItem( text, i );
    menu->connectItem( i, editor(), SLOT(gotoBookmark(int)) );
    int key = acceleratorNumKey( i );
    if( key > 0 )
    {
      menu->setAccel( ALT+key, i );
    }

    popup->insertItem( QString("[%1] %2").arg(i+1).arg(text), i );
    popup->connectItem( i, editor(), SLOT(gotoBookmark(int)) );
  }
}


void KHexEdit::setupCaption( const QString &url )
{
  if( mShowFullPath == true )
  {
    setCaption( url, hexView()->modified() );
  }
  else
  {
    QString n = url;
    setCaption( n.right(n.length()-n.findRev('/')-1), hexView()->modified() );
  }
}


void KHexEdit::fileActive( const QString &url, bool onDisk )
{
  setupCaption( url );
  mDragLabel->setUrl( url );
  mDragLabel->setEnabled( onDisk );

  mAction.revert->setEnabled( onDisk );

  if( url.isNull() == false )
  {
    addDocument( url );
    addRecentFile( url );
    setTickedDocument( url );
  }
}


void KHexEdit::fileRename( const QString &curName, const QString &newName )
{
  if( newName.isNull() == true )
  {
    return;
  }

  renameDocument( curName, newName );
  renameRecentFile( curName, newName );
  setupCaption( newName );
  //setCaption( newName );
}


void KHexEdit::fileClosed( const QString &url )
{
  removeDocument( url );
}


void KHexEdit::editMode( CHexBuffer::EEditMode editMode )
{
  statusBar()->changeItem( editMode == CHexBuffer::EditReplace ?
			   i18n("OVR") : i18n("INS"), status_Ovr );
}


void KHexEdit::encodingChanged( const SEncodeState &encodeState )
{
  KRadioAction *radioAction;

  if( encodeState.mode == CConversion::cnvDefault )
  {
    radioAction = mAction.defaultEncoding;
  }
  else if( encodeState.mode == CConversion::cnvUsAscii )
  {
    radioAction = mAction.usAsciiEncoding;
  }
  else if( encodeState.mode == CConversion::cnvEbcdic )
  {
    radioAction = mAction.ebcdicEncoding;
  }
  else
  {
    return;
  }

  radioAction->blockSignals(true);
  radioAction->setChecked( true );
  radioAction->blockSignals(false);

  if( mSelectionSize == 0 )
  {
    statusBar()->changeItem( i18n("Encoding: %1").arg(encodeState.name),
			     status_Selection );
  }
}


void KHexEdit::textWidth( uint )
{
  resize( mManager->preferredWidth(), height() );
}


void KHexEdit::setDisplayMode( void )
{
  if( mAction.hexadecimal->isChecked() == true )
  {
    editor()->setHexadecimalMode();
  }
  else if( mAction.decimal->isChecked() == true )
  {
    editor()->setDecimalMode();
  }
  else if( mAction.octal->isChecked() == true )
  {
    editor()->setOctalMode();
  }
  else if( mAction.binary->isChecked() == true )
  {
    editor()->setBinaryMode();
  }
  else if( mAction.textOnly->isChecked() == true )
  {
    editor()->setTextMode();
  }
}


void KHexEdit::showToolBar( void )
{
  mShowToolBar = mAction.showToolBar->isChecked();
  if( mShowToolBar == true )
  {
    toolBar()->show();
  }
  else
  {
    toolBar()->hide();
  }
}


void KHexEdit::showStatusBar( void )
{
  mShowStatusBar = mAction.showStatusBar->isChecked();
  if( mShowStatusBar == true )
  {
    statusBar()->show();
  }
  else
  {
    statusBar()->hide();
  }
}


void KHexEdit::showFullPath( void )
{
  mShowFullPath = mAction.showFullPath->isChecked();
  setupCaption( hexView()->url() );
}


void KHexEdit::showDocumentTabs( void )
{
  if( mAction.tabHide->isChecked() == true )
  {
    mManager->setTabBarPosition( CHexManagerWidget::HideItem );
  }
  else if( mAction.tabShowAboveEditor->isChecked() == true )
  {
    mManager->setTabBarPosition( CHexManagerWidget::AboveEditor );
  }
  else if( mAction.tabShowBelowEditor->isChecked() == true )
  {
    mManager->setTabBarPosition( CHexManagerWidget::BelowEditor );
  }
}


void KHexEdit::showConversionField( void )
{
  if( mAction.conversionHide->isChecked() == true )
  {
    mManager->setConversionVisibility( CHexManagerWidget::Hide );
  }
  else if( mAction.conversionFloat->isChecked() == true )
  {
    mManager->setConversionVisibility( CHexManagerWidget::Float );
  }
  else if( mAction.conversionEmbed->isChecked() == true )
  {
    mManager->setConversionVisibility( CHexManagerWidget::Embed );
    if( width() < mManager->preferredWidth() )
    {
      resize( mManager->preferredWidth(), height() );
    }
  }
}


void KHexEdit::showSearchBar( void )
{
  if( mAction.searchHide->isChecked() == true )
  {
    mManager->setSearchBarPosition( CHexManagerWidget::HideItem );
  }
  else if( mAction.searchShowAboveEditor->isChecked() == true )
  {
    mManager->setSearchBarPosition( CHexManagerWidget::AboveEditor );
  }
  else if( mAction.searchShowBelowEditor->isChecked() == true )
  {
    mManager->setSearchBarPosition( CHexManagerWidget::BelowEditor );
  }
}


void KHexEdit::setEncoding( void )
{
  if( mAction.defaultEncoding->isChecked() == true )
  {
    editor()->encode( CConversion::cnvDefault );
  }
  else if( mAction.usAsciiEncoding->isChecked() == true )
  {
    editor()->encode( CConversion::cnvUsAscii );
  }
  else if( mAction.ebcdicEncoding->isChecked() == true )
  {
    editor()->encode( CConversion::cnvEbcdic );
  }
}


void KHexEdit::setUndoState( uint undoState )
{
  if( mUndoState != undoState )
  {
    mUndoState = undoState;
    mAction.undo->setEnabled( mUndoState & CHexBuffer::UndoOk );
    mAction.redo->setEnabled( mUndoState & CHexBuffer::RedoOk );
  }
}


void KHexEdit::setSelectionState( uint selectionOffset, uint selectionSize )
{
  if( mSelectionSize != selectionSize )
  {
    mSelectionOffset = selectionOffset;
    mSelectionSize   = selectionSize;

    bool valid = mSelectionSize > 0 ? true : false;
    mAction.copy->setEnabled( valid );
    mAction.cut->setEnabled( valid );
    mAction.copyAsText->setEnabled( valid );

    setSelectionText( selectionOffset, selectionSize );
  }
}


void KHexEdit::setSelectionText( uint selectionOffset, uint selectionSize )
{
  if( selectionSize > 0 )
  {
    QString selection = i18n("Selection:");
    if( mSelectionAsHexadecimal == true )
    {
      selection += QString().sprintf( " %04x:%04x %04x:%04x",
        selectionOffset>>16, selectionOffset&0x0000FFFF,
        selectionSize>>16, selectionSize&0x0000FFFF );
    }
    else
    {
      selection += QString().sprintf( " %08u %08u",
        selectionOffset, selectionSize);
    }
    statusBar()->changeItem( selection, status_Selection );
  }
  else
  {
    statusBar()->changeItem(
      i18n("Encoding: %1").arg(hexView()->encoding().name), status_Selection);
  }
}


void KHexEdit::documentMenuCB( int index )
{
  if( (uint)index < mDocumentList.count() )
  {
    editor()->open( mDocumentList[index], false, 0 );
  }
}


void KHexEdit::addDocument( const QString &fileName )
{
  uint documentCount = mDocumentList.count();
  for( uint i=0; i < documentCount; i++ )
  {
    if( fileName == mDocumentList[i] )
    {
      return;
    }
  }

  mDocumentList.append( fileName );

  QPopupMenu *documentMenu = mAction.documentsMenu->popupMenu();
  documentMenu->insertItem( fileName, documentCount, documentCount );
  int key = acceleratorNumKey( documentCount );
  if( key > 0 )
  {
    documentMenu->setAccel( CTRL+key, documentCount );
  }
}


void KHexEdit::removeDocument( const QString &fileName )
{
  QStringList::Iterator it;
  for( it = mDocumentList.begin(); it != mDocumentList.end(); it++ )
  {
    if( *it == fileName )
    {
      QPopupMenu *documentMenu = mAction.documentsMenu->popupMenu();

      documentMenu->removeItemAt( mDocumentList.findIndex(*it) );
      mDocumentList.remove( it );

      for( uint i=0; i < mDocumentList.count(); i++ )
      {
	documentMenu->setId( i, i );
	int key = acceleratorNumKey( i );
	if( key > 0 )
	{
	  documentMenu->setAccel( CTRL+key, i );
	}
      }
      return;

    }
  }
}


void KHexEdit::renameDocument( const QString &curName, const QString &newName )
{
  QStringList::Iterator it;
  for( it = mDocumentList.begin(); it != mDocumentList.end(); it++ )
  {
    if( *it == curName )
    {
      QPopupMenu *documentMenu = mAction.documentsMenu->popupMenu();
      documentMenu->changeItem( newName, mDocumentList.findIndex(*it) );
      mDocumentList.insert( it, newName );
      mDocumentList.remove( it );
      return;
    }
  }
}


void KHexEdit::setTickedDocument( const QString &fileName )
{
  uint documentCount = mDocumentList.count();
  QPopupMenu *documentMenu = mAction.documentsMenu->popupMenu();
  for( uint i=0; i < documentCount; i++ )
  {
    documentMenu->setItemChecked( i, fileName == mDocumentList[i] );
  }
}


void KHexEdit::conversionClosed( void )
{
  mAction.conversionHide->setChecked(true);
}


void KHexEdit::searchBarClosed( void )
{
  mAction.searchHide->setChecked(true);
}


void KHexEdit::showAboutApplication( void )
{
  if( mAboutKHexEdit == 0 )
  {
    mAboutKHexEdit = new CAboutKHexEdit( topLevelWidget(), 0, false );
    if( mAboutKHexEdit == 0 ) { return; }
  }

  if( mAboutKHexEdit->isVisible() == false )
  {
    mAboutKHexEdit->show();
  }
  else
  {
    mAboutKHexEdit->raise();
  }
}


bool KHexEdit::eventFilter( QObject *o, QEvent *event )
{
  if( event->type() == QEvent::DragEnter )
  {
    QDragEnterEvent *e = (QDragEnterEvent*)event;
    if( QUriDrag::canDecode(e) )
    {
      e->accept();
      hexView()->setDropHighlight( true );
    }
    return( true );
  }
  else if( event->type() == QEvent::Drop )
  {
    QMimeSource &m = *(QDropEvent*)event;
    hexView()->setDropHighlight( false );

    QStringList list, offset;
    if( QUriDrag::decodeToUnicodeUris( &m, list ) == true )
    {
      open( list, offset );
      return( true );
    }
    return( true );
  }
  else if( event->type() == QEvent::DragLeave )
  {
    hexView()->setDropHighlight( false );
    return( true );
  }

  return KMainWindow::eventFilter( o, event );
}


int KHexEdit::acceleratorNumKey( uint index )
{
  static int keys[9] = 
  {
    Key_1, Key_2, Key_3, Key_4, Key_5, Key_6, Key_7, Key_8, Key_9
  };
  return( index >= 9 ? -1 : keys[index] );
}


#include <sys/time.h>
#include <unistd.h>

//
// This function is used to test kwin performance
//
void KHexEdit::resizeTest()
{
  struct timeval t1, t2;
  gettimeofday( &t1, 0 );

  int loop=400;

  for( int i=0; i<loop; i+=4 )
  {
    resize( i+400, i+400 );
    //kapp->processOneEvent();
  }

  gettimeofday( &t2, 0 );
  uint last = (t2.tv_sec-t1.tv_sec) * 1000000 + (t2.tv_usec - t1.tv_usec);

  kdDebug() << "Duration: " << (float)last/ 1000000.0 <<  endl;
  kdDebug() << "Duration/loop: " << (float)last/ (1000000.0*(float)loop) << endl;
}
#include "toplevel.moc"
