/**********************************************************************
** Copyright (C) 2000-2008 Trolltech ASA.  All rights reserved.
**
** This file is part of TQt Designer.
**
** This file may be used under the terms of the GNU General
** Public License versions 2.0 or 3.0 as published by the Free
** Software Foundation and appearing in the files LICENSE.GPL2
** and LICENSE.GPL3 included in the packaging of this file.
** Alternatively you may (at your option) use any later version
** of the GNU General Public License if such license has been
** publicly approved by Trolltech ASA (or its successors, if any)
** and the KDE Free TQt Foundation.
**
** Please review the following information to ensure GNU General
** Public Licensing requirements will be met:
** http://trolltech.com/products/qt/licenses/licensing/opensource/.
** If you are unsure which license is appropriate for your use, please
** review the following information:
** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
** or contact the sales department at sales@trolltech.com.
**
** Licensees holding valid TQt Commercial licenses may use this file in
** accordance with the TQt Commercial License Agreement provided with
** the Software.
**
** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
** herein.
**
**********************************************************************/

#include <ntqvariant.h>  // HP-UX compiler needs this here
#include "workspace.h"
#include "formwindow.h"
#include "mainwindow.h"
#include "globaldefs.h"
#include "command.h"
#include "project.h"
#include "pixmapcollection.h"
#include "sourcefile.h"
#include "sourceeditor.h"
#include "propertyeditor.h"

#include <ntqheader.h>
#include <ntqdragobject.h>
#include <ntqfileinfo.h>
#include <ntqapplication.h>
#include <ntqpainter.h>
#include <ntqpen.h>
#include <ntqobjectlist.h>
#include <ntqworkspace.h>
#include <ntqpopupmenu.h>
#include <ntqtextstream.h>
#include "qcompletionedit.h"

WorkspaceItem::WorkspaceItem( TQListView *parent, Project* p )
    : TQListViewItem( parent )
{
    init();
    project = p;
    t = ProjectType;
    setPixmap( 0, TQPixmap::fromMimeSource( "designer_folder.png" ) );
    setExpandable( FALSE );
}

WorkspaceItem::WorkspaceItem( TQListViewItem *parent, SourceFile* sf )
    : TQListViewItem( parent )
{
    init();
    sourceFile = sf;
    t = SourceFileType;
    setPixmap( 0, TQPixmap::fromMimeSource( "designer_filenew.png" ) );
}

WorkspaceItem::WorkspaceItem( TQListViewItem *parent, TQObject *o, Project *p )
    : TQListViewItem( parent )
{
    init();
    object = o;
    project = p;
    t = ObjectType;
    setPixmap( 0, TQPixmap::fromMimeSource( "designer_object.png" ) );
    TQObject::connect( p->fakeFormFileFor( o ), TQ_SIGNAL( somethingChanged(FormFile*) ),
		      listView(), TQ_SLOT( update() ) );
}

WorkspaceItem::WorkspaceItem( TQListViewItem *parent, FormFile* ff, Type type )
    : TQListViewItem( parent )
{
    init();
    formFile = ff;
    t = type;
    if ( type ==  FormFileType ) {
	setPixmap( 0, TQPixmap::fromMimeSource( "designer_form.png" ) );
	TQObject::connect( ff, TQ_SIGNAL( somethingChanged(FormFile*) ), listView(), TQ_SLOT( update(FormFile*) ) );
	if ( formFile->supportsCodeFile() ) {
	    (void) new WorkspaceItem( this, formFile, FormSourceType );
	}
    } else if ( type == FormSourceType ) {
	setPixmap( 0, TQPixmap::fromMimeSource( "designer_filenew.png" ) );
    }
}


void WorkspaceItem::init()
{
    autoOpen = FALSE;
    useOddColor = FALSE;
    project = 0;
    sourceFile = 0;
    formFile = 0;
}

void WorkspaceItem::paintCell( TQPainter *p, const TQColorGroup &cg, int column, int width, int align )
{
    TQColorGroup g( cg );
    g.setColor( TQColorGroup::Base, backgroundColor() );
    g.setColor( TQColorGroup::Foreground, TQt::black );

    if ( type() == FormSourceType &&
	 ( !formFile->hasFormCode() || ( formFile->codeFileState() == FormFile::Deleted && formFile->formWindow() ) ) &&
	 parent() && parent()->parent() && ( (WorkspaceItem*)parent()->parent() )->project &&
	 ( (WorkspaceItem*)parent()->parent() )->project->isCpp() ) {
	g.setColor( TQColorGroup::Text, listView()->palette().disabled().color( TQColorGroup::Text) );
	g.setColor( TQColorGroup::HighlightedText, listView()->palette().disabled().color( TQColorGroup::Text) );
    } else {
	g.setColor( TQColorGroup::Text, TQt::black );
    }
    p->save();

    if ( isModified() ) {
	TQFont f = p->font();
	f.setBold( TRUE );
	p->setFont( f );
    }

    TQListViewItem::paintCell( p, g, column, width, align );
    p->setPen( TQPen( cg.dark(), 1 ) );
    if ( column == 0 )
	p->drawLine( 0, 0, 0, height() - 1 );
    if ( listView()->firstChild() != this ) {
	if ( nextSibling() != itemBelow() && itemBelow()->depth() < depth() ) {
	    int d = depth() - itemBelow()->depth();
	    p->drawLine( -listView()->treeStepSize() * d, height() - 1, 0, height() - 1 );
	}
    }
    p->drawLine( 0, height() - 1, width, height() - 1 );
    p->drawLine( width - 1, 0, width - 1, height() );
    p->restore();
}

TQString WorkspaceItem::text( int column ) const
{
    if ( column != 0 )
	return TQListViewItem::text( column );
    switch( t ) {
    case ProjectType:
	if ( project->isDummy() ) {
	    return Project::tr("<No Project>" );
	} else if ( MainWindow::self->singleProjectMode() ) {
	    return TQFileInfo( project->fileName() ).baseName();
	}
	return project->makeRelative( project->fileName() );
    case FormFileType:
	if ( !MainWindow::self->singleProjectMode() )
	    return formFile->formName() + ": " + formFile->fileName();
	return formFile->formName();
    case FormSourceType:
	if ( !MainWindow::self->singleProjectMode() )
	    return formFile->codeFile();
	return formFile->formName() + " [Source]";
    case SourceFileType:
	return sourceFile->fileName();
    case ObjectType:
	if ( !project->hasParentObject( object ) )
	    return object->name();
	return project->qualifiedName( object );
    }

    return TQString::null; // shut up compiler
}

void WorkspaceItem::fillCompletionList( TQStringList& completion )
{
    switch( t ) {
    case ProjectType:
	break;
    case FormFileType:
	completion += formFile->formName();
	completion += formFile->fileName();
	break;
    case FormSourceType:
	completion += formFile->codeFile();
	break;
    case SourceFileType:
	completion += sourceFile->fileName();
	break;
    case ObjectType:
	completion += object->name();
    }
}

bool WorkspaceItem::checkCompletion( const TQString& completion )
{
    switch( t ) {
    case ProjectType:
	break;
    case FormFileType:
	return  completion == formFile->formName()
		 || completion == formFile->fileName();
    case FormSourceType:
	return completion == formFile->codeFile();
    case SourceFileType:
	return completion == sourceFile->fileName();
    case ObjectType:
	return completion == object->name();
    }
    return FALSE;
}


bool WorkspaceItem::isModified() const
{
    switch( t ) {
    case ProjectType:
	return project->isModified();
    case FormFileType:
	return formFile->isModified( FormFile::WFormWindow );
    case FormSourceType:
	return formFile->isModified( FormFile::WFormCode );
    case SourceFileType:
	return sourceFile->isModified();
    case ObjectType:
	return project->fakeFormFileFor( object )->isModified();
	break;
    }
    return FALSE; // shut up compiler
}

TQString WorkspaceItem::key( int column, bool ) const
{
    TQString key = text( column );
    if ( t == FormFileType )
	key.prepend( "0" );
    else if ( t == ObjectType )
	key.prepend( "a" );
    else
	key.prepend( "A" );
    return key;
}

TQColor WorkspaceItem::backgroundColor()
{
    bool b = useOddColor;
    if ( t == FormSourceType && parent() )
	b = ( ( WorkspaceItem*)parent() )->useOddColor;
    return b ? *backColor2 : *backColor1;
}


void WorkspaceItem::setOpen( bool b )
{
    TQListViewItem::setOpen( b );
    autoOpen = FALSE;
}

void WorkspaceItem::setAutoOpen( bool b )
{
    TQListViewItem::setOpen( b );
    autoOpen = b;
}

Workspace::Workspace( TQWidget *parent, MainWindow *mw )
    : TQListView( parent, 0, WStyle_Customize | WStyle_NormalBorder | WStyle_Title |
		 WStyle_Tool | WStyle_MinMax | WStyle_SysMenu ), mainWindow( mw ),
	project( 0 ), completionDirty( FALSE )
{
    init_colors();

    setDefaultRenameAction( Accept );
    blockNewForms = FALSE;
    bufferEdit = 0;
    header()->setStretchEnabled( TRUE );
    header()->hide();
    setSorting( 0 );
    setResizePolicy( TQScrollView::Manual );
#ifndef TQ_WS_MAC
    TQPalette p( palette() );
    p.setColor( TQColorGroup::Base, TQColor( *backColor2 ) );
    (void)*selectedBack; // hack
    setPalette( p );
#endif
    addColumn( tr( "Files" ) );
    setAllColumnsShowFocus( TRUE );
    connect( this, TQ_SIGNAL( mouseButtonClicked( int, TQListViewItem *, const TQPoint &, int ) ),
	     this, TQ_SLOT( itemClicked( int, TQListViewItem *, const TQPoint& ) ) ),
    connect( this, TQ_SIGNAL( doubleClicked( TQListViewItem * ) ),
	     this, TQ_SLOT( itemDoubleClicked( TQListViewItem * ) ) ),
    connect( this, TQ_SIGNAL( contextMenuRequested( TQListViewItem *, const TQPoint &, int ) ),
	     this, TQ_SLOT( rmbClicked( TQListViewItem *, const TQPoint& ) ) ),
    setHScrollBarMode( AlwaysOff );
    setVScrollBarMode( AlwaysOn );
    viewport()->setAcceptDrops( TRUE );
    setAcceptDrops( TRUE );
    setColumnWidthMode( 1, Manual );
}


void Workspace::projectDestroyed( TQObject* o )
{
    if ( o == project ) {
	project = 0;
	clear();
    }
}

void Workspace::setCurrentProject( Project *pro )
{
    if ( project == pro )
	return;
    if ( project ) {
	disconnect( project, TQ_SIGNAL( sourceFileAdded(SourceFile*) ), this, TQ_SLOT( sourceFileAdded(SourceFile*) ) );
	disconnect( project, TQ_SIGNAL( sourceFileRemoved(SourceFile*) ), this, TQ_SLOT( sourceFileRemoved(SourceFile*) ) );
	disconnect( project, TQ_SIGNAL( formFileAdded(FormFile*) ), this, TQ_SLOT( formFileAdded(FormFile*) ) );
	disconnect( project, TQ_SIGNAL( formFileRemoved(FormFile*) ), this, TQ_SLOT( formFileRemoved(FormFile*) ) );
	disconnect( project, TQ_SIGNAL( objectAdded(TQObject*) ), this, TQ_SLOT( objectAdded(TQObject*) ) );
	disconnect( project, TQ_SIGNAL( objectRemoved(TQObject*) ), this, TQ_SLOT( objectRemoved(TQObject*) ) );
	disconnect( project, TQ_SIGNAL( projectModified() ), this, TQ_SLOT( update() ) );
    }
    project = pro;
    connect( project, TQ_SIGNAL( sourceFileAdded(SourceFile*) ), this, TQ_SLOT( sourceFileAdded(SourceFile*) ) );
    connect( project, TQ_SIGNAL( sourceFileRemoved(SourceFile*) ), this, TQ_SLOT( sourceFileRemoved(SourceFile*) ) );
    connect( project, TQ_SIGNAL( formFileAdded(FormFile*) ), this, TQ_SLOT( formFileAdded(FormFile*) ) );
    connect( project, TQ_SIGNAL( formFileRemoved(FormFile*) ), this, TQ_SLOT( formFileRemoved(FormFile*) ) );
    connect( project, TQ_SIGNAL( destroyed(TQObject*) ), this, TQ_SLOT( projectDestroyed(TQObject*) ) );
    connect( project, TQ_SIGNAL( objectAdded(TQObject*) ), this, TQ_SLOT( objectAdded(TQObject*) ) );
    connect( project, TQ_SIGNAL( objectRemoved(TQObject*) ), this, TQ_SLOT( objectRemoved(TQObject*) ) );
    connect( project, TQ_SIGNAL( projectModified() ), this, TQ_SLOT( update() ) );
    clear();

    if ( bufferEdit )
	bufferEdit->clear();

    projectItem = new WorkspaceItem( this, project );

    projectItem->setOpen( TRUE );

    for ( TQPtrListIterator<SourceFile> sources = project->sourceFiles();
	  sources.current(); ++sources ) {
	SourceFile* f = sources.current();
	(void) new WorkspaceItem( projectItem, f );
    }

    for ( TQPtrListIterator<FormFile> forms = project->formFiles();
	  forms.current(); ++forms ) {
	FormFile* f = forms.current();
	if ( f->isFake() )
	    continue;

	(void) new WorkspaceItem( projectItem, f );
    }

    TQObjectList l = project->objects();
    TQObjectListIt objs( l );
    for ( ;objs.current(); ++objs ) {
	TQObject* o = objs.current();
	(void) new WorkspaceItem( projectItem, o, project );
    }

    updateColors();
    completionDirty = TRUE;
}

void Workspace::sourceFileAdded( SourceFile* sf )
{
    (void) new WorkspaceItem( projectItem, sf );
    updateColors();
}

void Workspace::sourceFileRemoved( SourceFile* sf )
{
    delete findItem( sf );
    updateColors();
}

void Workspace::formFileAdded( FormFile* ff )
{
    if ( ff->isFake() )
	return;
    (void) new WorkspaceItem( projectItem, ff );
    updateColors();
}

void Workspace::formFileRemoved( FormFile* ff )
{
    delete findItem( ff );
    updateColors();
}

void Workspace::objectAdded( TQObject *o )
{
    (void) new WorkspaceItem( projectItem, o, project );
    updateColors();
}

void Workspace::objectRemoved( TQObject *o )
{
    delete findItem( o );
    updateColors();
}

void Workspace::update()
{
    completionDirty = TRUE;
    triggerUpdate();
}

void Workspace::update( FormFile* ff )
{
    TQListViewItem* i = findItem( ff );
    if ( i ) {
	i->repaint();
	if ( (i = i->firstChild()) )
	    i->repaint();
    }
}


void Workspace::activeFormChanged( FormWindow *fw )
{
    WorkspaceItem *i = findItem( fw->formFile() );
    if ( i ) {
	setCurrentItem( i );
	setSelected( i, TRUE );
	if ( !i->isOpen() )
	    i->setAutoOpen( TRUE );
    }

    closeAutoOpenItems();

}

void Workspace::activeEditorChanged( SourceEditor *se )
{
    if ( !se->object() )
	return;

    if ( se->formWindow() ) {
	WorkspaceItem *i = findItem( se->formWindow()->formFile() );
	if ( i && i->firstChild() ) {
	    if ( !i->isOpen() )
		i->setAutoOpen( TRUE );
	    setCurrentItem( i->firstChild() );
	    setSelected( i->firstChild(), TRUE );
	}
    } else {
	WorkspaceItem *i = findItem( se->sourceFile() );
	if ( i ) {
	    setCurrentItem( i );
	    setSelected( i, TRUE );
	}
    }

    closeAutoOpenItems();
}

WorkspaceItem *Workspace::findItem( FormFile* ff)
{
    TQListViewItemIterator it( this );
    for ( ; it.current(); ++it ) {
	if ( ( (WorkspaceItem*)it.current() )->formFile == ff )
	    return (WorkspaceItem*)it.current();
    }
    return 0;
}

WorkspaceItem *Workspace::findItem( SourceFile *sf )
{
    TQListViewItemIterator it( this );
    for ( ; it.current(); ++it ) {
	if ( ( (WorkspaceItem*)it.current() )->sourceFile == sf )
	    return (WorkspaceItem*)it.current();
    }
    return 0;
}

WorkspaceItem *Workspace::findItem( TQObject *o )
{
    TQListViewItemIterator it( this );
    for ( ; it.current(); ++it ) {
	if ( ( (WorkspaceItem*)it.current() )->object == o )
	    return (WorkspaceItem*)it.current();
    }
    return 0;
}

void Workspace::closeAutoOpenItems()
{
    TQListViewItemIterator it( this );
    for ( ; it.current(); ++it ) {
	WorkspaceItem* i = (WorkspaceItem*) it.current();
	WorkspaceItem* ip = (WorkspaceItem*) i->parent();
	if ( i->type() == WorkspaceItem::FormSourceType ) {
	    if ( !i->isSelected() && !ip->isSelected()
		 && ip->isAutoOpen() ) {
		ip->setAutoOpen( FALSE );
	    }
	}
    }
}


void Workspace::closeEvent( TQCloseEvent *e )
{
    e->accept();
}

void Workspace::itemDoubleClicked( TQListViewItem *i )
{
    if ( ( (WorkspaceItem*)i)->type()== WorkspaceItem::ProjectType )
	i->setOpen( TRUE );
}

void Workspace::itemClicked( int button, TQListViewItem *i, const TQPoint& )
{
    if ( !i || button != LeftButton )
	return;

    closeAutoOpenItems();

    WorkspaceItem* wi = (WorkspaceItem*)i;
    switch( wi->type() ) {
    case WorkspaceItem::ProjectType:
	break; // ### TODO
    case WorkspaceItem::FormFileType:
	wi->formFile->showFormWindow();
	break;
    case WorkspaceItem::FormSourceType:
	wi->formFile->showEditor( FALSE );
	break;
    case WorkspaceItem::SourceFileType:
	mainWindow->editSource( wi->sourceFile );
	break;
    case WorkspaceItem::ObjectType:
	project->fakeFormFileFor( wi->object )->formWindow()->setFocus();
	mainWindow->propertyeditor()->setWidget( wi->object,
				 project->fakeFormFileFor( wi->object )->formWindow() );
	mainWindow->objectHierarchy()->
	    setFormWindow( project->fakeFormFileFor( wi->object )->formWindow(), wi->object );
	project->fakeFormFileFor( wi->object )->showEditor();
	break;
    }
}

void Workspace::contentsDropEvent( TQDropEvent *e )
{
    if ( !TQUriDrag::canDecode( e ) ) {
	e->ignore();
    } else {
	TQStringList files;
	TQUriDrag::decodeLocalFiles( e, files );
	if ( !files.isEmpty() ) {
	    for ( TQStringList::Iterator it = files.begin(); it != files.end(); ++it ) {
		TQString fn = *it;
		mainWindow->fileOpen( "", "", fn );
	    }
	}
    }
}

void Workspace::contentsDragEnterEvent( TQDragEnterEvent *e )
{
    if ( !TQUriDrag::canDecode( e ) )
	e->ignore();
    else
	e->accept();
}

void Workspace::contentsDragMoveEvent( TQDragMoveEvent *e )
{
    if ( !TQUriDrag::canDecode( e ) )
	e->ignore();
    else
	e->accept();
}

void Workspace::rmbClicked( TQListViewItem *i, const TQPoint& pos )
{
    if ( !i )
	return;
    WorkspaceItem* wi = (WorkspaceItem*)i;
    enum { OPEN_SOURCE, REMOVE_SOURCE, OPEN_FORM, REMOVE_FORM,
	   OPEN_FORM_SOURCE, REMOVE_FORM_SOURCE, OPEN_OBJECT_SOURCE };
    TQPopupMenu menu( this );
    menu.setCheckable( TRUE );
    switch ( wi->type() ) {
    case WorkspaceItem::SourceFileType:
	menu.insertItem( tr( "&Open source file" ), OPEN_SOURCE );
	menu.insertSeparator();
	menu.insertItem( TQPixmap::fromMimeSource( "designer_editcut.png" ),
			 tr( "&Remove source file from project" ), REMOVE_SOURCE );
	break;
    case WorkspaceItem::FormFileType:
	menu.insertItem( tr( "&Open form" ), OPEN_FORM );
	menu.insertSeparator();
	menu.insertItem( TQPixmap::fromMimeSource( "designer_editcut.png" ),
			 tr( "&Remove form from project" ), REMOVE_FORM );
	break;
    case WorkspaceItem::FormSourceType:
	menu.insertItem( tr( "&Open form source" ), OPEN_FORM_SOURCE );
	menu.insertSeparator();
	if ( project->isCpp() )
	    menu.insertItem( TQPixmap::fromMimeSource( "designer_editcut.png" ),
			     tr( "&Remove source file from form" ), REMOVE_FORM_SOURCE );
	else
	    menu.insertItem( TQPixmap::fromMimeSource( "designer_editcut.png" ),
	                     tr( "&Remove form from project" ), REMOVE_FORM );
	break;
    case WorkspaceItem::ProjectType:
	MainWindow::self->popupProjectMenu( pos );
	return;
    case WorkspaceItem::ObjectType:
	menu.insertItem( tr( "&Open source" ), OPEN_OBJECT_SOURCE );
	break;
    }

    switch ( menu.exec( pos ) ) {
    case REMOVE_SOURCE:
	project->removeSourceFile( wi->sourceFile );
	break;
    case REMOVE_FORM:
	project->removeFormFile( wi->formFile );
	break;
    case REMOVE_FORM_SOURCE:
	( (WorkspaceItem*)i )->formFile->setModified( TRUE );
	( (WorkspaceItem*)i )->formFile->setCodeFileState( FormFile::Deleted );
	delete ( (WorkspaceItem*)i )->formFile->editor();
	break;
    case OPEN_OBJECT_SOURCE:
    case OPEN_SOURCE:
    case OPEN_FORM:
    case OPEN_FORM_SOURCE:
	itemClicked( LeftButton, i, pos );
	break;
    }
}

bool Workspace::eventFilter( TQObject *o, TQEvent * e )
{
    // Reggie, on what type of events do we have to execute updateBufferEdit()
    if ( o ==bufferEdit && e->type() != TQEvent::ChildRemoved )
	updateBufferEdit();
    return TQListView::eventFilter( o, e );
}

void Workspace::setBufferEdit( TQCompletionEdit *edit )
{
    bufferEdit = edit;
    connect( bufferEdit, TQ_SIGNAL( chosen( const TQString & ) ),
	     this, TQ_SLOT( bufferChosen( const TQString & ) ) );
    bufferEdit->installEventFilter( this );
}

void Workspace::updateBufferEdit()
{
    if ( !bufferEdit || !completionDirty || !MainWindow::self)
	return;
    completionDirty = FALSE;
    TQStringList completion = MainWindow::self->projectFileNames();
    TQListViewItemIterator it( this );
    while ( it.current() ) {
	( (WorkspaceItem*)it.current())->fillCompletionList( completion );
	++it;
    }
    completion.sort();
    bufferEdit->setCompletionList( completion );
}

void Workspace::bufferChosen( const TQString &buffer )
{
    if ( bufferEdit )
	bufferEdit->setText( "" );

    if ( MainWindow::self->projectFileNames().contains( buffer ) ) {
	MainWindow::self->setCurrentProjectByFilename( buffer );
	return;
    }

    TQListViewItemIterator it( this );
    while ( it.current() ) {
	if ( ( (WorkspaceItem*)it.current())->checkCompletion( buffer ) ) {
	    itemClicked( LeftButton, it.current(), TQPoint() );
	    break;
	}
	++it;
    }
}

void Workspace::updateColors()
{
    TQListViewItem* i = firstChild();
    if ( i )
	i = i->firstChild();
    bool b = TRUE;
    while ( i ) {
	WorkspaceItem* wi = ( WorkspaceItem*) i;
	i = i->nextSibling();
	wi->useOddColor = b;
	b = !b;
    }
}
