/*******************************************************************
 KNotes -- Notes for the KDE project

 Copyright (c) 1997-2005, The KNotes Developers

 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 <tqdragobject.h>
#include <tqfont.h>

#include <kdebug.h>
#include <tdelocale.h>
#include <tdeaction.h>
#include <kurldrag.h>
#include <kstdaction.h>
#include <kcolordialog.h>
#include <tqpopupmenu.h>
#include <kiconloader.h>
#include "knoteedit.h"
#include "knote.h"

static const short SEP = 5;
static const short ICON_SIZE = 10;


KNoteEdit::KNoteEdit( TDEActionCollection *actions, TQWidget *parent, const char *name )
    : KTextEdit( parent, name ), m_note( 0 )
{
    setAcceptDrops( true );
    setWordWrap( WidgetWidth );
    setWrapPolicy( AtWhiteSpace );
    setLinkUnderline( true );
    setCheckSpellingEnabled(false);
    // create the actions for the RMB menu
    undo = KStdAction::undo( this, TQ_SLOT(undo()), actions );
    redo = KStdAction::redo( this, TQ_SLOT(redo()), actions );
    undo->setEnabled( isUndoAvailable() );
    redo->setEnabled( isRedoAvailable() );

    m_cut = KStdAction::cut( this, TQ_SLOT(cut()), actions );
    m_copy = KStdAction::copy( this, TQ_SLOT(copy()), actions );
    m_paste = KStdAction::paste( this, TQ_SLOT(paste()), actions );

    m_cut->setEnabled( false );
    m_copy->setEnabled( false );
    m_paste->setEnabled( true );

    connect( this, TQ_SIGNAL(undoAvailable(bool)), this, TQ_SLOT(setEnabledUndo(bool)) );
    connect( this, TQ_SIGNAL(redoAvailable(bool)), this, TQ_SLOT(setEnabledRedo(bool)) );

    connect( this, TQ_SIGNAL(copyAvailable(bool)), this, TQ_SLOT( slotCutEnabled( bool ) ) );
    connect( this, TQ_SIGNAL(copyAvailable(bool)), m_copy, TQ_SLOT(setEnabled(bool)) );

    new TDEAction( KStdGuiItem::clear(), 0, this, TQ_SLOT(clear()), actions, "edit_clear" );
    KStdAction::selectAll( this, TQ_SLOT(selectAll()), actions );

    // create the actions modifying the text format
    m_textBold = new TDEToggleAction( i18n("Bold"), "format-text-bold", CTRL + Key_B, 0, 0,
                                    actions, "format_bold" );
    m_textItalic = new TDEToggleAction( i18n("Italic"), "format-text-italic", CTRL + Key_I, 0, 0,
                                      actions, "format_italic" );
    m_textUnderline = new TDEToggleAction( i18n("Underline"), "format-text-underline", CTRL + Key_U, 0, 0,
                                         actions, "format_underline" );
    m_textStrikeOut = new TDEToggleAction( i18n("Strike Out"), "format-text-strikethrough", CTRL + Key_S, 0, 0,
                                         actions, "format_strikeout" );

    connect( m_textBold, TQ_SIGNAL(toggled(bool)), TQ_SLOT(setBold(bool)) );
    connect( m_textItalic, TQ_SIGNAL(toggled(bool)), TQ_SLOT(setItalic(bool)) );
    connect( m_textUnderline, TQ_SIGNAL(toggled(bool)), TQ_SLOT(setUnderline(bool)) );
    connect( m_textStrikeOut, TQ_SIGNAL(toggled(bool)), TQ_SLOT(textStrikeOut(bool)) );

    m_textAlignLeft = new TDEToggleAction( i18n("Align Left"), "format-text-direction-ltr", ALT + Key_L,
                                 this, TQ_SLOT(textAlignLeft()),
                                 actions, "format_alignleft" );
    m_textAlignLeft->setChecked( true ); // just a dummy, will be updated later
    m_textAlignCenter = new TDEToggleAction( i18n("Align Center"), "text_center", ALT + Key_C,
                                 this, TQ_SLOT(textAlignCenter()),
                                 actions, "format_aligncenter" );
    m_textAlignRight = new TDEToggleAction( i18n("Align Right"), "format-text-direction-rtl", ALT + Key_R,
                                 this, TQ_SLOT(textAlignRight()),
                                 actions, "format_alignright" );
    m_textAlignBlock = new TDEToggleAction( i18n("Align Block"), "text_block", ALT + Key_B,
                                 this, TQ_SLOT(textAlignBlock()),
                                 actions, "format_alignblock" );

    m_textAlignLeft->setExclusiveGroup( "align" );
    m_textAlignCenter->setExclusiveGroup( "align" );
    m_textAlignRight->setExclusiveGroup( "align" );
    m_textAlignBlock->setExclusiveGroup( "align" );

    m_textList = new TDEToggleAction( i18n("List"), "enum_list", 0,
                                    this, TQ_SLOT(textList()),
                                    actions, "format_list" );

    m_textList->setExclusiveGroup( "style" );

    m_textSuper = new TDEToggleAction( i18n("Superscript"), "text_super", 0,
                                     this, TQ_SLOT(textSuperScript()),
                                     actions, "format_super" );
    m_textSub = new TDEToggleAction( i18n("Subscript"), "text_sub", 0,
                                   this, TQ_SLOT(textSubScript()),
                                   actions, "format_sub" );

    m_textSuper->setExclusiveGroup( "valign" );
    m_textSub->setExclusiveGroup( "valign" );

// There is no easy possibility to implement text indenting with TQTextEdit
//
//     m_textIncreaseIndent = new TDEAction( i18n("Increase Indent"), "format_increaseindent", 0,
//                                         this, TQ_SLOT(textIncreaseIndent()),
//                                         actions, "format_increaseindent" );
//
//     m_textDecreaseIndent = new TDEAction( i18n("Decrease Indent"), "format_decreaseindent", 0,
//                                         this, TQ_SLOT(textDecreaseIndent()),
//                                         actions, "format_decreaseindent" );

    TQPixmap pix( ICON_SIZE, ICON_SIZE );
    pix.fill( black );     // just a dummy, gets updated before widget is shown
    m_textColor = new TDEAction( i18n("Text Color..."), pix, 0, this,
                                  TQ_SLOT(textColor()), actions, "format_color" );

    m_textFont = new TDEFontAction( i18n("Text Font"), "text", KKey(),
                                  actions, "format_font" );
    connect( m_textFont, TQ_SIGNAL(activated( const TQString & )),
             this, TQ_SLOT(setFamily( const TQString & )) );

    m_textSize = new TDEFontSizeAction( i18n("Text Size"), KKey(),
                                      actions, "format_size" );
    connect( m_textSize, TQ_SIGNAL(fontSizeChanged( int )),
             this, TQ_SLOT(setPointSize( int )) );

    // TQTextEdit connections
    connect( this, TQ_SIGNAL(returnPressed()), TQ_SLOT(slotReturnPressed()) );
    connect( this, TQ_SIGNAL(currentFontChanged( const TQFont & )),
             this, TQ_SLOT(fontChanged( const TQFont & )) );
    connect( this, TQ_SIGNAL(currentColorChanged( const TQColor & )),
             this, TQ_SLOT(colorChanged( const TQColor & )) );
    connect( this, TQ_SIGNAL(currentAlignmentChanged( int )),
             this, TQ_SLOT(alignmentChanged( int )) );
    connect( this, TQ_SIGNAL(currentVerticalAlignmentChanged( VerticalAlignment )),
             this, TQ_SLOT(verticalAlignmentChanged( VerticalAlignment )) );
}

KNoteEdit::~KNoteEdit()
{
}

void KNoteEdit::setEnabledRedo( bool b )
{
    redo->setEnabled( b && !isReadOnly() );
}

void KNoteEdit::setEnabledUndo( bool b )
{
    undo->setEnabled( b && !isReadOnly() );
}

void KNoteEdit::slotCutEnabled( bool b )
{
    m_cut->setEnabled( b && !isReadOnly() );
}

void KNoteEdit::setText( const TQString& text )
{
    // to update the font and font size combo box - TQTextEdit stopped
    // emitting the currentFontChanged signal with the new optimizations
    KTextEdit::setText( text );
    fontChanged( currentFont() );
}

void KNoteEdit::setTextFont( const TQFont& font )
{
    if ( textFormat() == PlainText )
        setFont( font );
    else
        setCurrentFont( font );
}

void KNoteEdit::setTextColor( const TQColor& color )
{
    setColor( color );
    colorChanged( color );
}

void KNoteEdit::setTabStop( int tabs )
{
    TQFontMetrics fm( font() );
    setTabStopWidth( fm.width( 'x' ) * tabs );
}

void KNoteEdit::setAutoIndentMode( bool newmode )
{
    m_autoIndentMode = newmode;
}


/** public slots **/

void KNoteEdit::setTextFormat( TextFormat f )
{
    if ( f == textFormat() )
        return;

    if ( f == RichText )
    {
        TQString t = text();
        KTextEdit::setTextFormat( f );

        // if the note contains html/xml source try to display it, otherwise
        // get the modified text again and set it to preserve newlines
        if ( TQStyleSheet::mightBeRichText( t ) )
            setText( t );
        else
            setText( text() );

        enableRichTextActions();
    }
    else
    {
        KTextEdit::setTextFormat( f );
        TQString t = text();
        setText( t );

        disableRichTextActions();
    }
}

void KNoteEdit::textStrikeOut( bool s )
{
    // TQTextEdit does not support stroke out text (no saving,
    // no changing of more than one selected character)
    TQFont font;

    if ( !hasSelectedText() )
    {
        font = currentFont();
        font.setStrikeOut( s );
        setCurrentFont( font );
    }
    else
    {
        int pFrom, pTo, iFrom, iTo, iF, iT;
        int cp, ci;

        getSelection( &pFrom, &iFrom, &pTo, &iTo );
        getCursorPosition( &cp, &ci );

        for ( int p = pFrom; p <= pTo; p++ )
        {
            iF = 0;
            iT = paragraphLength( p );

            if ( p == pFrom )
                iF = iFrom;

            if ( p == pTo )
                iT = iTo;

            for ( int i = iF; i < iT; i++ )
            {
                setCursorPosition( p, i + 1 );
                setSelection( p, i, p, i + 1 );
                font = currentFont();
                font.setStrikeOut( s );
                setCurrentFont( font );
            }
        }

        setSelection( pFrom, iFrom, pTo, iTo );
        setCursorPosition( cp, ci );
    }
}

void KNoteEdit::textColor()
{
    if ( m_note )
        m_note->blockEmitDataChanged( true );
    TQColor c = color();
    int ret = KColorDialog::getColor( c, this );
    if ( ret == TQDialog::Accepted )
        setTextColor( c );
    if ( m_note )
        m_note->blockEmitDataChanged( false );
}

void KNoteEdit::textAlignLeft()
{
    setAlignment( AlignLeft );
    m_textAlignLeft->setChecked( true );
}

void KNoteEdit::textAlignCenter()
{
    setAlignment( AlignCenter );
    m_textAlignCenter->setChecked( true );
}

void KNoteEdit::textAlignRight()
{
    setAlignment( AlignRight );
    m_textAlignRight->setChecked( true );
}

void KNoteEdit::textAlignBlock()
{
    setAlignment( AlignJustify );
    m_textAlignBlock->setChecked( true );
}

void KNoteEdit::textList()
{
    if ( m_textList->isChecked() )
        setParagType( TQStyleSheetItem::DisplayListItem, TQStyleSheetItem::ListDisc );
    else
        setParagType( TQStyleSheetItem::DisplayBlock, TQStyleSheetItem::ListDisc );
}

void KNoteEdit::textSuperScript()
{
    if ( m_textSuper->isChecked() )
        setVerticalAlignment( AlignSuperScript );
    else
        setVerticalAlignment( AlignNormal );
}

void KNoteEdit::textSubScript()
{
    if ( m_textSub->isChecked() )
        setVerticalAlignment( AlignSubScript );
    else
        setVerticalAlignment( AlignNormal );
}

//void KNoteEdit::textIncreaseIndent()
//{
//}

//void KNoteEdit::textDecreaseIndent()
//{
//}


/** protected methods **/

void KNoteEdit::contentsDragEnterEvent( TQDragEnterEvent *e )
{
    if ( KURLDrag::canDecode( e ) )
        e->accept();
    else
        KTextEdit::contentsDragEnterEvent( e );
}

void KNoteEdit::contentsDropEvent( TQDropEvent *e )
{
    KURL::List list;

    if ( KURLDrag::decode( e, list ) )
    {
	KURL::List::ConstIterator begin = list.constBegin();
	KURL::List::ConstIterator end = list.constEnd();
        for ( KURL::List::ConstIterator it = begin; it != end; ++it )
        {
            if ( it != begin )
                insert( ", " );

            insert( (*it).prettyURL() );
        }
    }
    else
        KTextEdit::contentsDropEvent( e );
}

/** private slots **/

void KNoteEdit::slotReturnPressed()
{
    if ( m_autoIndentMode )
        autoIndent();
}

void KNoteEdit::fontChanged( const TQFont &f )
{
    m_textFont->setFont( f.family() );
    m_textSize->setFontSize( f.pointSize() );

    m_textBold->setChecked( f.bold() );
    m_textItalic->setChecked( f.italic() );
    m_textUnderline->setChecked( f.underline() );
    m_textStrikeOut->setChecked( f.strikeOut() );
}

void KNoteEdit::colorChanged( const TQColor &c )
{
    TQPixmap pix( ICON_SIZE, ICON_SIZE );
    pix.fill( c );
    m_textColor->setIconSet( pix );
}

void KNoteEdit::alignmentChanged( int a )
{
    // TODO: AlignAuto
    if ( ( a == AlignAuto ) || ( a & AlignLeft ) )
        m_textAlignLeft->setChecked( true );
    else if ( ( a & AlignHCenter ) )
        m_textAlignCenter->setChecked( true );
    else if ( ( a & AlignRight ) )
        m_textAlignRight->setChecked( true );
    else if ( ( a & AlignJustify ) )
        m_textAlignBlock->setChecked( true );
}

void KNoteEdit::verticalAlignmentChanged( VerticalAlignment a )
{
    if ( a == AlignNormal )
    {
        m_textSuper->setChecked( false );
        m_textSub->setChecked( false );
    }
    else if ( a == AlignSuperScript )
        m_textSuper->setChecked( true );
    else if ( a == AlignSubScript )
        m_textSub->setChecked( true );
}


/** private methods **/

void KNoteEdit::autoIndent()
{
    int para, index;
    TQString string;
    getCursorPosition( &para, &index );
    while ( para > 0 && string.stripWhiteSpace().isEmpty() )
        string = text( --para );

    if ( string.stripWhiteSpace().isEmpty() )
        return;

    // This routine returns the whitespace before the first non white space
    // character in string.
    // It is assumed that string contains at least one non whitespace character
    // ie \n \r \t \v \f and space
    TQString indentString;

    int len = string.length();
    int i = 0;
    while ( i < len && string.at(i).isSpace() )
        indentString += string.at( i++ );

    if ( !indentString.isEmpty() )
        insert( indentString );
}

void KNoteEdit::emitLinkClicked( const TQString &s )
{
    kdDebug(5500) << k_funcinfo << s << endl;
}

void KNoteEdit::enableRichTextActions()
{
    m_textColor->setEnabled( true );
    m_textFont->setEnabled( true );
    m_textSize->setEnabled( true );

    m_textBold->setEnabled( true );
    m_textItalic->setEnabled( true );
    m_textUnderline->setEnabled( true );
    m_textStrikeOut->setEnabled( true );

    m_textAlignLeft->setEnabled( true );
    m_textAlignCenter->setEnabled( true );
    m_textAlignRight->setEnabled( true );
    m_textAlignBlock->setEnabled( true );

    m_textList->setEnabled( true );
    m_textSuper->setEnabled( true );
    m_textSub->setEnabled( true );

//    m_textIncreaseIndent->setEnabled( true );
//    m_textDecreaseIndent->setEnabled( true );
}

void KNoteEdit::disableRichTextActions()
{
    m_textColor->setEnabled( false );
    m_textFont->setEnabled( false );
    m_textSize->setEnabled( false );

    m_textBold->setEnabled( false );
    m_textItalic->setEnabled( false );
    m_textUnderline->setEnabled( false );
    m_textStrikeOut->setEnabled( false );

    m_textAlignLeft->setEnabled( false );
    m_textAlignCenter->setEnabled( false );
    m_textAlignRight->setEnabled( false );
    m_textAlignBlock->setEnabled( false );

    m_textList->setEnabled( false );
    m_textSuper->setEnabled( false );
    m_textSub->setEnabled( false );

//    m_textIncreaseIndent->setEnabled( false );
//    m_textDecreaseIndent->setEnabled( false );
}

void KNoteEdit::slotAllowTab()
{
    setTabChangesFocus(!tabChangesFocus());
}

TQPopupMenu *KNoteEdit::createPopupMenu( const TQPoint &pos )
{
    enum { IdUndo, IdRedo, IdSep1, IdCut, IdCopy, IdPaste, IdClear, IdSep2, IdSelectAll };

    TQPopupMenu *menu = TQTextEdit::createPopupMenu( pos );

    if ( isReadOnly() )
      menu->changeItem( menu->idAt(0), SmallIconSet("edit-copy"), menu->text( menu->idAt(0) ) );
    else {
      int id = menu->idAt(0);
      menu->changeItem( id - IdUndo, SmallIconSet("edit-undo"), menu->text( id - IdUndo) );
      menu->changeItem( id - IdRedo, SmallIconSet("edit-redo"), menu->text( id - IdRedo) );
      menu->changeItem( id - IdCut, SmallIconSet("edit-cut"), menu->text( id - IdCut) );
      menu->changeItem( id - IdCopy, SmallIconSet("edit-copy"), menu->text( id - IdCopy) );
      menu->changeItem( id - IdPaste, SmallIconSet("edit-paste"), menu->text( id - IdPaste) );
      menu->changeItem( id - IdClear, SmallIconSet("edit-clear"), menu->text( id - IdClear) );

        menu->insertSeparator();
        id = menu->insertItem( SmallIconSet( "tools-check-spelling" ), i18n( "Check Spelling..." ),
                                   this, TQ_SLOT( checkSpelling() ) );

        if( text().isEmpty() )
            menu->setItemEnabled( id, false );

	menu->insertSeparator();
	id=menu->insertItem(i18n("Allow Tabulations"),this,TQ_SLOT(slotAllowTab()));
	menu->setItemChecked(id, !tabChangesFocus());
    }

    return menu;
}

#include "knoteedit.moc"
