//----------------------------------------------------------------------------
//    filename             : kmdichildview.cpp
//----------------------------------------------------------------------------
//    Project              : KDE MDI extension
//
//    begin                : 07/1999       by Szymon Stefanek as part of kvirc
//                                         (an IRC application)
//    changes              : 09/1999       by Falk Brettschneider to create a
//                           -06/2000      stand-alone Qt extension set of
//                                         classes and a Qt-based library
//                           2000-2003     maintained by the KDevelop project
//    patches              : 02/2000       by Massimo Morin (mmorin@schedsys.com)
//                           */2000        by Lars Beikirch (Lars.Beikirch@gmx.net)
//                           02/2001       by Eva Brucherseifer (eva@rt.e-technik.tu-darmstadt.de)
//                           01/2003       by Jens Zurheide (jens.zurheide@gmx.de)
//
//    copyright            : (C) 1999-2003 by Szymon Stefanek (stefanek@tin.it)
//                                         and
//                                         Falk Brettschneider
//    email                :  falkbr@kdevelop.org (Falk Brettschneider)
//----------------------------------------------------------------------------
//
//----------------------------------------------------------------------------
//
//    This program is free software; you can redistribute it and/or modify
//    it under the terms of the GNU Library General Public License as
//    published by the Free Software Foundation; either version 2 of the
//    License, or (at your option) any later version.
//
//----------------------------------------------------------------------------

#include "kmdichildview.h"
#include "kmdichildview.moc"

#include <qdatetime.h>
#include <qobjectlist.h>

#include "kmdimainfrm.h"
#include "kmdichildfrm.h"
#include "kmdidefines.h"
#include <kdebug.h>
#include <klocale.h>
#include <qiconset.h>

//============ KMdiChildView ============//

KMdiChildView::KMdiChildView( const QString& caption, QWidget* parentWidget, const char* name, WFlags f )
	: QWidget( parentWidget, name, f )
	, m_focusedChildWidget( 0L )
	, m_firstFocusableChildWidget( 0L )
	, m_lastFocusableChildWidget( 0L )
	, m_stateChanged( true )
	, m_bToolView( false )
	, m_bInterruptActivation( false )
	, m_bMainframesActivateViewIsPending( false )
	, m_bFocusInEventIsPending( false )
	, m_trackChanges( 0 )
{
	setGeometry( 0, 0, 0, 0 );  // reset
	if ( caption != 0L )
		m_szCaption = caption;
	else
		m_szCaption = i18n( "Unnamed" );
	
	m_sTabCaption = m_szCaption;
	setFocusPolicy( ClickFocus );
	installEventFilter( this );
	
	// store the current time
	updateTimeStamp();
}


//============ KMdiChildView ============//

KMdiChildView::KMdiChildView( QWidget* parentWidget, const char* name, WFlags f )
	: QWidget( parentWidget, name, f )
	, m_focusedChildWidget( 0L )
	, m_firstFocusableChildWidget( 0L )
	, m_lastFocusableChildWidget( 0L )
	, m_stateChanged( true )
	, m_bToolView( false )
	, m_bInterruptActivation( false )
	, m_bMainframesActivateViewIsPending( false )
	, m_bFocusInEventIsPending( false )
{
	setGeometry( 0, 0, 0, 0 );  // reset
	m_szCaption = i18n( "Unnamed" );
	m_sTabCaption = m_szCaption;
	setFocusPolicy( ClickFocus );
	installEventFilter( this );

	// store the current time
	updateTimeStamp();
}

//============ ~KMdiChildView ============//

KMdiChildView::~KMdiChildView()
{
	kdDebug( 760 ) << k_funcinfo << endl;
}

void KMdiChildView::trackIconAndCaptionChanges( QWidget *view )
{
	m_trackChanges = view;
}


//============== internal geometry ==============//

QRect KMdiChildView::internalGeometry() const
{
	if ( mdiParent() )
	{ // is attached
		// get the client area coordinates inside the MDI child frame
		QRect posInFrame = geometry();
		// map these values to the parent of the MDI child frame
		// (this usually is the MDI child area) and return
		QPoint ptTopLeft = mdiParent() ->mapToParent( posInFrame.topLeft() );
		QSize sz = size();
		return QRect( ptTopLeft, sz );
	}
	else
	{
		QRect geo = geometry();
		QRect frameGeo = externalGeometry();
		return QRect( frameGeo.x(), frameGeo.y(), geo.width(), geo.height() );
		//      return geometry();
	}
}

//============== set internal geometry ==============//

void KMdiChildView::setInternalGeometry( const QRect& newGeometry )
{
	if ( mdiParent() )
	{ // is attached
		// retrieve the frame size
		QRect geo = internalGeometry();
		QRect frameGeo = externalGeometry();
		int nFrameSizeTop = geo.y() - frameGeo.y();
		int nFrameSizeLeft = geo.x() - frameGeo.x();

		// create the new geometry that is accepted by the QWidget::setGeometry() method
		QRect newGeoQt;
		newGeoQt.setX( newGeometry.x() - nFrameSizeLeft );
		newGeoQt.setY( newGeometry.y() - nFrameSizeTop );

		newGeoQt.setWidth( newGeometry.width() + nFrameSizeLeft + KMDI_CHILDFRM_DOUBLE_BORDER / 2 );
		newGeoQt.setHeight( newGeometry.height() + nFrameSizeTop + KMDI_CHILDFRM_DOUBLE_BORDER / 2 );
		//      newGeoQt.setWidth(newGeometry.width()+KMDI_MDI_CHILDFRM_DOUBLE_BORDER);
		//      newGeoQt.setHeight(newGeometry.height()+mdiParent()->captionHeight()+KMDI_MDI_CHILDFRM_DOUBLE_BORDER);

		// set the geometry
		mdiParent()->setGeometry( newGeoQt );
	}
	else
	{
		// retrieve the frame size
		QRect geo = internalGeometry();
		QRect frameGeo = externalGeometry();
		int nFrameSizeTop = geo.y() - frameGeo.y();
		int nFrameSizeLeft = geo.x() - frameGeo.x();

		// create the new geometry that is accepted by the QWidget::setGeometry() method
		QRect newGeoQt;

		newGeoQt.setX( newGeometry.x() - nFrameSizeLeft );
		newGeoQt.setY( newGeometry.y() - nFrameSizeTop );

		newGeoQt.setWidth( newGeometry.width() );
		newGeoQt.setHeight( newGeometry.height() );

		// set the geometry
		setGeometry( newGeoQt );
	}
}

//============== external geometry ==============//

QRect KMdiChildView::externalGeometry() const
{
	return mdiParent() ? mdiParent()->frameGeometry() : frameGeometry();
}

//============== set external geometry ==============//

void KMdiChildView::setExternalGeometry( const QRect& newGeometry )
{
	if ( mdiParent() )
	{ // is attached
		mdiParent() ->setGeometry( newGeometry );
	}
	else
	{
		// retrieve the frame size
		QRect geo = internalGeometry();
		QRect frameGeo = externalGeometry();
		int nTotalFrameWidth = frameGeo.width() - geo.width();
		int nTotalFrameHeight = frameGeo.height() - geo.height();
		int nFrameSizeTop = geo.y() - frameGeo.y();
		int nFrameSizeLeft = geo.x() - frameGeo.x();

		// create the new geometry that is accepted by the QWidget::setGeometry() method
		// not attached => the window system makes the frame
		QRect newGeoQt;
		newGeoQt.setX( newGeometry.x() + nFrameSizeLeft );
		newGeoQt.setY( newGeometry.y() + nFrameSizeTop );
		newGeoQt.setWidth( newGeometry.width() - nTotalFrameWidth );
		newGeoQt.setHeight( newGeometry.height() - nTotalFrameHeight );

		// set the geometry
		setGeometry( newGeoQt );
	}
}

//============== minimize ==============//

void KMdiChildView::minimize( bool bAnimate )
{
	if ( mdiParent() )
	{
		if ( !isMinimized() )
		{
			mdiParent() ->setState( KMdiChildFrm::Minimized, bAnimate );
		}
	}
	else
		showMinimized();
}

void KMdiChildView::showMinimized()
{
	emit isMinimizedNow();
	QWidget::showMinimized();
}

//slot:
void KMdiChildView::minimize()
{
	minimize( true );
}

//============= maximize ==============//

void KMdiChildView::maximize( bool bAnimate )
{
	if ( mdiParent() )
	{
		if ( !isMaximized() )
		{
			mdiParent() ->setState( KMdiChildFrm::Maximized, bAnimate );
			emit mdiParentNowMaximized( true );
		}
	}
	else
		showMaximized();
}

void KMdiChildView::showMaximized()
{
	emit isMaximizedNow();
	QWidget::showMaximized();
}

//slot:
void KMdiChildView::maximize()
{
	maximize( true );
}

//============== restoreGeometry ================//

QRect KMdiChildView::restoreGeometry()
{
	if ( mdiParent() )
		return mdiParent() ->restoreGeometry();
	else //FIXME not really supported, may be we must use Windows or X11 funtions
		return geometry();
}

//============== setRestoreGeometry ================//

void KMdiChildView::setRestoreGeometry( const QRect& newRestGeo )
{
	if ( mdiParent() )
		mdiParent()->setRestoreGeometry( newRestGeo );
}

//============== attach ================//

void KMdiChildView::attach()
{
	emit attachWindow( this, true );
}

//============== detach =================//

void KMdiChildView::detach()
{
	emit detachWindow( this, true );
}

//=============== isMinimized ? =================//

bool KMdiChildView::isMinimized() const
{
	if ( mdiParent() )
		return ( mdiParent()->state() == KMdiChildFrm::Minimized );
	else
		return QWidget::isMinimized();
}

//============== isMaximized ? ==================//

bool KMdiChildView::isMaximized() const
{
	if ( mdiParent() )
		return ( mdiParent()->state() == KMdiChildFrm::Maximized );
	else
		return QWidget::isMaximized();
}

//============== restore ================//

void KMdiChildView::restore()
{
	if ( mdiParent() )
	{
		if ( isMaximized() )
			emit mdiParentNowMaximized( false );
		
		if ( isMinimized() || isMaximized() )
			mdiParent()->setState( KMdiChildFrm::Normal );
	}
	else
		showNormal();
}

void KMdiChildView::showNormal()
{
	emit isRestoredNow();
	QWidget::showNormal();
}

//=============== youAreAttached ============//

void KMdiChildView::youAreAttached( KMdiChildFrm *lpC )
{
	lpC->setCaption( m_szCaption );
	emit isAttachedNow();
}

//================ youAreDetached =============//

void KMdiChildView::youAreDetached()
{
	setCaption( m_szCaption );

	setTabCaption( m_sTabCaption );
	if ( myIconPtr() )
		setIcon( *( myIconPtr() ) );
	
	setFocusPolicy( QWidget::StrongFocus );

	emit isDetachedNow();
}

//================ setCaption ================//
// this set the caption of only the window
void KMdiChildView::setCaption( const QString& szCaption )
{
	// this will work only for window
	m_szCaption = szCaption;
	if ( mdiParent() )
		mdiParent() ->setCaption( m_szCaption );
	else //have to call the parent one
		QWidget::setCaption( m_szCaption );

	emit windowCaptionChanged( m_szCaption );
}

//============== closeEvent ================//

void KMdiChildView::closeEvent( QCloseEvent *e )
{
	e->ignore(); //we ignore the event , and then close later if needed.
	emit childWindowCloseRequest( this );
}

//================ myIconPtr =================//

QPixmap* KMdiChildView::myIconPtr()
{
	return 0;
}

//============= focusInEvent ===============//

void KMdiChildView::focusInEvent( QFocusEvent *e )
{
	QWidget::focusInEvent( e );

	// every widget get a focusInEvent when a popup menu is opened!?! -> maybe bug of QT
	if ( e && ( ( e->reason() ) == QFocusEvent::Popup ) )
		return ;


	m_bFocusInEventIsPending = true;
	activate();
	m_bFocusInEventIsPending = false;

	emit gotFocus( this );
}

//============= activate ===============//

void KMdiChildView::activate()
{
	// avoid circularity
	static bool s_bActivateIsPending = false;
	if ( s_bActivateIsPending )
		return ;
	
	s_bActivateIsPending = true;

	// raise the view and push the taskbar button
	if ( !m_bMainframesActivateViewIsPending )
		emit focusInEventOccurs( this );

	// if this method was called directly, check if the mainframe wants that we interrupt
	if ( m_bInterruptActivation )
		m_bInterruptActivation = false;
	else
	{
		if ( !m_bFocusInEventIsPending )
			setFocus();
		
		kdDebug( 760 ) << k_funcinfo << endl;
		emit activated( this );
	}

	if ( m_focusedChildWidget != 0L )
		m_focusedChildWidget->setFocus();
	else
	{
		if ( m_firstFocusableChildWidget != 0L )
		{
			m_firstFocusableChildWidget->setFocus();
			m_focusedChildWidget = m_firstFocusableChildWidget;
		}
	}
	s_bActivateIsPending = false;
}

//============= focusOutEvent ===============//

void KMdiChildView::focusOutEvent( QFocusEvent* e )
{
	QWidget::focusOutEvent( e );
	emit lostFocus( this );
}

//============= resizeEvent ===============//

void KMdiChildView::resizeEvent( QResizeEvent* e )
{
	QWidget::resizeEvent( e );

	if ( m_stateChanged )
	{
		m_stateChanged = false;
		if ( isMaximized() )
		{ //maximized
			emit isMaximizedNow();
		}
		else if ( isMinimized() )
		{ //minimized
			emit isMinimizedNow();
		}
		else
		{ //is restored
			emit isRestoredNow();
		}
	}
}

void KMdiChildView::slot_childDestroyed()
{
	// do what we do if a child is removed

	// if we lost a child we uninstall ourself as event filter for the lost
	// child and its children
	const QObject * pLostChild = QObject::sender();
	if ( pLostChild && ( pLostChild->isWidgetType() ) )
	{
		QObjectList* list = ( ( QObject* ) ( pLostChild ) ) ->queryList( "QWidget" );
		list->insert( 0, pLostChild );        // add the lost child to the list too, just to save code
		QObjectListIt it( *list );          // iterate over all lost child widgets
		QObject* obj;
		while ( ( obj = it.current() ) != 0 )
		{ // for each found object...
			QWidget * widg = ( QWidget* ) obj;
			++it;
			widg->removeEventFilter( this );
			if ( m_firstFocusableChildWidget == widg )
				m_firstFocusableChildWidget = 0L;   // reset first widget
			
			if ( m_lastFocusableChildWidget == widg )
				m_lastFocusableChildWidget = 0L;    // reset last widget
			
			if ( m_focusedChildWidget == widg )
				m_focusedChildWidget = 0L;          // reset focused widget
		}
		delete list;                        // delete the list, not the objects
	}
}

//============= eventFilter ===============//
bool KMdiChildView::eventFilter( QObject *obj, QEvent *e )
{
	if ( e->type() == QEvent::KeyPress && isAttached() )
	{
		QKeyEvent* ke = ( QKeyEvent* ) e;
		if ( ke->key() == Qt::Key_Tab )
		{
			QWidget* w = ( QWidget* ) obj;
			FocusPolicy wfp = w->focusPolicy();
			if ( wfp == QWidget::StrongFocus || wfp == QWidget::TabFocus || w->focusPolicy() == QWidget::WheelFocus )
			{
				if ( m_lastFocusableChildWidget != 0 )
				{
					if ( w == m_lastFocusableChildWidget )
					{
						if ( w != m_firstFocusableChildWidget )
							m_firstFocusableChildWidget->setFocus();
					}
				}
			}
		}
	}
	else if ( e->type() == QEvent::FocusIn )
	{
		if ( obj->isWidgetType() )
		{
			QObjectList * list = queryList( "QWidget" );
			if ( list->find( obj ) != -1 )
				m_focusedChildWidget = ( QWidget* ) obj;

			delete list;   // delete the list, not the objects
		}
		if ( !isAttached() )
		{   // is toplevel, for attached views activation is done by main frame event filter
			static bool m_bActivationIsPending = false;
			if ( !m_bActivationIsPending )
			{
				m_bActivationIsPending = true;
				activate(); // sets the focus
				m_bActivationIsPending = false;
			}
		}
	}
	else if ( e->type() == QEvent::ChildRemoved )
	{
		// if we lost a child we uninstall ourself as event filter for the lost
		// child and its children
		QObject * pLostChild = ( ( QChildEvent* ) e ) ->child();
		if ( ( pLostChild != 0L ) && ( pLostChild->isWidgetType() ) )
		{
			QObjectList * list = pLostChild->queryList( "QWidget" );
			list->insert( 0, pLostChild );        // add the lost child to the list too, just to save code
			QObjectListIt it( *list );          // iterate over all lost child widgets
			QObject * o;
			while ( ( o = it.current() ) != 0 )
			{ // for each found object...
				QWidget * widg = ( QWidget* ) o;
				++it;
				widg->removeEventFilter( this );
				FocusPolicy wfp = widg->focusPolicy();
				if ( wfp == QWidget::StrongFocus || wfp == QWidget::TabFocus || widg->focusPolicy() == QWidget::WheelFocus )
				{
					if ( m_firstFocusableChildWidget == widg )
						m_firstFocusableChildWidget = 0L;   // reset first widget
					
					if ( m_lastFocusableChildWidget == widg )
						m_lastFocusableChildWidget = 0L;    // reset last widget
				}
			}
			delete list;                        // delete the list, not the objects
		}
	}
	else if ( e->type() == QEvent::ChildInserted )
	{
		// if we got a new child and we are attached to the MDI system we
		// install ourself as event filter for the new child and its children
		// (as we did when we were added to the MDI system).
		QObject * pNewChild = ( ( QChildEvent* ) e ) ->child();
		if ( ( pNewChild != 0L ) && ( pNewChild->isWidgetType() ) )
		{
			QWidget * pNewWidget = ( QWidget* ) pNewChild;
			if ( pNewWidget->testWFlags( Qt::WType_Dialog | Qt::WShowModal ) )
				return false;
			QObjectList *list = pNewWidget->queryList( "QWidget" );
			list->insert( 0, pNewChild );         // add the new child to the list too, just to save code
			QObjectListIt it( *list );          // iterate over all new child widgets
			QObject * o;
			while ( ( o = it.current() ) != 0 )
			{ // for each found object...
				QWidget * widg = ( QWidget* ) o;
				++it;
				widg->installEventFilter( this );
				connect( widg, SIGNAL( destroyed() ), this, SLOT( slot_childDestroyed() ) );
				FocusPolicy wfp = widg->focusPolicy();
				if ( wfp == QWidget::StrongFocus || wfp == QWidget::TabFocus || widg->focusPolicy() == QWidget::WheelFocus )
				{
					if ( m_firstFocusableChildWidget == 0 )
						m_firstFocusableChildWidget = widg;  // first widge
					
					m_lastFocusableChildWidget = widg; // last widget
				}
			}
			delete list;                        // delete the list, not the objects
		}
	}
	else
	{
		if ( e->type() == QEvent::IconChange )
		{
			//            qDebug("KMDiChildView:: QEvent:IconChange intercepted\n");
			if ( obj == this )
				iconUpdated( this, icon() ? ( *icon() ) : QPixmap() );
			else if ( obj == m_trackChanges )
				setIcon( m_trackChanges->icon() ? ( *( m_trackChanges->icon() ) ) : QPixmap() );
		}
		if ( e->type() == QEvent::CaptionChange )
		{
			if ( obj == this )
				captionUpdated( this, caption() );
		}
	}

	return false;                           // standard event processing
}

/** Switches interposing in event loop of all current child widgets off. */
void KMdiChildView::removeEventFilterForAllChildren()
{
	QObjectList* list = queryList( "QWidget" );
	QObjectListIt it( *list );          // iterate over all child widgets
	QObject* obj;
	while ( ( obj = it.current() ) != 0 )
	{ // for each found object...
		QWidget* widg = ( QWidget* ) obj;
		++it;
		widg->removeEventFilter( this );
	}
	delete list;                        // delete the list, not the objects
}

QWidget* KMdiChildView::focusedChildWidget()
{
	return m_focusedChildWidget;
}

void KMdiChildView::setFirstFocusableChildWidget( QWidget* firstFocusableChildWidget )
{
	m_firstFocusableChildWidget = firstFocusableChildWidget;
}

void KMdiChildView::setLastFocusableChildWidget( QWidget* lastFocusableChildWidget )
{
	m_lastFocusableChildWidget = lastFocusableChildWidget;
}

/** Set a new value of  the task bar button caption  */
void KMdiChildView::setTabCaption ( const QString& stbCaption )
{
	m_sTabCaption = stbCaption;
	emit tabCaptionChanged( m_sTabCaption );
}

void KMdiChildView::setMDICaption ( const QString& caption )
{
	setCaption( caption );
	setTabCaption( caption );
}

/** sets an ID  */
void KMdiChildView::setWindowMenuID( int id )
{
	m_windowMenuID = id;
}

//============= slot_clickedInWindowMenu ===============//

/** called if someone click on the "Window" menu item for this child frame window */
void KMdiChildView::slot_clickedInWindowMenu()
{
	updateTimeStamp();
	emit clickedInWindowMenu( m_windowMenuID );
}

//============= slot_clickedInDockMenu ===============//

/** called if someone click on the "Dock/Undock..." menu item for this child frame window */
void KMdiChildView::slot_clickedInDockMenu()
{
	emit clickedInDockMenu( m_windowMenuID );
}

//============= setMinimumSize ===============//

void KMdiChildView::setMinimumSize( int minw, int minh )
{
	QWidget::setMinimumSize( minw, minh );
	if ( mdiParent() && mdiParent()->state() != KMdiChildFrm::Minimized )
	{
		mdiParent() ->setMinimumSize( minw + KMDI_CHILDFRM_DOUBLE_BORDER,
		                              minh + KMDI_CHILDFRM_DOUBLE_BORDER + KMDI_CHILDFRM_SEPARATOR + mdiParent() ->captionHeight() );
	}
}

//============= setMaximumSize ===============//

void KMdiChildView::setMaximumSize( int maxw, int maxh )
{
	if ( mdiParent() && mdiParent()->state() == KMdiChildFrm::Normal )
	{
		int w = maxw + KMDI_CHILDFRM_DOUBLE_BORDER;
		if ( w > QWIDGETSIZE_MAX )
			w = QWIDGETSIZE_MAX;

		int h = maxh + KMDI_CHILDFRM_DOUBLE_BORDER + KMDI_CHILDFRM_SEPARATOR + mdiParent() ->captionHeight();
		if ( h > QWIDGETSIZE_MAX )
			h = QWIDGETSIZE_MAX;

		mdiParent()->setMaximumSize( w, h );
	}
	QWidget::setMaximumSize( maxw, maxh );
}

//============= show ===============//

void KMdiChildView::show()
{
	if ( mdiParent() )
		mdiParent()->show();

	QWidget::show();
}

//============= hide ===============//

void KMdiChildView::hide()
{
	if ( mdiParent() )
		mdiParent()->hide();
	
	QWidget::hide();
}

//============= raise ===============//

void KMdiChildView::raise()
{
	if ( mdiParent() )  //TODO Check Z-order
		mdiParent()->raise();

	QWidget::raise();
}

// kate: space-indent off; replace-tabs off; indent-mode csands; tab-width 4;
