text:  /* The smooth Class Library
    * Copyright (C) 1998-2020 Robert Kausch <robert.kausch@gmx.net>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of "The Artistic License, Version 2.0".
    *
    * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
    * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
    * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */
  
  #include <smooth/gui/window/backends/haiku/windowhaiku.h>
  #include <smooth/gui/window/window.h>
  #include <smooth/gui/application/application.h>
  #include <smooth/input/pointer.h>
  #include <smooth/misc/math.h>
  #include <smooth/backends/haiku/backendhaiku.h>
  #include <smooth/system/backends/haiku/eventhaiku.h>
  #include <smooth/system/system.h>
  #include <smooth/init.h>
  #include <smooth/foreach.h>
  
  #include <Message.h>
  #include <Region.h>
  #include <Entry.h>
  #include <Path.h>
  
  S::GUI::WindowBackend *CreateWindowHaiku()
  {
  	return new S::GUI::WindowHaiku();
  }
  
  S::Int	 windowHaikuTmp = S::GUI::WindowBackend::SetBackend(&CreateWindowHaiku);
  
  S::Array<S::GUI::WindowHaiku *, S::Void *>	 S::GUI::WindowHaiku::windowBackends;
  
  namespace smooth
  {
  	namespace GUI
  	{
  		const Int	 B_WINDOW_CREATED   = 'WCRE';
  		const Int	 B_WINDOW_DESTROYED = 'WDES';
  		const Int	 B_PAINT	    = 'PINT';
  
  		class HaikuView : public BView
  		{
  			public:
  				HaikuView(BRect frame) : BView(frame, NULL, B_FOLLOW_ALL_SIDES, B_WILL_DRAW)
  				{
  					SetEventMask(B_POINTER_EVENTS);
  
  					SetViewColor(Setup::BackgroundColor.GetRed(), Setup::BackgroundColor.GetGreen(), Setup::BackgroundColor.GetBlue());
  				}
  
  				void Draw(BRect updateRect)
  				{
  					BWindow		*wnd	  = Window();
  					WindowHaiku	*backend  = WindowHaiku::GetWindowBackend(wnd);
  
  					backend->ProcessSystemMessages(B_PAINT, (((int) updateRect.left    + 32768) << 16) | ((int) updateRect.top	+ 32768),
  										(((int) updateRect.Width() + 32768) << 16) | ((int) updateRect.Height() + 32768), *(wnd->CurrentMessage()));
  				}
  
  				void MouseMoved(BPoint point, uint32 transit, const BMessage *message)
  				{
  					BWindow	*wnd = Window();
  					BPoint	 pos = wnd->ConvertToScreen(point);
  
  					if	(transit == B_ENTERED_VIEW ||
  						 transit == B_INSIDE_VIEW)  Input::Pointer::UpdatePosition(Window::GetWindow(wnd), pos.x, pos.y);
  					else if (transit == B_EXITED_VIEW)  Input::Pointer::UpdatePosition(NIL, pos.x, pos.y);
  					else if (transit == B_OUTSIDE_VIEW) Input::Pointer::UpdatePosition(Input::Pointer::GetPointerWindow(), pos.x, pos.y);
  
  					System::EventHaiku::EnqueueMessage(wnd, *(wnd->CurrentMessage()), B_MOUSE_MOVED, point.x, point.y);
  				}
  
  				void MouseDown(BPoint point)
  				{
  					BWindow	*wnd = Window();
  
  					if (Input::Pointer::GetPointerWindow() != Window::GetWindow(wnd)) return;
  
  					System::EventHaiku::EnqueueMessage(wnd, *(wnd->CurrentMessage()), B_MOUSE_DOWN, point.x, point.y);
  				}
  
  				void MouseUp(BPoint point)
  				{
  					BWindow	*wnd = Window();
  
  					System::EventHaiku::EnqueueMessage(wnd, *(wnd->CurrentMessage()), B_MOUSE_UP, point.x, point.y);
  				}
  		};
  
  		class HaikuWindow : public BWindow
  		{
  			private:
  				BView	*view;
  			public:
  				HaikuWindow(BRect frame, String title, Int look, Int feel, S::Int flags) : BWindow(frame, title, (window_look) look, (window_feel) feel, flags)
  				{
  					view = new HaikuView(Bounds());
  
  					AddChild(view);
  				}
  
  				void WindowActivated(bool active)
  				{
  					System::EventHaiku::EnqueueMessage(this, *CurrentMessage(), B_WINDOW_ACTIVATED, active, 0);
  				}
  
  				void FrameMoved(BPoint origin)
  				{
  					WindowHaiku	*backend = WindowHaiku::GetWindowBackend(this);
  
  					backend->ProcessSystemMessages(B_WINDOW_MOVED, origin.x, origin.y, *CurrentMessage());
  				}
  
  				void FrameResized(float width, float height)
  				{
  					WindowHaiku	*backend = WindowHaiku::GetWindowBackend(this);
  
  					backend->ProcessSystemMessages(B_WINDOW_RESIZED, width, height, *CurrentMessage());
  				}
  
  				bool QuitRequested()
  				{
  					WindowHaiku	*backend = WindowHaiku::GetWindowBackend(this);
  
  					backend->RequestClose();
  
  					if (backend->GetSystemWindow() == NIL) return true;
  					else				       return false;
  				}
  
  				void MessageReceived(BMessage *message)
  				{
  					System::EventHaiku::EnqueueMessage(this, *CurrentMessage(), message->what, 0, 0);
  
  					if (message->what != B_SIMPLE_DATA) BWindow::MessageReceived(message);
  				}
  		};
  	};
  };
  
  S::GUI::WindowHaiku::WindowHaiku(Void *iWindow)
  {
  	type	    = WINDOW_HAIKU;
  
  	wnd	    = NIL;
  	view	    = NIL;
  
  	id	    = windowBackends.Add(this);
  
  	minSize	    = Size(160, 24);
  	maxSize	    = Size(32768, 32768);
  
  	fontSize    = Surface().GetSurfaceDPI() / 96.0;
  
  	flags	    = 0;
  
  	dropMessage = NIL;
  }
  
  S::GUI::WindowHaiku::~WindowHaiku()
  {
  	windowBackends.Remove(id);
  }
  
  S::Void *S::GUI::WindowHaiku::GetSystemWindow() const
  {
  	return (Void *) wnd;
  }
  
  S::GUI::WindowHaiku *S::GUI::WindowHaiku::GetWindowBackend(BWindow *wnd)
  {
  	if (wnd == NIL) return NIL;
  
  	for (Int i = 0; i < windowBackends.Length(); i++)
  	{
  		WindowHaiku	*window = windowBackends.GetNth(i);
  
  		if (window != NIL)
  		{
  			if (window->wnd == wnd) return window;
  		}
  	}
  
  	return NIL;
  }
  
  S::Input::Keyboard::Key S::GUI::WindowHaiku::ConvertKey(Int keyCode, const BMessage &currentMessage)
  {
  	Input::Keyboard::Key	 key = Input::Keyboard::KeyOther;
  
  	switch (keyCode)
  	{
  		case B_LEFT_ARROW:  key = Input::Keyboard::KeyLeft;   break;
  		case B_UP_ARROW:    key = Input::Keyboard::KeyUp;     break;
  		case B_RIGHT_ARROW: key = Input::Keyboard::KeyRight;  break;
  		case B_DOWN_ARROW:  key = Input::Keyboard::KeyDown;   break;
  
  		case B_HOME:	    key = Input::Keyboard::KeyHome;   break;
  		case B_END:	    key = Input::Keyboard::KeyEnd;    break;
  		case B_INSERT:	    key = Input::Keyboard::KeyInsert; break;
  		case B_DELETE:	    key = Input::Keyboard::KeyDelete; break;
  		case B_PAGE_UP:	    key = Input::Keyboard::KeyPrior;  break;
  		case B_PAGE_DOWN:   key = Input::Keyboard::KeyNext;   break;
  
  		case B_RETURN:	    key = Input::Keyboard::KeyReturn; break;
  		case B_BACKSPACE:   key = Input::Keyboard::KeyBack;   break;
  		case B_TAB:	    key = Input::Keyboard::KeyTab;    break;
  
  		case B_SPACE:	    key = Input::Keyboard::KeySpace;  break;
  
  		case B_ESCAPE:	    key = Input::Keyboard::KeyEscape; break;
  
  		case B_FUNCTION_KEY:
  			{
  				int32	  keyCode;
  
  				currentMessage.FindInt32("key", &keyCode);
  
  				switch (keyCode)
  				{
  					case B_F1_KEY:	key = Input::Keyboard::KeyF1;  break;
  					case B_F2_KEY:	key = Input::Keyboard::KeyF2;  break;
  					case B_F3_KEY:	key = Input::Keyboard::KeyF3;  break;
  					case B_F4_KEY:	key = Input::Keyboard::KeyF4;  break;
  					case B_F5_KEY:	key = Input::Keyboard::KeyF5;  break;
  					case B_F6_KEY:	key = Input::Keyboard::KeyF6;  break;
  					case B_F7_KEY:	key = Input::Keyboard::KeyF7;  break;
  					case B_F8_KEY:	key = Input::Keyboard::KeyF8;  break;
  					case B_F9_KEY:	key = Input::Keyboard::KeyF9;  break;
  					case B_F10_KEY:	key = Input::Keyboard::KeyF10; break;
  					case B_F11_KEY:	key = Input::Keyboard::KeyF11; break;
  					case B_F12_KEY:	key = Input::Keyboard::KeyF12; break;
  				}
  			}
  	}
  
  	if	(keyCode >= '0' && keyCode <= '9') key = (Input::Keyboard::Key)  keyCode;
  	else if	(keyCode >= 'a' && keyCode <= 'z') key = (Input::Keyboard::Key) (keyCode - 0x20);
  
  	return key;
  }
  
  S::Int S::GUI::WindowHaiku::ProcessSystemMessages(Int message, Int wParam, Int lParam, const BMessage &currentMessage)
  {
  	static Int	 focusWndId = -1;
  
  	/* Lock application while processing messages.
  	 */
  	Application::Lock	 lock;
  
  	/* Process system messages not relevant
  	 * to portable Window implementation.
  	 */
  	switch (message)
  	{
  		case B_WINDOW_CREATED:
  			onCreate.Emit();
  
  			/* Set internal focus window.
  			 */
  			focusWndId = id;
  
  			onEvent.Call(SM_GETFOCUS, 0, 0);
  
  			return MessageProcessed;
  
  		case B_WINDOW_DESTROYED:
  			onDestroy.Emit();
  
  			/* Clear internal focus window.
  			 */
  			if (focusWndId == id) focusWndId = -1;
  
  			return MessageProcessed;
  	}
  
  	static int32	 buttons = -1;
  
  	int32		 clicks	 =  0;
  	float		 amount	 =  0;
  
  	/* Convert Windows messages to smooth messages.
  	 */
  	switch (message)
  	{
  		/* Mouse messages:
  		 */
  		case B_MOUSE_MOVED:
  			return onEvent.Call(SM_MOUSEMOVE, 0, 0);
  
  		case B_MOUSE_WHEEL_CHANGED:
  			currentMessage.FindFloat("be:wheel_delta_y", &amount);
  
  			/* Pass message to smooth window.
  			 */
  			onEvent.Call(SM_MOUSEWHEEL, amount * -120.0, 0);
  
  			break;
  
  		case B_MOUSE_DOWN:
  			currentMessage.FindInt32("buttons", &buttons);
  			currentMessage.FindInt32("clicks", &clicks);
  
  			/* Reject if a modal window is active.
  			 */
  			if (IsModalWindowActive()) break;
  
  			/* Grab the keyboard focus if we don't have it already.
  			 */
  			if (focusWndId != id)
  			{
  				WindowHaiku	*focusWnd = windowBackends.Get(focusWndId);
  
  				if (focusWnd != NIL) focusWnd->onEvent.Call(SM_LOSEFOCUS, Window::GetWindow((Void *) wnd)->GetHandle(), 0);
  
  				focusWndId = id;
  
  				onEvent.Call(SM_GETFOCUS, 0, 0);
  			}
  
  			/* Send mouse button event.
  			 */
  			if	(buttons == B_PRIMARY_MOUSE_BUTTON)   onEvent.Call(SM_LBUTTONDOWN, 0, 0);
  			else if (buttons == B_SECONDARY_MOUSE_BUTTON) onEvent.Call(SM_RBUTTONDOWN, 0, 0);
  			else if (buttons == B_TERTIARY_MOUSE_BUTTON)  onEvent.Call(SM_MBUTTONDOWN, 0, 0);
  
  			if (clicks == 2)
  			{
  				if	(buttons == B_PRIMARY_MOUSE_BUTTON)   onEvent.Call(SM_LBUTTONDBLCLK, 0, 0);
  				else if (buttons == B_SECONDARY_MOUSE_BUTTON) onEvent.Call(SM_RBUTTONDBLCLK, 0, 0);
  				else if (buttons == B_TERTIARY_MOUSE_BUTTON)  onEvent.Call(SM_MBUTTONDBLCLK, 0, 0);
  			}
  
  			break;
  
  		case B_MOUSE_UP:
  			/* Send mouse button event.
  			 */
  			if	(buttons == B_PRIMARY_MOUSE_BUTTON)   onEvent.Call(SM_LBUTTONUP, 0, 0);
  			else if (buttons == B_SECONDARY_MOUSE_BUTTON) onEvent.Call(SM_RBUTTONUP, 0, 0);
  			else if (buttons == B_TERTIARY_MOUSE_BUTTON)  onEvent.Call(SM_MBUTTONUP, 0, 0);
  
  			break;
  
  		/* Keyboard messages:
  		 */
  		case B_KEY_DOWN:
  			{
  				int32		 raw   = 0;
  				const char	*bytes = NIL;
  				String		 string;
  
  				currentMessage.FindInt32("raw_char", &raw);
  				currentMessage.FindString("bytes", &bytes);
  
  				string.ImportFrom("UTF-8", bytes);
  
  				onEvent.Call(SM_CHAR, string[0], 0);
  
  				Input::Keyboard::UpdateKeyState(ConvertKey(raw, currentMessage), True);
  
  				onEvent.Call(SM_KEYDOWN, ConvertKey(raw, currentMessage), 0);
  			}
  
  			break;
  
  		case B_KEY_UP:
  			{
  				int32	 raw = 0;
  
  				currentMessage.FindInt32("raw_char", &raw);
  
  				Input::Keyboard::UpdateKeyState(ConvertKey(raw, currentMessage), False);
  
  				onEvent.Call(SM_KEYUP, ConvertKey(raw, currentMessage), 0);
  			}
  
  			break;
  
  		case B_MODIFIERS_CHANGED:
  			{
  				int32	 modifiers = 0;
  
  				currentMessage.FindInt32("modifiers", &modifiers);
  
  				Input::Keyboard::UpdateKeyState(Input::Keyboard::KeyShift,   modifiers & B_SHIFT_KEY);
  				Input::Keyboard::UpdateKeyState(Input::Keyboard::KeyControl, modifiers & B_CONTROL_KEY);
  				Input::Keyboard::UpdateKeyState(Input::Keyboard::KeyAlt,     modifiers & B_COMMAND_KEY);
  
  				if (modifiers & B_SHIFT_KEY)   onEvent.Call(SM_KEYDOWN, Input::Keyboard::KeyShift, 0);
  				if (modifiers & B_CONTROL_KEY) onEvent.Call(SM_KEYDOWN, Input::Keyboard::KeyControl, 0);
  				if (modifiers & B_COMMAND_KEY) onEvent.Call(SM_KEYDOWN, Input::Keyboard::KeyAlt, 0);
  			}
  
  			break;
  
  		/* Clipboard messages:
  		 */
  		case B_COPY:	   onEvent.Call(SM_KEYDOWN, 'C', 0); break;
  		case B_CUT:	   onEvent.Call(SM_KEYDOWN, 'X', 0); break;
  		case B_PASTE:	   onEvent.Call(SM_KEYDOWN, 'V', 0); break;
  		case B_SELECT_ALL: onEvent.Call(SM_KEYDOWN, 'A', 0); break;
  
  		case B_UNDO:	   onEvent.Call(SM_KEYDOWN, 'Z', 0); break;
  		case B_REDO:	   onEvent.Call(SM_KEYDOWN, 'Y', 0); break;
  
  		/* Paint messages:
  		 */
  		case B_PAINT:
  			{
  				BRect	 windowRect = wnd->Frame();
  
  				Point	 pos  = Point(windowRect.left, windowRect.top);
  				Size	 size = (Size(windowRect.Width(), windowRect.Height()) + Size(1, 1)) / fontSize;
  
  				if (drawSurface != NIL) drawSurface->SetSize(Size(windowRect.Width(), windowRect.Height()) + Size(1, 1));
  
  				onEvent.Call(SM_WINDOWMETRICS, ((pos.x	 + 32768) << 16) | (pos.y   + 32768),
  							       ((size.cx + 32768) << 16) | (size.cy + 32768));
  
  				updateRect = Rect(Point((unsigned(wParam) >> 16) - 32768, (unsigned(wParam) & 65535) - 32768),
  						   Size((unsigned(lParam) >> 16) - 32768, (unsigned(lParam) & 65535) - 32768) + Size(1, 1));
  
  				onEvent.Call(SM_PAINT, 0, 0);
  			}
  
  			break;
  
  		/* Window state change messages:
  		 */
  		case B_WINDOW_RESIZED:
  			{
  				BRect	 windowRect = wnd->Frame();
  
  				Point	 pos  =  Point(windowRect.left, windowRect.top);
  				Size	 size = (Size(windowRect.Width(), windowRect.Height()) + Size(1, 1)) / fontSize;
  
  				if (drawSurface != NIL) drawSurface->SetSize(Size(windowRect.Width(), windowRect.Height()) + Size(1, 1));
  
  				onEvent.Call(SM_WINDOWMETRICS, ((pos.x	 + 32768) << 16) | (pos.y   + 32768),
  							       ((size.cx + 32768) << 16) | (size.cy + 32768));
  
  				updateRect = Rect(Point(0, 0), Size(windowRect.Width(), windowRect.Height()) + Size(1, 1));
  
  				onEvent.Call(SM_PAINT, 0, 0);
  			}
  
  			break;
  
  		case B_WINDOW_MOVED:
  			{
  				BRect	 windowRect = wnd->Frame();
  
  				Point	 pos  = Point(windowRect.left, windowRect.top);
  				Size	 size = (Size(windowRect.Width(), windowRect.Height()) + Size(1, 1)) / fontSize;
  
  				onEvent.Call(SM_WINDOWMETRICS, ((pos.x	 + 32768) << 16) | (pos.y   + 32768),
  							       ((size.cx + 32768) << 16) | (size.cy + 32768));
  			}
  
  			return Success();
  
  		case B_WINDOW_ACTIVATED:
  			if (wParam == True)
  			{
  				focusWndId = id;
  
  				onEvent.Call(SM_GETFOCUS, 0, 0);
  			}
  			else
  			{
  				Input::Keyboard::ResetKeyState();
  
  				System::System::Sleep(50);
  
  				/* Get the window that now has the focus.
  				 */
  				Window	*focusWnd = NIL;
  
  				foreach (WindowHaiku *backend, windowBackends)
  				{
  					if (backend->wnd != NIL && backend->wnd->IsActive())
  					{
  						focusWnd = Window::GetWindow((Void *) backend->wnd);
  
  						break;
  					}
  				}
  
  				onEvent.Call(SM_LOSEFOCUS, focusWnd != NIL ? focusWnd->GetHandle() : -1, 0);
  			}
  
  			break;
  
  		/* Drag & drop messages:
  		 */
  		case B_SIMPLE_DATA:
  			{
  				BPoint	 cursorPos;
  
  				currentMessage.FindPoint("_drop_point_", &cursorPos);
  
  				cursorPos   = wnd->ConvertFromScreen(cursorPos);
  				dropMessage = &currentMessage;
  
  				onEvent.Call(SM_DROPFILES, cursorPos.x, cursorPos.y);
  			}
  
  			break;
  	}
  
  	return Success();
  }
  
  S::Int S::GUI::WindowHaiku::Open(const String &title, const Point &pos, const Size &size, Int iFlags)
  {
  	flags = iFlags;
  
  	Int	 windowLook  = B_TITLED_WINDOW_LOOK;
  	Int	 windowFeel  = B_NORMAL_WINDOW_FEEL;
  	Int	 windowFlags = 0;
  
  	if (flags & WF_NORESIZE	 )   windowFlags |= B_NOT_RESIZABLE;
  	if (flags & WF_NOTITLE	 )   windowLook	 ^= B_TITLED_WINDOW_LOOK;
  	if (flags & WF_THINBORDER) { windowFlags  = B_AVOID_FOCUS;
  				     windowLook	  = B_NO_BORDER_WINDOW_LOOK; }
  	if (flags & WF_TOPMOST	 )   windowFeel	  = B_FLOATING_ALL_WINDOW_FEEL;
  	if (flags & WF_MODAL	 )   windowFeel	  = B_MODAL_APP_WINDOW_FEEL;
  
  	wnd = new HaikuWindow(BRect(pos.x, pos.y, pos.x + Math::Round(size.cx * fontSize) + sizeModifier.cx - 1, pos.y + Math::Round(size.cy * fontSize) + sizeModifier.cy - 1), title, windowLook, windowFeel, windowFlags);
  
  	if (wnd != NIL)
  	{
  		view = wnd->ChildAt(0);
  
  		/* Send the Create message.
  		 */
  		ProcessSystemMessages(B_WINDOW_CREATED, 0, 0, BMessage());
  
  		/* Create drawing surface.
  		 */
  		if ((flags & WF_THINBORDER) || (flags & WF_NORESIZE)) drawSurface = new Surface(view, size * fontSize + sizeModifier);
  		else						      drawSurface = new Surface(view);
  
  		drawSurface->SetSize(size * fontSize + sizeModifier);
  
  		/* Set minimum and maximum size.
  		 */
  		if (!(flags & WF_NORESIZE || flags & WF_THINBORDER)) wnd->SetSizeLimits(Math::Round(minSize.cx * fontSize) + sizeModifier.cx - 1, Math::Round(maxSize.cx * fontSize) + sizeModifier.cx - 1,
  											Math::Round(minSize.cy * fontSize) + sizeModifier.cy - 1, Math::Round(maxSize.cy * fontSize) + sizeModifier.cy - 1);
  
  		return Success();
  	}
  
  	return Error();
  }
  
  S::Int S::GUI::WindowHaiku::Close()
  {
  	if (wnd == NIL) return Success();
  
  	/* Send the Destroy message.
  	 */
  	ProcessSystemMessages(B_WINDOW_DESTROYED, 0, 0, BMessage());
  
  	if (Window::nOfActiveWindows == 0)
  	{
  		BApplication	*app = Backends::BackendHaiku::GetApplication();
  
  		app->PostMessage(B_QUIT_REQUESTED);
  	}
  
  	/* Suspend the application lock to
  	 * allow the quit operation to finish.
  	 */
  	Int	 suspendCount = (wnd->Thread() != find_thread(NIL) ? Application::Lock::SuspendLock() : 0);
  
  	/* Destroy window.
  	 */
  	BWindow	*oldwnd = wnd;
  
  	oldwnd->Lock();
  
  	wnd  = NIL;
  	view = NIL;
  
  	oldwnd->Quit();
  
  	/* Resume the application lock.
  	 */
  	Application::Lock::ResumeLock(suspendCount);
  
  	/* Delete surface.
  	 */
  	if (drawSurface != NIL) delete drawSurface;
  
  	drawSurface = NIL;
  
  	return Success();
  }
  
  S::Int S::GUI::WindowHaiku::RequestClose()
  {
  	if (doClose.Call()) return Close();
  
  	return Success();
  }
  
  S::Bool S::GUI::WindowHaiku::IsModalWindowActive()
  {
  	/* Ignore modal windows if this is a topmost window.
  	 */
  	if (flags & WF_TOPMOST) return False;
  
  	/* Look for modal windows opened after ourselves.
  	 */
  	foreachreverse (WindowHaiku *backend, windowBackends)
  	{
  		if	(backend == this)				   return False;
  		else if (backend->wnd != NIL && backend->flags & WF_MODAL) return True;
  	}
  
  	return False;
  }
  
  S::Int S::GUI::WindowHaiku::SetTitle(const String &nTitle)
  {
  	if (wnd == NIL) return Error();
  
  	wnd->SetTitle(nTitle);
  
  	return Success();
  }
  
  const S::Array<S::String> &S::GUI::WindowHaiku::GetDroppedFiles() const
  {
  	if (dropMessage == NIL) return WindowBackend::GetDroppedFiles();
  
  	static Array<String>	 fileNames;
  
  	fileNames.RemoveAll();
  
  	/* Query number of files dropped.
  	 */
  	int32	 nOfFiles = 0;
  
  	dropMessage->GetInfo("refs", NIL, &nOfFiles);
  
  	/* Query dropped files.
  	 */
  	for (Int i = 0; i < nOfFiles; i++)
  	{
  		entry_ref	 ref;
  		BPath		 path;
  
  		dropMessage->FindRef("refs", i, &ref);
  
  		BEntry(&ref).GetPath(&path);
  
  		fileNames.Add(path.Path());
  	}
  
  	return fileNames;
  }
  
  S::Int S::GUI::WindowHaiku::SetMinimumSize(const Size &nMinSize)
  {
  	minSize = nMinSize;
  
  	if (wnd == NIL) return Success();
  
  	wnd->SetSizeLimits(Math::Round(minSize.cx * fontSize) + sizeModifier.cx - 1, Math::Round(maxSize.cx * fontSize) + sizeModifier.cx - 1,
  			   Math::Round(minSize.cy * fontSize) + sizeModifier.cy - 1, Math::Round(maxSize.cy * fontSize) + sizeModifier.cy - 1);
  
  	BRect	 windowRect = wnd->Frame();
  
  	SetMetrics(Point(windowRect.left, windowRect.top),
  		   Size(Math::Max(Math::Round((windowRect.right  - windowRect.left - sizeModifier.cx + 1) / fontSize), minSize.cx),
  			Math::Max(Math::Round((windowRect.bottom - windowRect.top  - sizeModifier.cy + 1) / fontSize), minSize.cy)));
  
  	return Success();
  }
  
  S::Int S::GUI::WindowHaiku::SetMaximumSize(const Size &nMaxSize)
  {
  	maxSize = nMaxSize;
  
  	if (wnd == NIL) return Success();
  
  	wnd->SetSizeLimits(Math::Round(minSize.cx * fontSize) + sizeModifier.cx - 1, Math::Round(maxSize.cx * fontSize) + sizeModifier.cx - 1,
  			   Math::Round(minSize.cy * fontSize) + sizeModifier.cy - 1, Math::Round(maxSize.cy * fontSize) + sizeModifier.cy - 1);
  
  	BRect	 windowRect = wnd->Frame();
  
  	SetMetrics(Point(windowRect.left, windowRect.top),
  		   Size(Math::Min(Math::Round((windowRect.right  - windowRect.left - sizeModifier.cx + 1) / fontSize), maxSize.cx),
  			Math::Min(Math::Round((windowRect.bottom - windowRect.top  - sizeModifier.cy + 1) / fontSize), maxSize.cy)));
  
  	return Success();
  }
  
  S::Int S::GUI::WindowHaiku::Show()
  {
  	if (wnd == NIL) return Success();
  
  	if (wnd->IsHidden())
  	{
  		while (wnd->LockWithTimeout(0) != B_OK) Application::Lock::ResumeLock(Application::Lock::SuspendLock());
  
  		wnd->Show();
  		wnd->Unlock();
  	}
  
  	return Success();
  }
  
  S::Int S::GUI::WindowHaiku::Hide()
  {
  	if (wnd == NIL) return Success();
  
  	if (!wnd->IsHidden())
  	{
  		while (wnd->LockWithTimeout(0) != B_OK) Application::Lock::ResumeLock(Application::Lock::SuspendLock());
  
  		wnd->Hide();
  		wnd->Unlock();
  	}
  
  	return Success();
  }
  
  S::Int S::GUI::WindowHaiku::SetMetrics(const Point &nPos, const Size &nSize)
  {
  	if (wnd == NIL) return Success();
  
  	/* If the window belongs to another thread, we
  	 * need to suspend our application lock in order
  	 * for the move and resize operations to finish.
  	 */
  	Int	 suspendCount = (wnd->Thread() != find_thread(NIL) ? Application::Lock::SuspendLock() : 0);
  
  	/* Set window metrics.
  	 */
  	wnd->MoveTo(nPos.x, nPos.y);
  	wnd->ResizeTo(Math::Round(nSize.cx * fontSize) + sizeModifier.cx - 1, Math::Round(nSize.cy * fontSize) + sizeModifier.cy - 1);
  
  	/* Resume the application lock.
  	 */
  	Application::Lock::ResumeLock(suspendCount);
  
  	return Success();
  }
  