//=============================================================================
//
//   File : NotifierWindow.cpp
//   Creation date : Tue Jul 6 2004 20:25:12 CEST by Szymon Stefanek
//
//   This file is part of the KVIrc irc client distribution
//   Copyright (C) 2004-2008 Iacopo Palazzi < iakko(at)siena.linux.it >
//
//   This program is FREE software. You can redistribute it and/or
//   modify it under the linkss of the GNU General Public License
//   as published by the Free Software Foundation; either version 2
//   of the License, or (at your opinion) 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 "kvi_settings.h"

#include "NotifierMessage.h"
#include "NotifierWindow.h"
#include "NotifierWindowBorder.h"
#include "NotifierWindowTab.h"

#include "KviIconManager.h"
#include "KviConfigurationFile.h"
#include "KviWindow.h"
#include "KviLocale.h"
#include "KviMainWindow.h"
#include "KviOptions.h"
#include "KviUserInput.h"
#include "KviThemedLineEdit.h"

#include <QApplication>
#include <QImage>
#include <QDesktopWidget>
#include <QToolTip>
#include <QEvent>
#include <QPen>
#include <QFontMetrics>
#include <QRegExp>
#include <QPainter>
#include <QPaintEvent>
#include <QMouseEvent>

extern NotifierWindow * g_pNotifierWindow;

NotifierWindow::NotifierWindow()
: QWidget(0,
#if defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW)
	Qt::FramelessWindowHint |
	Qt::Tool |
	Qt::WindowStaysOnTopHint)
#else
	Qt::FramelessWindowHint |
#ifndef COMPILE_ON_MAC
	Qt::Tool |
	Qt::X11BypassWindowManagerHint |
#endif
	Qt::WindowStaysOnTopHint)
#endif
{
	setObjectName("kvirc_notifier_window");

	g_pNotifierWindow = this;

	m_eState = Hidden;
	m_dOpacity = 0.0;
	m_pShowHideTimer = 0;
	m_pBlinkTimer = 0;
	m_tAutoHideAt = 0;
	m_tStartedAt=0;
	m_pAutoHideTimer = 0;

	m_pWndBorder = new NotifierWindowBorder();

	setFocusPolicy(Qt::NoFocus);
	setMouseTracking(true);
	setAutoFillBackground(true);

	hide();

	m_bBlinkOn = false;

	m_bCloseDown = false;
	m_bPrevDown = false;
	m_bNextDown = false;
	m_bWriteDown = false;

	m_bLeftButtonIsPressed = false;
	m_bDiagonalResizing = false;
	m_bResizing = false;

	m_pContextPopup = 0;
	m_pDisablePopup = 0;

	m_bDragging = false;

	m_bDisableHideOnMainWindowGotAttention = false;

	// Positioning the notifier bottom-right
	QDesktopWidget * pDesktop = QApplication::desktop();
	QRect r = pDesktop->availableGeometry(pDesktop->primaryScreen());

	m_wndRect.setRect(
		r.x() + r.width() - (WDG_MIN_WIDTH + SPACING),
		r.y() + r.height() - (WDG_MIN_HEIGHT + SPACING),
		WDG_MIN_WIDTH,
		WDG_MIN_HEIGHT);

	m_pWndTabs = new QTabWidget(this);
	m_pWndTabs->setUsesScrollButtons(true);
	m_pWndTabs->setTabsClosable(true);
	connect(m_pWndTabs,SIGNAL(tabCloseRequested(int)),this,SLOT(slotTabCloseRequested(int)));

	m_pProgressBar = new QProgressBar(this);
	m_pProgressBar->setOrientation(Qt::Vertical);
	m_pProgressBar->setRange(0, 100);
	m_pProgressBar->setTextVisible(false);
	m_pProgressBar->setMaximumWidth(8);
	m_pProgressBar->installEventFilter(this);

	m_pLineEdit = new KviThemedLineEdit(this, 0, "notifier_lineedit");
	QPalette palette=m_pLineEdit->palette();
	palette.setColor(m_pLineEdit->backgroundRole(), Qt::transparent);
	m_pLineEdit->setPalette(palette);
	m_pLineEdit->installEventFilter(this);
	connect(m_pLineEdit,SIGNAL(returnPressed()),this,SLOT(returnPressed()));

	QGridLayout *layout = new QGridLayout;
	layout->addWidget(m_pProgressBar, 0,0,2,1);
	layout->addWidget(m_pWndTabs,0,1,1,1);
	layout->addWidget(m_pLineEdit,1,1,1,1);
	layout->setSpacing(SPACING);

	layout->setGeometry(m_pWndBorder->bodyRect());
	layout->setContentsMargins(5,25,5,5);
	setLayout(layout);

	connect(g_pApp,SIGNAL(updateNotifier()),this,SLOT(updateGui()));
	QTimer::singleShot(0,this,SLOT(updateGui()));
}

NotifierWindow::~NotifierWindow()
{
	stopShowHideTimer();
	stopBlinkTimer();
	stopAutoHideTimer();
	delete m_pWndBorder;
	m_pWndTabs->deleteLater();
}

void NotifierWindow::updateGui()
{
	setFont(KVI_OPTION_FONT(KviOption_fontNotifier));
	QPalette palette=m_pLineEdit->palette();
	palette.setColor(m_pLineEdit->foregroundRole(), KVI_OPTION_COLOR(KviOption_colorNotifierForeground));
	m_pLineEdit->setPalette(palette);
	m_pLineEdit->setFont(KVI_OPTION_FONT(KviOption_fontNotifier));
	
	for(int i=0; i<m_pWndTabs->count();++i)
	{
		((NotifierWindowTab*)m_pWndTabs->widget(i))->updateGui();
	}

}

void NotifierWindow::addMessage(KviWindow * pWnd, const QString & szImageId, const QString & szText, unsigned int uMessageTime)
{
	QPixmap * pIcon;
	QString szMessage = szText;
	szMessage.replace( QRegExp("\r([^\r])*\r([^\r])+\r"), "\\2" );
	if(szImageId.isEmpty())
		pIcon = 0;
	else
		pIcon = g_pIconManager->getImage(szImageId);

	NotifierMessage * pMessage = new NotifierMessage(pIcon ? new QPixmap(*pIcon) : 0,szMessage);

	//search for an existing tab
	NotifierWindowTab * pTab = 0, * pTmp = 0;
	for(int i=0; i < m_pWndTabs->count();++i)
	{
		pTmp = (NotifierWindowTab*)m_pWndTabs->widget(i);
		if(pTmp->wnd() == pWnd)
		{
			pTab = pTmp;
			break;
		}
	}

	if(!pTab)
	{
		pTab = new NotifierWindowTab(pWnd, m_pWndTabs);
	}

	//if the notifier is already visible, don't steal the focus from the current tab!
	//the user could be writing a message on it (bug #678)
	if(!isVisible())
		m_pWndTabs->setCurrentWidget(pTab);

	pTab->appendMessage(pMessage);

	if(!isActiveWindow())
		startBlinking();

	if(uMessageTime > 0)
	{
		kvi_time_t tAutoHide = kvi_unixTime() + uMessageTime;
		if(m_tAutoHideAt < tAutoHide)
		{
			m_tAutoHideAt = tAutoHide;
			if(m_eState == Visible)
				startAutoHideTimer();
		}
	} else {
		// never hide
		stopAutoHideTimer();
		m_tAutoHideAt = 0;
	}

	if(pWnd)
	{
		if(pWnd->hasAttention())
			m_bDisableHideOnMainWindowGotAttention = true;
	}

	if(isVisible())
		update();
}

void NotifierWindow::stopShowHideTimer()
{
	if(!m_pShowHideTimer)
		return;
	delete m_pShowHideTimer;
	m_pShowHideTimer = 0;
}

void NotifierWindow::stopBlinkTimer()
{
	if(!m_pBlinkTimer)
		return;
	delete m_pBlinkTimer;
	m_pBlinkTimer = 0;
}

void NotifierWindow::stopAutoHideTimer()
{
	if(!m_pAutoHideTimer)
		return;
	delete m_pAutoHideTimer;
	m_pAutoHideTimer = 0;
	m_pProgressBar->setValue(0);
}

// FIXME: The code for detecting fullscreen window does NOT take into account multi-screen setups.
//        We also lack the code for MacOSX and Qt-only-X11 compilation.

#if COMPILE_KDE_SUPPORT
	#include <kwindowsystem.h>
	
	static bool active_window_is_full_screen()
	{
		WId activeId = KWindowSystem::activeWindow();
		KWindowInfo wi = KWindowSystem::windowInfo(activeId, NET::WMState);
		return (wi.valid() && wi.hasState(NET::FullScreen));
	}
#else // COMPILE_KDE_SUPPORT
	#if defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW)
	
		#include <windows.h>
		
		static bool active_window_is_full_screen()
		{
			HWND hForeground = ::GetForegroundWindow();
			if(!hForeground)
				return false;

			HWND hDesktop = ::GetDesktopWindow();
			if(hForeground == hDesktop)
				return false;

			HWND hShell = ::GetShellWindow();
			if(hForeground == hShell)
				return false;

			RECT rct;
			::GetWindowRect(hForeground,&rct);

			if((rct.right - rct.left) < GetSystemMetrics(SM_CXSCREEN))
				return false;
			
			if((rct.bottom - rct.top) < GetSystemMetrics(SM_CYSCREEN))
				return false;

			return true;
		}
	
	#endif //COMPILE_ON_WINDOWS || COMPILE_ON_MINGW
#endif // COMPILE_KDE_SUPPORT

void NotifierWindow::doShow(bool bDoAnimate)
{
	if(!KVI_OPTION_BOOL(KviOption_boolEnableNotifier))
		return;

	kvi_time_t tNow = kvi_unixTime();
	if(g_tNotifierDisabledUntil > tNow)
		return;
	g_tNotifierDisabledUntil = 0;

#if defined(COMPILE_KDE_SUPPORT) || defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW)
	if(KVI_OPTION_BOOL(KviOption_boolDontShowNotifierIfActiveWindowIsFullScreen))
	{
		// check if the active window is full screen
		if(active_window_is_full_screen())
			return;
	}
#endif //COMPILE_KDE_SUPPORT || COMPILE_ON_WINDOWS || COMPILE_ON_MINGW

	switch(m_eState)
	{
		case Showing:
			// already showing up
			return;
		break;
		case Visible:
			// already visible
			return;
		break;
		case Hiding:
			// ops.. hiding!
			m_eState = Showing;
		break;
		case Hidden:
			stopShowHideTimer();
			stopBlinkTimer();

			m_bDragging = false;
			m_bCloseDown = false;
			m_bPrevDown = false;
			m_bNextDown = false;
			m_bWriteDown = false;
			m_bBlinkOn = false;
			if(bDoAnimate)
			{
				m_pShowHideTimer = new QTimer();
				connect(m_pShowHideTimer,SIGNAL(timeout()),this,SLOT(heartbeat()));
				m_dOpacity = OPACITY_STEP;
				m_eState = Showing;
				m_bCrashShowWorkAround = true;
				setWindowOpacity(m_dOpacity);
				show();
				m_pShowHideTimer->start(40);
				m_bCrashShowWorkAround = false;
			} else {
				m_dOpacity = 1.0;
				m_eState = Visible;
				show();
				startBlinking();
				startAutoHideTimer();
			}
		break;
		default:
		break;
	}
}

bool NotifierWindow::shouldHideIfMainWindowGotAttention()
{
	if(m_bDisableHideOnMainWindowGotAttention)
		return false;
	NotifierWindowTab * pTab = (NotifierWindowTab *) m_pWndTabs->currentWidget();
	if(!pTab)
		return false;
	if(!pTab->wnd())
		return false;
	return pTab->wnd()->hasAttention();
}

void NotifierWindow::heartbeat()
{
	bool bIncreasing;
	double targetOpacity = 0;
	switch(m_eState)
	{
		case Hidden:
			hideNow();
		break;
		case Visible:
			stopShowHideTimer();
			m_dOpacity = 1.0;
			if(!isVisible())
				show();
			else
				update();
		break;
		case Showing:
			// if the main window got attention while
			// showing up then just hide now
			if(shouldHideIfMainWindowGotAttention())
			{
				m_eState = Hiding;
			} else {
				m_dOpacity += OPACITY_STEP;
				targetOpacity = isActiveWindow() ? KVI_OPTION_UINT(KviOption_uintNotifierActiveTransparency) : KVI_OPTION_UINT(KviOption_uintNotifierInactiveTransparency);

				targetOpacity/=100;
				if(m_dOpacity >= targetOpacity)
				{
					m_dOpacity = targetOpacity;
					m_eState = Visible;
					stopShowHideTimer();
					startBlinking();
					startAutoHideTimer();
				}

				if(!isVisible())
					show();
				setWindowOpacity(m_dOpacity);
				update();

			}
		break;
		case FocusingOn:
			targetOpacity = KVI_OPTION_UINT(KviOption_uintNotifierActiveTransparency);
			targetOpacity /= 100;
			bIncreasing = targetOpacity > m_dOpacity;
			m_dOpacity += bIncreasing?
				OPACITY_STEP : -(OPACITY_STEP);
			if((bIncreasing && (m_dOpacity >= targetOpacity)) ||
				(!bIncreasing && (m_dOpacity <= targetOpacity))
				)
			{
				m_dOpacity = targetOpacity;
				m_eState = Visible;
				stopShowHideTimer();
			}

			setWindowOpacity(m_dOpacity);
			break;
		case FocusingOff:
			targetOpacity = KVI_OPTION_UINT(KviOption_uintNotifierInactiveTransparency);
			targetOpacity /= 100;
			bIncreasing = targetOpacity > m_dOpacity;
			m_dOpacity += bIncreasing ? OPACITY_STEP : -(OPACITY_STEP);
			//qDebug("%f %f %i %i",m_dOpacity,targetOpacity,bIncreasing,(m_dOpacity >= targetOpacity));
			if((bIncreasing && (m_dOpacity >= targetOpacity)) ||
				(!bIncreasing && (m_dOpacity <= targetOpacity))
				)
			{
				m_dOpacity = targetOpacity;
				m_eState = Visible;
				stopShowHideTimer();
			}

			setWindowOpacity(m_dOpacity);
			break;
		case Hiding:
			m_dOpacity -= OPACITY_STEP;
			setWindowOpacity(m_dOpacity);
			if(m_dOpacity <= 0.0)
				hideNow();
			else
				update();
		break;
	}
}

void NotifierWindow::hideNow()
{
	stopBlinkTimer();
	stopShowHideTimer();
	stopAutoHideTimer();
	m_eState = Hidden;
	m_dOpacity = 0.0;
	m_tAutoHideAt = 0;
	hide();
}

void NotifierWindow::doHide(bool bDoAnimate)
{
	stopAutoHideTimer();
	switch(m_eState)
	{
	case Hiding:
		// already hiding up
		if(!bDoAnimate)
			hideNow();
		return;
		break;
	case Hidden:
		// already hidden
		if(isVisible())
			hideNow();
		return;
		break;
	case Showing:
		// ops.. hiding!
		if(!bDoAnimate)
			hideNow();
		else {
			// continue animating, but hide
			m_eState = Hiding;
		}
		break;
	case Visible:
		stopBlinkTimer();
		stopShowHideTimer();
		if((!bDoAnimate) || (x() != m_pWndBorder->x()) || (y() != m_pWndBorder->y()))
		{

			//qDebug("just hide quickly with notifier x() %d and notifier y() % - WBorderx() %d and WBordery() %d and bDoanimate %d",x(),y(),m_pWndBorder->x(),m_pWndBorder->y(),bDoAnimate);
			// the user asked to not animate or
			// the window has been moved and the animation would suck anyway
			// just hide quickly
			hideNow();
		} else {
			//qDebug("starting hide animation notifier x() %d and notifier y() % - WBorderx() %d and WBordery() %d and bDoanimate %d",x(),y(),m_pWndBorder->x(),m_pWndBorder->y(),bDoAnimate);
			m_pShowHideTimer = new QTimer();
			connect(m_pShowHideTimer,SIGNAL(timeout()),this,SLOT(heartbeat()));
			m_dOpacity = 1.0 - OPACITY_STEP;
			m_eState = Hiding;
			setWindowOpacity(m_dOpacity);
			update();
			m_pShowHideTimer->start(40);
		}
		break;
		default:
		break;
	}
}

void NotifierWindow::showEvent(QShowEvent *)
{
 	setGeometry(m_wndRect);
}

void NotifierWindow::hideEvent(QHideEvent *)
{
	if(m_bCrashShowWorkAround)
		return;
	stopBlinkTimer();
	stopShowHideTimer();
	stopAutoHideTimer();
	m_eState = Hidden; // make sure it's true
	m_tAutoHideAt = 0; // make sure it's true
	m_bDisableHideOnMainWindowGotAttention = false;
}

void NotifierWindow::startBlinking()
{
	stopBlinkTimer();
	m_bBlinkOn = false;

	if(KVI_OPTION_BOOL(KviOption_boolNotifierFlashing))
	{
		m_pBlinkTimer = new QTimer();
		connect(m_pBlinkTimer,SIGNAL(timeout()),this,SLOT(blink()));
		m_iBlinkCount = 0;
		m_pBlinkTimer->start(1000);
	}
}

void NotifierWindow::startAutoHideTimer()
{
	stopAutoHideTimer();
	m_tStartedAt = kvi_unixTime();
	if(m_tAutoHideAt <= m_tStartedAt)
		return;

	int iSecs = m_tAutoHideAt - m_tStartedAt;
	if(iSecs < 5)
		iSecs = 5;
	m_pAutoHideTimer = new QTimer();

	connect(m_pAutoHideTimer,SIGNAL(timeout()),this,SLOT(progressUpdate()));
	m_pAutoHideTimer->start(100);
}

void NotifierWindow::blink()
{
	m_iBlinkCount++;
	m_bBlinkOn = !m_bBlinkOn;

	if(m_iBlinkCount > 100)
	{
		// stop blinking at a certain point and remain highlighted
		m_bBlinkOn = true;
		stopBlinkTimer();
	} else {
		// if the main window got attention while
		// showing up then just hide now
		if(shouldHideIfMainWindowGotAttention())
		{
			doHide(false); // don't animate: the background has almost surely changed
			return;
		}
	}
	update();
}

void NotifierWindow::paintEvent(QPaintEvent * e)
{
	QPainter * pPaint = new QPainter(this);

	if(m_wndRect.width() != m_pWndBorder->width() || m_wndRect.height() != m_pWndBorder->height())
		m_pWndBorder->resize(m_wndRect.size());

	if(m_bBlinkOn)
	{
		m_pWndBorder->draw(pPaint,true);
	} else {
		m_pWndBorder->draw(pPaint);
	}

	pPaint->setPen(KVI_OPTION_COLOR(KviOption_colorNotifierTitleForeground));
	pPaint->setFont(KVI_OPTION_FONT(KviOption_fontNotifierTitle));

	QString szTitle = "KVIrc - ";
	NotifierWindowTab * pTab = (NotifierWindowTab *)m_pWndTabs->currentWidget();
	if(pTab)
	{
		if(pTab->wnd())
		{
			szTitle += pTab->wnd()->plainTextCaption();
		} else {
			szTitle += "notifier";
		}
	} else {
			szTitle += "notifier";
	}
	pPaint->drawText(m_pWndBorder->titleRect(),Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine,szTitle);
	
	delete pPaint;
	e->ignore();
}

void NotifierWindow::mouseMoveEvent(QMouseEvent * e)
{
	if(!m_bLeftButtonIsPressed)
	{
		if(checkResizing(e->pos()))
			goto sartelo;

		if(m_pWndBorder->captionRect().contains(e->pos()))
		{
			if(m_pWndBorder->closeRect().contains(e->pos()))
				m_pWndBorder->setCloseIcon(WDG_ICON_OVER);
			else
				m_pWndBorder->setCloseIcon(WDG_ICON_OUT);
			goto sartelo;
		}

sartelo:
		update();
	}

	if(m_bDragging)
	{
		setCursor(Qt::SizeAllCursor);

		int iW = m_wndRect.width();
		int iH = m_wndRect.height();

		m_wndRect.setX(m_pntPos.x() + cursor().pos().x() - m_pntDrag.x());
		m_wndRect.setY(m_pntPos.y() + cursor().pos().y() - m_pntDrag.y());

		m_wndRect.setWidth(iW);
		m_wndRect.setHeight(iH);

		setGeometry(m_wndRect);
	} else if(m_bResizing)
	{
		resize(e->pos());
	}
}

void NotifierWindow::mousePressEvent(QMouseEvent * e)
{
	// stop blinking first of all :)
	bool bWasBlinkOn = m_bBlinkOn;
	m_bBlinkOn = false;
	stopBlinkTimer();
	m_tAutoHideAt = 0;
	stopAutoHideTimer();

	activateWindow();
	setFocus();

	m_pntClick = e->pos();

	if(e->button() == Qt::RightButton)
	{
		contextPopup(mapToGlobal(e->pos()));
		return;
	} else if(e->button() == Qt::LeftButton)
	{
		m_bLeftButtonIsPressed = true;
	}

	if(checkResizing(m_pntClick))
	{
		update();
		return;
	}
	
	if(m_pWndBorder->captionRect().contains(e->pos()))
	{
		if(m_pWndBorder->closeRect().contains(e->pos()))
		{
			m_bCloseDown = true;
			m_pWndBorder->setCloseIcon(WDG_ICON_CLICKED);
			update();
			return;
		}

		if(!m_bResizing)
		{
			m_bDragging = true;
			m_pntDrag = cursor().pos();
			m_pntPos = pos();
			update();
			return;
		}
	}

	if(m_pWndBorder->rect().contains(e->pos()) || bWasBlinkOn)
		update();
}

void NotifierWindow::mouseReleaseEvent(QMouseEvent * e)
{
	m_bWriteDown = false;
	m_bNextDown = false;
	m_bPrevDown = false;
	m_bCloseDown = false;
	m_bLeftButtonIsPressed = false;
	m_bResizing = false;

	if(m_bDragging)
	{
		m_bDragging = false;
		if(QApplication::overrideCursor())
			QApplication::restoreOverrideCursor();
		return;
	}

	if(m_pWndBorder->captionRect().contains(e->pos()))
	{
		if(m_pWndBorder->closeRect().contains(e->pos()))
			hideNow();
		else
			update();
	}

	setCursor(-1);
}

void NotifierWindow::keyPressEvent(QKeyEvent * e)
{
	if(e->key() == Qt::Key_Escape)
	{
		hideNow();
		return;
	}
}

bool NotifierWindow::checkResizing(QPoint e)
{
	if(e.y() < WDG_BORDER_THICKNESS)
	{
		if(e.x() < WDG_BORDER_THICKNESS)
		{
			//// UP LEFT CORNER ////
			setCursor(Qt::SizeFDiagCursor);
			if (m_bLeftButtonIsPressed)
			{
				m_bResizing = true;
				m_whereResizing = WDG_UPSX;
			}
		} else if(e.x() > (size().width()-WDG_BORDER_THICKNESS))
		{
			//// UP RIGHT CORNER ////
			setCursor(Qt::SizeBDiagCursor);
			if (m_bLeftButtonIsPressed)
			{
				m_bResizing = true;
				m_whereResizing = WDG_UPDX;
			}
		} else {
			//// UP SIDE ////
			setCursor(Qt::SizeVerCursor);
			if (m_bLeftButtonIsPressed)
			{
				m_bResizing = true;
				m_whereResizing = WDG_UP;
			}
		}
	} else if(e.y() > (size().height()-WDG_BORDER_THICKNESS))
	{
		if(e.x() < WDG_BORDER_THICKNESS)
		{
			//// DOWN LEFT CORNER ////
			setCursor(Qt::SizeBDiagCursor);
			if(m_bLeftButtonIsPressed)
			{
				m_bResizing = true;
				m_whereResizing = WDG_DWNSX;
			}
		} else if(e.x() > (size().width()-WDG_BORDER_THICKNESS))
		{
			//// DOWN RIGHT CORNER ////
			setCursor(Qt::SizeFDiagCursor);
			if(m_bLeftButtonIsPressed)
			{
				m_bResizing = true;
				m_whereResizing = WDG_DWNDX;
			}
		} else {
			//// DOWN SIDE ////
			setCursor(Qt::SizeVerCursor);
			if(m_bLeftButtonIsPressed)
			{
				m_bResizing = true;
				m_whereResizing = WDG_DWN;
			}
		}
	} else {
		if(e.x() < WDG_BORDER_THICKNESS)
		{
			//// LEFT SIZE ////
			setCursor(Qt::SizeHorCursor);
			if(m_bLeftButtonIsPressed)
			{
				m_bResizing = true;
				m_whereResizing = WDG_SX;
			}
		} else if(e.x() > (size().width()-WDG_BORDER_THICKNESS))
		{
			//// RIGHT SIZE ////
			setCursor(Qt::SizeHorCursor);
			if(m_bLeftButtonIsPressed)
			{
				m_bResizing = true;
				m_whereResizing = WDG_DX;
			}
		} else {
			//// ELSEWHERE ////
			m_whereResizing = 0;
			m_bResizing = false;
			setCursor(-1);
		}
	}
	return m_bResizing;
}

void NotifierWindow::resize(QPoint, bool)
{
	if(m_whereResizing == WDG_UPSX || m_whereResizing == WDG_SX || m_whereResizing == WDG_DWNSX)
	{
		if((x() + width() - cursor().pos().x()) < WDG_MIN_WIDTH)
			m_wndRect.setLeft(x() + width() - WDG_MIN_WIDTH);
		else
			m_wndRect.setLeft(cursor().pos().x());
	}

	if(m_whereResizing == WDG_UPSX || m_whereResizing == WDG_UP || m_whereResizing == WDG_UPDX)
	{
		if(y()+height()-cursor().pos().y() < WDG_MIN_HEIGHT)
			m_wndRect.setTop(y() + height() - WDG_MIN_HEIGHT);
		else
			m_wndRect.setTop(cursor().pos().y());
	}

	if(m_whereResizing == WDG_DX || m_whereResizing == WDG_UPDX || m_whereResizing == WDG_DWNDX)
	{
		if((cursor().pos().x() - x()) > WDG_MIN_WIDTH)
			m_wndRect.setRight(cursor().pos().x());
		else
			m_wndRect.setRight(x() + WDG_MIN_WIDTH);
	}

	if(m_whereResizing == WDG_DWN || m_whereResizing == WDG_DWNDX || m_whereResizing == WDG_DWNSX)
	{
		if((cursor().pos().y() - y()) > WDG_MIN_HEIGHT)
			m_wndRect.setBottom(cursor().pos().y());
		else
			m_wndRect.setBottom(y() + WDG_MIN_HEIGHT);
	}

	showLineEdit(m_pLineEdit->isVisible());
	setGeometry(m_wndRect);
}

inline void NotifierWindow::setCursor(int iCur)
{
	if(m_cursor.shape() != iCur)
	{
		if(QApplication::overrideCursor())
			QApplication::restoreOverrideCursor();
		m_cursor.setShape((Qt::CursorShape)iCur);
		QApplication::setOverrideCursor(m_cursor);
	} else if(iCur == -1)
	{
		if(QApplication::overrideCursor())
			QApplication::restoreOverrideCursor();
	}
}

void NotifierWindow::enterEvent(QEvent *)
{
	if(!m_pShowHideTimer)
	{
		m_pShowHideTimer = new QTimer();
		connect(m_pShowHideTimer,SIGNAL(timeout()),this,SLOT(heartbeat()));
	}
	m_eState = FocusingOn;
	m_pShowHideTimer->start(40);
}

void NotifierWindow::leaveEvent(QEvent *)
{
	// Leaving the widget area, restore default cursor
	m_pWndBorder->resetIcons();
	if(!m_bResizing)
		setCursor(-1);
	
	if(!m_pShowHideTimer)
	{
		m_pShowHideTimer = new QTimer();
		connect(m_pShowHideTimer,SIGNAL(timeout()),this,SLOT(heartbeat()));
	}
	
	if(m_eState!=Hidden)
	{
		m_eState = FocusingOff;
		m_pShowHideTimer->start(40);
	}
}

void NotifierWindow::contextPopup(const QPoint & pos)
{
	if(!m_pContextPopup)
	{
		m_pContextPopup = new KviTalPopupMenu(this);
		connect(m_pContextPopup,SIGNAL(aboutToShow()),this,SLOT(fillContextPopup()));
		m_pDisablePopup = new KviTalPopupMenu(this);
	}

	m_pContextPopup->popup(pos);
}

void NotifierWindow::fillContextPopup()
{
	m_pContextPopup->clear();
	m_pContextPopup->insertItem(*(g_pIconManager->getSmallIcon(KviIconManager::Editor)),__tr2qs_ctx("Show/Hide input line","notifier"),this,SLOT(toggleLineEdit()));
	m_pContextPopup->insertItem(*(g_pIconManager->getSmallIcon(KviIconManager::Close)),__tr2qs_ctx("Hide","notifier"),this,SLOT(hideNow()));
	m_pContextPopup->insertSeparator();
	m_pDisablePopup->clear();
	m_pDisablePopup->insertItem(*(g_pIconManager->getSmallIcon(KviIconManager::Time)),__tr2qs_ctx("1 Minute","notifier"),this,SLOT(disableFor1Minute()));
	m_pDisablePopup->insertItem(*(g_pIconManager->getSmallIcon(KviIconManager::Time)),__tr2qs_ctx("5 Minutes","notifier"),this,SLOT(disableFor5Minutes()));
	m_pDisablePopup->insertItem(*(g_pIconManager->getSmallIcon(KviIconManager::Time)),__tr2qs_ctx("15 Minutes","notifier"),this,SLOT(disableFor15Minutes()));
	m_pDisablePopup->insertItem(*(g_pIconManager->getSmallIcon(KviIconManager::Time)),__tr2qs_ctx("30 Minutes","notifier"),this,SLOT(disableFor30Minutes()));
	m_pDisablePopup->insertItem(*(g_pIconManager->getSmallIcon(KviIconManager::Time)),__tr2qs_ctx("1 Hour","notifier"),this,SLOT(disableFor60Minutes()));
	m_pDisablePopup->insertSeparator();
	m_pDisablePopup->insertItem(*(g_pIconManager->getSmallIcon(KviIconManager::Quit)),__tr2qs_ctx("Until KVIrc is Restarted","notifier"),this,SLOT(disableUntilKVIrcRestarted()));
	m_pDisablePopup->insertItem(*(g_pIconManager->getSmallIcon(KviIconManager::Quit)),__tr2qs_ctx("Permanently (Until Explicitly Enabled)","notifier"),this,SLOT(disablePermanently()));

	m_pContextPopup->insertItem(*(g_pIconManager->getSmallIcon(KviIconManager::Quit)),__tr2qs_ctx("Disable","notifier"),m_pDisablePopup);
}

void NotifierWindow::disableFor15Minutes()
{
	kvi_time_t tNow = kvi_unixTime();
	g_tNotifierDisabledUntil = tNow + (60 * 15);

	hideNow();
}

void NotifierWindow::disableFor5Minutes()
{
	kvi_time_t tNow = kvi_unixTime();
	g_tNotifierDisabledUntil = tNow + (60 * 5);

	hideNow();
}


void NotifierWindow::disableFor1Minute()
{
	kvi_time_t tNow = kvi_unixTime();
	g_tNotifierDisabledUntil = tNow + 60;

	hideNow();
}

void NotifierWindow::disableFor30Minutes()
{
	kvi_time_t tNow = kvi_unixTime();
	g_tNotifierDisabledUntil = tNow + (60 * 30);

	hideNow();
}

void NotifierWindow::disableFor60Minutes()
{
	kvi_time_t tNow = kvi_unixTime();
	g_tNotifierDisabledUntil = tNow + (60 * 60);

	hideNow();
}

void NotifierWindow::disableUntilKVIrcRestarted()
{
	kvi_time_t tNow = kvi_unixTime();
	// in fact we just disable for one year
	g_tNotifierDisabledUntil = tNow + (3600 * 24 * 360); // one year :D
	// this will NOT work if the module is forcibly unloaded with /notifier.unload
	// but the user should never do it :D
	hideNow();
}

void NotifierWindow::disablePermanently()
{
	KVI_OPTION_BOOL(KviOption_boolEnableNotifier) = false;
	hideNow();
}

void NotifierWindow::toggleLineEdit()
{
	showLineEdit(!m_pLineEdit->isVisible());
}

void NotifierWindow::showLineEdit(bool bShow)
{
	if(bShow)
	{
		if(m_pLineEdit->isVisible())
			return;
		if(!((NotifierWindowTab *)m_pWndTabs->currentWidget())->wnd())
			return;
		m_pLineEdit->setToolTip("");
		
		QString szTip = __tr2qs_ctx("Write text or commands to window","notifier");
		szTip += " \"";
		szTip += ((NotifierWindowTab *)m_pWndTabs->currentWidget())->wnd()->plainTextCaption();
		szTip += "\"";
		m_pLineEdit->setToolTip(szTip);
		m_pLineEdit->show();
		m_pLineEdit->setFocus();
		activateWindow();
	} else {
		if(!m_pLineEdit->isVisible())
			return;
		m_pLineEdit->hide();
		setFocus();
		update();
	}
}

bool NotifierWindow::eventFilter(QObject * pEdit, QEvent * e)
{
	if(pEdit != (QObject *)m_pLineEdit)
		return false; // huh ?
	if(!m_pLineEdit->isVisible())
		return false;
	if(e->type() == QEvent::MouseButtonPress)
	{
		bool bWasBlinkOn = m_bBlinkOn;
		m_bBlinkOn = false;
		m_tAutoHideAt = 0;
		stopAutoHideTimer();
		stopBlinkTimer();
		activateWindow();
		m_pLineEdit->setFocus();
		if(bWasBlinkOn)
			update();
		return true;
	}
	if(e->type() == QEvent::KeyPress)
	{
		if(((QKeyEvent *)e)->key() == Qt::Key_Escape)
		{
			hideNow();
			return true;
		}
	}
	return false;
}

void NotifierWindow::returnPressed()
{
	if(!m_pLineEdit->isVisible())
		return;

	NotifierWindowTab * pTab = (NotifierWindowTab *) m_pWndTabs->currentWidget();
	if(!pTab)
		return;
	if(!pTab->wnd())
		return;

	QString szTxt = m_pLineEdit->text();
	if(szTxt.isEmpty())
		return;
	QString szHtml = szTxt;
	szHtml.replace("<","&lt;");
	szHtml.replace(">","&gt;");
	KviCString szTmp(KviCString::Format,"%d",KviIconManager::OwnPrivMsg);

	addMessage(pTab->wnd(),szTmp.ptr(),szHtml,0);
	m_pLineEdit->setText("");
	KviUserInput::parse(szTxt,pTab->wnd(),QString(),1);
}

void NotifierWindow::progressUpdate()
{
	kvi_time_t now = kvi_unixTime();
	int iProgress = (int)(100/(m_tAutoHideAt - m_tStartedAt)*(now - m_tStartedAt));
	m_pProgressBar->setValue(iProgress);
	if(now >= m_tAutoHideAt)
	{
		m_tAutoHideAt = 0;
		stopAutoHideTimer();
		doHide(true);
	}
}

void NotifierWindow::slotTabCloseRequested(int iIndex)
{
	if(m_pWndTabs)
	{
		NotifierWindowTab * pTab = (NotifierWindowTab *) m_pWndTabs->widget(iIndex);
		m_pWndTabs->removeTab(iIndex);
		if(pTab)
			pTab->deleteLater();

		if(m_pWndTabs->count()==0)
			hideNow();
	}
}

#ifndef COMPILE_USE_STANDALONE_MOC_SOURCES
#include "m_NotifierWindow.moc"
#endif //!COMPILE_USE_STANDALONE_MOC_SOURCES
