/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#ifndef nsWidget_h__
#define nsWidget_h__

#include "nsBaseWidget.h"
#include "nsIKBStateControl.h"
#include "nsIRegion.h"
#ifdef PHOTON_DND
#include "nsIDragService.h"
#endif
#include "nsClipboard.h"

class nsILookAndFeel;
class nsIAppShell;
class nsIToolkit;

#include <Pt.h>

class nsWidget;

#define NS_TO_PH_RGB(ns) (ns & 0xff) << 16 | (ns & 0xff00) | ((ns >> 16) & 0xff)
#define PH_TO_NS_RGB(ns) (ns & 0xff) << 16 | (ns & 0xff00) | ((ns >> 16) & 0xff)

/**
 * Base of all Photon native widgets.
 */

class nsWidget : public nsBaseWidget, nsIKBStateControl
{
public:
  nsWidget();
  virtual ~nsWidget();

  NS_DECL_ISUPPORTS_INHERITED

  // nsIWidget

	// create with nsIWidget parent
  inline NS_IMETHOD            Create(nsIWidget *aParent,
                               const nsRect &aRect,
                               EVENT_CALLBACK aHandleEventFunction,
                               nsIDeviceContext *aContext,
                               nsIAppShell *aAppShell = nsnull,
                               nsIToolkit *aToolkit = nsnull,
                               nsWidgetInitData *aInitData = nsnull)
		{
		return(CreateWidget(aParent, aRect, aHandleEventFunction, aContext, aAppShell, aToolkit, aInitData, nsnull));
		}

	// create with a native parent
  inline NS_IMETHOD            Create(nsNativeWidget aParent,
                               const nsRect &aRect,
                               EVENT_CALLBACK aHandleEventFunction,
                               nsIDeviceContext *aContext,
                               nsIAppShell *aAppShell = nsnull,
                               nsIToolkit *aToolkit = nsnull,
                               nsWidgetInitData *aInitData = nsnull)
		{
		return(CreateWidget(nsnull, aRect, aHandleEventFunction, aContext, aAppShell, aToolkit, aInitData,aParent));
		}

  NS_IMETHOD Destroy(void);
  inline nsIWidget* GetParent(void)
		{
		if( mIsDestroying ) return nsnull;
		nsIWidget* result = mParent;
		if( mParent ) NS_ADDREF( result );
		return result;
		}

  virtual void OnDestroy();

  NS_IMETHOD SetModal(PRBool aModal);
  NS_IMETHOD Show(PRBool state);
  inline NS_IMETHOD CaptureRollupEvents(nsIRollupListener *aListener, PRBool aDoCapture, PRBool aConsumeRollupEvent) { return NS_OK; }

  inline NS_IMETHOD IsVisible(PRBool &aState) { aState = mShown; return NS_OK; }

  inline NS_IMETHOD ConstrainPosition(PRBool aAllowSlop, PRInt32 *aX, PRInt32 *aY) { return NS_OK; }
  NS_IMETHOD Move(PRInt32 aX, PRInt32 aY);
  NS_IMETHOD Resize(PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint);
  inline NS_IMETHOD Resize(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint)
		{
		Move(aX,aY);
		Resize(aWidth,aHeight,aRepaint);
		return NS_OK;
		}

  inline NS_IMETHOD Enable(PRBool aState)
		{
		if( mWidget ) PtSetResource( mWidget, Pt_ARG_FLAGS, aState ? 0 : Pt_BLOCKED, Pt_BLOCKED );
		return NS_OK;
		}

  inline NS_IMETHOD IsEnabled(PRBool *aState)
		{
		if(PtWidgetFlags(mWidget) & Pt_BLOCKED) *aState = PR_FALSE;
		else *aState = PR_TRUE;
		return NS_OK;
		}

  PRBool OnResize(nsSizeEvent event);
  virtual PRBool OnResize(nsRect &aRect);
  virtual PRBool OnMove(PRInt32 aX, PRInt32 aY);

  inline nsIFontMetrics *GetFont(void) { return nsnull; }
  NS_IMETHOD SetFont(const nsFont &aFont);

  inline NS_IMETHOD SetBackgroundColor(const nscolor &aColor)
		{
		nsBaseWidget::SetBackgroundColor( aColor );
		if( mWidget ) PtSetResource( mWidget, Pt_ARG_FILL_COLOR, NS_TO_PH_RGB( aColor ), 0 );
		return NS_OK;
		}

  NS_IMETHOD SetCursor(nsCursor aCursor);

  inline NS_IMETHOD SetColorMap(nsColorMap *aColorMap) { return NS_OK; }

  inline void* GetNativeData(PRUint32 aDataType) { return (void *)mWidget; }

  NS_IMETHOD WidgetToScreen(const nsRect &aOldRect, nsRect &aNewRect);
  NS_IMETHOD ScreenToWidget(const nsRect &aOldRect, nsRect &aNewRect);

  inline NS_IMETHOD BeginResizingChildren(void)
		{
		PtHold();
		return NS_OK;
		}

  inline NS_IMETHOD EndResizingChildren(void)
		{
		PtRelease();
		return NS_OK;
		}

  inline NS_IMETHOD GetPreferredSize(PRInt32& aWidth, PRInt32& aHeight)
		{
		aWidth  = mPreferredWidth;
		aHeight = mPreferredHeight;
		return (mPreferredWidth != 0 && mPreferredHeight != 0)?NS_OK:NS_ERROR_FAILURE;
		}

  inline NS_IMETHOD SetPreferredSize(PRInt32 aWidth, PRInt32 aHeight)
		{
		mPreferredWidth  = aWidth;
		mPreferredHeight = aHeight;
		return NS_OK;
		}

  // Use this to set the name of a widget for normal widgets.. not the same as the nsWindow version
  inline NS_IMETHOD SetTitle(const nsAString& aTitle) { return NS_OK; }

  inline void ConvertToDeviceCoordinates(nscoord &aX, nscoord &aY) { }

  // the following are nsWindow specific, and just stubbed here
  inline NS_IMETHOD Scroll(PRInt32 aDx, PRInt32 aDy, nsRect *aClipRect) { return NS_OK; }
  inline NS_IMETHOD ScrollWidgets(PRInt32 aDx, PRInt32 aDy) { return NS_OK; }
 
  inline NS_IMETHOD SetMenuBar(nsIMenuBar *aMenuBar) { return NS_ERROR_FAILURE; }
  inline NS_IMETHOD ShowMenuBar(PRBool aShow) { return NS_ERROR_FAILURE; }
  // *could* be done on a widget, but that would be silly wouldn't it?
  NS_IMETHOD CaptureMouse(PRBool aCapture) { return NS_ERROR_FAILURE; }


  NS_IMETHOD Invalidate(PRBool aIsSynchronous);
  NS_IMETHOD Invalidate(const nsRect &aRect, PRBool aIsSynchronous);
  NS_IMETHOD InvalidateRegion(const nsIRegion *aRegion, PRBool aIsSynchronous);
  inline NS_IMETHOD Update(void)
		{
		/* if the widget has been invalidated or damaged then re-draw it */
		PtFlush();
		return NS_OK;
		}

  NS_IMETHOD DispatchEvent(nsGUIEvent* event, nsEventStatus & aStatus);


  // nsIKBStateControl
  NS_IMETHOD ResetInputState();
  NS_IMETHOD SetIMEOpenState(PRBool aState);
  NS_IMETHOD GetIMEOpenState(PRBool* aState);
  NS_IMETHOD SetIMEEnabled(PRUint32 aState);
  NS_IMETHOD GetIMEEnabled(PRUint32* aState);
  NS_IMETHOD CancelIMEComposition();
  NS_IMETHOD GetToggledKeyState(PRUint32 aKeyCode, PRBool* aLEDState);

  inline void InitEvent(nsGUIEvent& event, PRUint32 aEventType, nsPoint* aPoint = nsnull)
		{
		if( aPoint == nsnull ) {
		  event.refPoint.x = 0;
		  event.refPoint.y = 0;
		  }
		else {
		  event.refPoint.x = aPoint->x;
		  event.refPoint.y = aPoint->y;
		  }
		event.widget = this;
		event.time = PR_IntervalNow();
		event.message = aEventType;
		}

  // Utility functions

  inline PRBool     ConvertStatus(nsEventStatus aStatus)
		{
		return aStatus == nsEventStatus_eConsumeNoDefault;
		}

  PRBool     DispatchMouseEvent(nsMouseEvent& aEvent);
  inline PRBool     DispatchStandardEvent(PRUint32 aMsg)
		{
		nsGUIEvent event(PR_TRUE, 0, nsnull);
		InitEvent(event, aMsg);
		return DispatchWindowEvent(&event);
		}

  // are we a "top level" widget?
  PRBool     mIsToplevel;

  // Set/Get the Pt_ARG_USER_DATA associated with each PtWidget_t *
  // this is always set to the "this" that owns the PtWidget_t
  static inline void SetInstance( PtWidget_t *pWidget, nsWidget * inst ) { PtSetResource( pWidget, Pt_ARG_POINTER, (void *)inst, 0 ); }
  static inline nsWidget*  GetInstance( PtWidget_t *pWidget )
		{
		nsWidget *data;
		PtGetResource( pWidget, Pt_ARG_POINTER, &data, 0 );
		return data;
		}

protected:
  NS_IMETHOD CreateNative(PtWidget_t *parentWindow) { return NS_OK; }

  nsresult CreateWidget(nsIWidget *aParent,
                        const nsRect &aRect,
                        EVENT_CALLBACK aHandleEventFunction,
                        nsIDeviceContext *aContext,
                        nsIAppShell *aAppShell,
                        nsIToolkit *aToolkit,
                        nsWidgetInitData *aInitData,
                        nsNativeWidget aNativeParent = nsnull);

  inline PRBool DispatchWindowEvent(nsGUIEvent* event)
		{
		nsEventStatus status;
		DispatchEvent(event, status);
		return ConvertStatus(status);
		}

#ifdef PHOTON_DND
	void DispatchDragDropEvent( PhEvent_t *phevent, PRUint32 aEventType, PhPoint_t *pos );
	void ProcessDrag( PhEvent_t *event, PRUint32 aEventType, PhPoint_t *pos );
#endif

  // this is the "native" destroy code that will destroy any
  // native windows / widgets for this logical widget
  virtual void DestroyNative(void);

  //////////////////////////////////////////////////////////////////
  //
  // Photon event support methods
  //
  //////////////////////////////////////////////////////////////////
  static int      RawEventHandler( PtWidget_t *widget, void *data, PtCallbackInfo_t *cbinfo );
  inline PRBool		HandleEvent( PtWidget_t *, PtCallbackInfo_t* aCbInfo );
  PRBool          DispatchKeyEvent(PhKeyEvent_t *aPhKeyEvent);

  inline void ScreenToWidgetPos( PhPoint_t &pt )
		{
		// pt is in screen coordinates - convert it to be relative to ~this~ widgets origin
		short x, y;
		PtGetAbsPosition( mWidget, &x, &y );
		pt.x -= x; pt.y -= y;
		}

	inline void InitKeyEvent(PhKeyEvent_t *aPhKeyEvent, nsKeyEvent &anEvent );
	inline void InitKeyPressEvent(PhKeyEvent_t *aPhKeyEvent, nsKeyEvent &anEvent );
  void InitMouseEvent( PhPointerEvent_t * aPhButtonEvent,
                       nsWidget         * aWidget,
                       nsMouseEvent     & anEvent,
                       PRUint32         aEventType,
                       PRInt16          aButton);


  /* Convert Photon key codes to Mozilla key codes */
  PRUint32 nsConvertKey( PhKeyEvent_t *aPhKeyEvent );

#if 0
  //Enable/Disable Photon Damage		  
	inline void EnableDamage( PtWidget_t *widget, PRBool enable )
		{
		PtWidget_t *top = PtFindDisjoint( widget );
		if( top ) {
			if( PR_TRUE == enable ) PtEndFlux( top );
			else PtStartFlux( top );
			}
		}
#endif


  //////////////////////////////////////////////////////////////////
  //
  // Photon widget callbacks
  //
  //////////////////////////////////////////////////////////////////
  static int  GotFocusCallback( PtWidget_t *widget, void *data, PtCallbackInfo_t *cbinfo );
  static int  LostFocusCallback( PtWidget_t *widget, void *data, PtCallbackInfo_t *cbinfo );
  static int  DestroyedCallback( PtWidget_t *widget, void *data, PtCallbackInfo_t *cbinfo );
#ifdef PHOTON_DND
  static int  DndCallback( PtWidget_t *widget, void *data, PtCallbackInfo_t *cbinfo );
#endif

  PtWidget_t          *mWidget;
  nsIWidget						*mParent;
  PRBool mShown;

  PRUint32 mPreferredWidth, mPreferredHeight;
  PRBool       mListenForResizes;
   
  // Focus used global variable
  static nsWidget* sFocusWidget; //Current Focus Widget
  
  static nsILookAndFeel *sLookAndFeel;
#ifdef PHOTON_DND
	static nsIDragService *sDragService;
#endif
	static nsClipboard *sClipboard;
  static PRUint32 sWidgetCount;
};

#endif /* nsWidget_h__ */
