packages feed

simple-pango-0.1.0.1: src/Graphics/Pango/Basic/LayoutObjects/PangoLayout/Internal.hsc

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE BlockArguments, LambdaCase, TupleSections #-}
{-# LANGUAGE PatternSynonyms, ViewPatterns #-}
{-# OPTIONS_GHC -Wall -fno-warn-tabs #-}

module Graphics.Pango.Basic.LayoutObjects.PangoLayout.Internal (
	-- * TYPE
	PangoLayout(..),
	PangoLayoutPrim(..), PangoLayoutST, PangoLayoutIO, mkPangoLayoutPrim,
	pangoLayoutFreeze, pangoLayoutThaw, pangoLayoutCopy,

	-- * CLASS
	-- ** PangoLayoutSetting
	PangoLayoutSetting, pangoLayoutSet, pangoLayoutGet,

	-- ** PangoLayoutInfo
	PangoLayoutInfo, pangoLayoutInfo,

	-- * FUNCTION
	pangoLayoutNew, pangoLayoutContextChanged,
	pangoLayoutSetMarkup, pangoLayoutSetMarkupWithAccel,

	pangoLayoutIndexToPos,
	pangoLayoutIndexToLineX,
	pangoLayoutXyToIndex,
	pangoLayoutGetCursorPos,
	pangoLayoutMoveCursorVisually, Dir(..),

	-- * SETTING

	-- ** Width and Height
	Width(..), Height(..),

	-- ** PangoWrapMode
	PangoWrapMode(..),
	pattern PangoWrapWord, pattern PangoWrapChar, pattern PangoWrapWordChar,

	-- ** PangoEllipsizeMode
	PangoEllipsizeMode(..),
	pattern PangoEllipsizeNone, pattern PangoEllipsizeStart,
	pattern PangoEllipsizeMiddle, pattern PangoEllipsizeEnd,

	-- ** Indent, Spacing, LineSpacing, Justify and AutoDir
	Indent(..), Spacing(..), LineSpacing(..), Justify(..), AutoDir(..),

	-- ** PangoAlignment
	PangoAlignment(..),
	pattern PangoAlignLeft, pattern PangoAlignCenter,
	pattern PangoAlignRight,

	-- ** SingleParagraphMode
	SingleParagraphMode(..),

	-- * INFO

	-- ** CharacterCount, IsWrapped, IsEllipsized and UnknownGlyphCount
	CharacterCount(..), IsWrapped(..), IsEllipsized(..), UnknownGlyphsCount(..),

	-- ** PangoLogAttrs
	PangoLogAttrs, pangoLogAttrsGetLogAttr, pangoLogAttrsGetSize,
	PangoLogAttr,
	pattern PangoLogAttr,
	pangoLogAttrIsLineBreak, pangoLogAttrIsMandatoryBreak,
	pangoLogAttrIsCharBreak, pangoLogAttrIsWhite,
	pangoLogAttrIsCursorPosition,
	pangoLogAttrIsWordStart, pangoLogAttrIsWordEnd,
	pangoLogAttrIsSentenceBoundary,
	pangoLogAttrIsSentenceStart, pangoLogAttrIsSentenceEnd,
	pangoLogAttrBackspaceDeleteCharacter, pangoLogAttrIsExpandableSpace,
	pangoLogAttrIsWordBoundary,

	-- ** LayoutSize, LayoutPixelSize, Baseline and LineCount
	LayoutSize(..), LayoutPixelSize(..), Baseline(..), LineCount(..),

	) where

import Foreign.Ptr
import Foreign.ForeignPtr hiding (newForeignPtr, addForeignPtrFinalizer)
import Foreign.Concurrent
import Foreign.Marshal
import Foreign.Storable
import Foreign.C.Types
import Foreign.C.String
import Foreign.C.String.Utf8
import Foreign.C.String.ForeignCStringLen
import Foreign.C.String.Misc
import Foreign.C.Enum
import Foreign.C.Struct
import Control.Monad.Primitive
import Data.Maybe
import Data.List
import Data.Bool
import Data.Word
import Data.Int
import Data.Char
import Data.Text.CString
import System.IO.Unsafe
import System.GLib.Bool
import System.GLib.GObject

import Graphics.Pango.Basic.GlyphStorage.Internal
import Graphics.Pango.Basic.Fonts.PangoFontDescription.Type
import Graphics.Pango.Basic.TextAttributes.Internal
import Graphics.Pango.LowLevel.Contexts.Internal
import Graphics.Pango.LowLevel.TabStops.Internal

import qualified Data.Text as T
import qualified Data.Text.Foreign as T

#include <pango/pango.h>
#include "pango_log_attr.h"

newtype PangoLayout = PangoLayout_ (ForeignPtr PangoLayout) deriving Show

foreign import ccall "pango_layout_copy" c_pango_layout_copy ::
	Ptr PangoLayout -> IO (Ptr PangoLayout)

foreign import ccall "g_object_unref" c_pango_layout_free ::
	Ptr PangoLayout -> IO ()

structPrim "PangoLayout" 'c_pango_layout_copy 'c_pango_layout_free [''Show]

mkPangoLayoutPrim :: Ptr PangoLayout -> IO (PangoLayoutPrim s)
mkPangoLayoutPrim p = PangoLayoutPrim <$> newForeignPtr p (c_g_object_unref p)

pangoLayoutNew :: PrimMonad m => PangoContext -> m (PangoLayoutPrim (PrimState m))
pangoLayoutNew (PangoContext fc) = unsafeIOToPrim
	$ mkPangoLayoutPrim =<< withForeignPtr fc c_pango_layout_new

foreign import ccall "pango_layout_new" c_pango_layout_new ::
	Ptr PangoContext -> IO (Ptr PangoLayout)

pangoLayoutContextChanged ::
	PrimMonad m => PangoLayoutPrim (PrimState m) -> m ()
pangoLayoutContextChanged (PangoLayoutPrim fl) = unsafeIOToPrim
	$ withForeignPtr fl c_pango_layout_context_changed

foreign import ccall "pango_layout_context_changed"
	c_pango_layout_context_changed ::
	Ptr PangoLayout -> IO ()

class PangoLayoutSetting s where
	pangoLayoutSet ::
		PrimMonad m => PangoLayoutPrim (PrimState m) -> s -> m ()
	pangoLayoutGet :: PangoLayout -> s

instance PangoLayoutSetting T.Text where
	pangoLayoutSet = (unsafeIOToPrim .) . pangoLayoutSetText
	pangoLayoutGet = pangoLayoutGetText

pangoLayoutSetText :: PangoLayoutPrim s -> T.Text -> IO ()
pangoLayoutSetText (PangoLayoutPrim fpl) s =
	withForeignPtr fpl \pl -> T.withCStringLen s \(cs, n) -> do
		cs' <- copyCString cs n
		addForeignPtrFinalizer fpl $ free cs'
		c_pango_layout_set_text pl cs' $ fromIntegral n

foreign import ccall "pango_layout_set_text" c_pango_layout_set_text ::
	Ptr PangoLayout -> CString -> CInt -> IO ()

pangoLayoutGetText :: PangoLayout -> T.Text
pangoLayoutGetText (PangoLayout_ fpl) = unsafePerformIO
	$ withForeignPtr fpl \pl -> peekCStringText =<< c_pango_layout_get_text pl

foreign import ccall "pango_layout_get_text" c_pango_layout_get_text ::
	Ptr PangoLayout -> IO CString

pangoLayoutSetMarkup ::
	PrimMonad m => PangoLayoutPrim (PrimState m) -> T.Text -> m ()
pangoLayoutSetMarkup (PangoLayoutPrim fpl) mu = unsafeIOToPrim
	$ withForeignPtr fpl \ppl -> T.withCStringLen mu \(cs, cl) ->
		c_pango_layout_set_markup ppl cs $ fromIntegral cl

foreign import ccall "pango_layout_set_markup"
	c_pango_layout_set_markup :: Ptr PangoLayout -> CString -> CInt -> IO ()

pangoLayoutSetMarkupWithAccel ::
	PrimMonad m => PangoLayoutPrim (PrimState m) -> T.Text -> Char -> m Char
pangoLayoutSetMarkupWithAccel (PangoLayoutPrim fpl) mu am = unsafeIOToPrim
	$ withForeignPtr fpl \ppl ->
		T.withCStringLen mu \(cs, cl) -> alloca \pa -> do
			c_pango_layout_set_markup_with_accel ppl cs (fromIntegral cl) (fromIntegral $ ord am) pa
			chr . fromIntegral <$> peek pa

foreign import ccall "pango_layout_set_markup_with_accel"
	c_pango_layout_set_markup_with_accel ::
	Ptr PangoLayout -> CString -> CInt ->
	#{type gunichar} -> Ptr #{type gunichar} -> IO ()

instance PangoLayoutSetting PangoTextAttrList where
	pangoLayoutSet = pangoLayoutSetTextAttributes
	pangoLayoutGet = pangoLayoutGetTextAttributes

pangoLayoutSetTextAttributes :: PrimMonad m =>
	PangoLayoutPrim (PrimState m) -> PangoTextAttrList -> m ()
pangoLayoutSetTextAttributes
	l@(PangoLayoutPrim fl) (PangoTextAttrList (cs, ln) al) = unsafeIOToPrim do
	withForeignPtr fl \pl -> withForeignPtr cs \cs' ->  c_pango_layout_set_text pl cs' $ fromIntegral ln
	pangoLayoutSetAttributes l al

pangoLayoutGetTextAttributes :: PangoLayout -> PangoTextAttrList
pangoLayoutGetTextAttributes l@(PangoLayout_ fl) = unsafePerformIO $ PangoTextAttrList
	<$> (copyToForeignCStringLen =<< toCStringLen =<< withForeignPtr fl c_pango_layout_get_text)
	<*> pangoLayoutGetAttributes l

pangoLayoutSetAttributes :: PangoLayoutPrim s -> PangoAttrList -> IO ()
pangoLayoutSetAttributes (PangoLayoutPrim fl) al =
	withForeignPtr fl \pl -> ($ c_pango_layout_set_attributes pl) case al of
		PangoAttrListNull -> ($ nullPtr)
		PangoAttrList fal -> withForeignPtr fal

foreign import ccall "pango_layout_set_attributes"
	c_pango_layout_set_attributes ::
	Ptr PangoLayout -> Ptr PangoAttrList -> IO ()

pangoLayoutGetAttributes :: PangoLayout -> IO PangoAttrList
pangoLayoutGetAttributes (PangoLayout_ fpl) =
	mkPangoAttrList =<< do
		p <- withForeignPtr fpl c_pango_layout_get_attributes
		p <$ c_pango_attr_list_ref p

foreign import ccall "pango_layout_get_attributes"
	c_pango_layout_get_attributes ::
	Ptr PangoLayout -> IO (Ptr PangoAttrList)

foreign import ccall "pango_attr_list_ref" c_pango_attr_list_ref ::
	Ptr PangoAttrList -> IO (Ptr PangoAttrList)

instance PangoLayoutSetting PangoFontDescriptionNullable where
	pangoLayoutSet = pangoLayoutSetFontDescription
	pangoLayoutGet = pangoLayoutGetFontDescription

pangoLayoutSetFontDescription :: PrimMonad m =>
	PangoLayoutPrim (PrimState m) -> PangoFontDescriptionNullable -> m ()
pangoLayoutSetFontDescription (PangoLayoutPrim fpl) fd = unsafeIOToPrim
	$ withForeignPtr fpl \pl -> case fd of
		PangoFontDescriptionNull -> c_pango_layout_set_font_description pl nullPtr
		PangoFontDescriptionNotNull ffd -> do
			addForeignPtrFinalizer fpl $ touchForeignPtr ffd
			withForeignPtr ffd $ c_pango_layout_set_font_description pl

foreign import ccall "pango_layout_set_font_description" c_pango_layout_set_font_description ::
	Ptr PangoLayout -> Ptr PangoFontDescription -> IO ()

pangoLayoutGetFontDescription :: PangoLayout -> PangoFontDescriptionNullable
pangoLayoutGetFontDescription (PangoLayout_ fpl) = unsafePerformIO
	$ mkPangoFontDescriptionNullable =<< withForeignPtr fpl \ppl ->
		c_pango_font_description_copy
			=<< c_pango_layout_get_font_description ppl

foreign import ccall "pango_layout_get_font_description" c_pango_layout_get_font_description ::
	Ptr PangoLayout -> IO (Ptr PangoFontDescription)

foreign import ccall "pango_font_description_copy" c_pango_font_description_copy ::
	Ptr PangoFontDescription -> IO (Ptr PangoFontDescription)

data Width = WidthDefault | Width PangoFixed deriving Show

getWidth :: Width -> CInt
getWidth = \case WidthDefault -> - 1; Width w -> toCInt w

width :: CInt -> Width
width = \case - 1 -> WidthDefault; w -> Width $ fromCInt w

instance PangoLayoutSetting Width where
	pangoLayoutSet l = pangoLayoutSetWidth l . getWidth
	pangoLayoutGet l = width $ pangoLayoutGetWidth l

data Height = HeightDefault | Height PangoFixed | LinesPerParagraph CInt
	deriving Show

toHeight :: CInt -> Height
toHeight n
	| n < 0 = LinesPerParagraph $ - n
	| otherwise = Height $ fromCInt n

fromHeight :: Height -> CInt
fromHeight = \case
	HeightDefault -> - 1
	Height d -> toCInt d
	LinesPerParagraph n -> - n

instance PangoLayoutSetting Height where
	pangoLayoutSet l = pangoLayoutSetHeight l . fromHeight
	pangoLayoutGet l = toHeight $ pangoLayoutGetHeight l

pangoLayoutSetWidth, pangoLayoutSetHeight ::
	PrimMonad m => PangoLayoutPrim (PrimState m) -> CInt -> m ()
pangoLayoutSetWidth (PangoLayoutPrim fl) w = unsafeIOToPrim
	$ withForeignPtr fl \pl -> c_pango_layout_set_width pl w

pangoLayoutSetHeight (PangoLayoutPrim fl) h = unsafeIOToPrim
	$ withForeignPtr fl \pl -> c_pango_layout_set_height pl h

pangoLayoutGetWidth, pangoLayoutGetHeight :: PangoLayout -> CInt
pangoLayoutGetWidth (PangoLayout_ fl) = unsafePerformIO
	$ withForeignPtr fl c_pango_layout_get_width

pangoLayoutGetHeight (PangoLayout_ fl) = unsafePerformIO
	$ withForeignPtr fl c_pango_layout_get_height

foreign import ccall "pango_layout_set_width" c_pango_layout_set_width ::
	Ptr PangoLayout -> CInt -> IO ()

foreign import ccall "pango_layout_get_width" c_pango_layout_get_width ::
	Ptr PangoLayout -> IO CInt

foreign import ccall "pango_layout_set_height" c_pango_layout_set_height ::
	Ptr PangoLayout -> CInt -> IO ()

foreign import ccall "pango_layout_get_height" c_pango_layout_get_height ::
	Ptr PangoLayout -> IO CInt

enum "PangoWrapMode" ''#{type PangoWrapMode} [''Show] [
	("PangoWrapWord", #{const PANGO_WRAP_WORD}),
	("PangoWrapChar", #{const PANGO_WRAP_CHAR}),
	("PangoWrapWordChar", #{const PANGO_WRAP_WORD_CHAR}) ]

instance PangoLayoutSetting PangoWrapMode where
	pangoLayoutSet = pangoLayoutSetWrap
	pangoLayoutGet = pangoLayoutGetWrap

pangoLayoutSetWrap ::
	PrimMonad m => PangoLayoutPrim (PrimState m) -> PangoWrapMode -> m ()
pangoLayoutSetWrap (PangoLayoutPrim fl) (PangoWrapMode wm) = unsafeIOToPrim
	$ withForeignPtr fl \pl -> c_pango_layout_set_wrap pl wm

pangoLayoutGetWrap :: PangoLayout -> PangoWrapMode
pangoLayoutGetWrap (PangoLayout_ fl) = unsafePerformIO $ PangoWrapMode
	<$> withForeignPtr fl c_pango_layout_get_wrap

foreign import ccall "pango_layout_set_wrap" c_pango_layout_set_wrap ::
	Ptr PangoLayout -> #{type PangoWrapMode} -> IO ()

foreign import ccall "pango_layout_get_wrap" c_pango_layout_get_wrap ::
	Ptr PangoLayout -> IO #{type PangoWrapMode}

enum "PangoEllipsizeMode" ''#{type PangoEllipsizeMode} [''Show] [
	("PangoEllipsizeNone", #{const PANGO_ELLIPSIZE_NONE}),
	("PangoEllipsizeStart", #{const PANGO_ELLIPSIZE_START}),
	("PangoEllipsizeMiddle", #{const PANGO_ELLIPSIZE_MIDDLE}),
	("PangoEllipsizeEnd", #{const PANGO_ELLIPSIZE_END}) ]

instance PangoLayoutSetting PangoEllipsizeMode where
	pangoLayoutSet = pangoLayoutSetEllipsize
	pangoLayoutGet = pangoLayoutGetEllipsize

pangoLayoutSetEllipsize :: PrimMonad m =>
	PangoLayoutPrim (PrimState m) -> PangoEllipsizeMode -> m ()
pangoLayoutSetEllipsize (PangoLayoutPrim fpl) (PangoEllipsizeMode pem) = unsafeIOToPrim
	$ withForeignPtr fpl \pl -> c_pango_layout_set_ellipsize pl pem

pangoLayoutGetEllipsize :: PangoLayout -> PangoEllipsizeMode
pangoLayoutGetEllipsize (PangoLayout_ fl) = unsafePerformIO $ PangoEllipsizeMode
	<$> withForeignPtr fl c_pango_layout_get_ellipsize

foreign import ccall "pango_layout_set_ellipsize" c_pango_layout_set_ellipsize ::
	Ptr PangoLayout -> #{type PangoEllipsizeMode} -> IO ()

foreign import ccall "pango_layout_get_ellipsize" c_pango_layout_get_ellipsize ::
	Ptr PangoLayout -> IO #{type PangoEllipsizeMode}

newtype Indent = Indent { getIndent :: PangoFixed } deriving Show

instance PangoLayoutSetting Indent where
	pangoLayoutSet l = pangoLayoutSetIndent l . toCInt . getIndent
	pangoLayoutGet l = Indent . fromCInt $ pangoLayoutGetIndent l

pangoLayoutSetIndent ::
	PrimMonad m => PangoLayoutPrim (PrimState m) -> CInt -> m ()
pangoLayoutSetIndent (PangoLayoutPrim fl) idt = unsafeIOToPrim
	$ withForeignPtr fl \pl -> c_pango_layout_set_indent pl idt

pangoLayoutGetIndent :: PangoLayout -> CInt
pangoLayoutGetIndent (PangoLayout_ fl) = unsafePerformIO
	$ withForeignPtr fl c_pango_layout_get_indent

foreign import ccall "pango_layout_set_indent" c_pango_layout_set_indent ::
	Ptr PangoLayout -> CInt -> IO ()

foreign import ccall "pango_layout_get_indent" c_pango_layout_get_indent ::
	Ptr PangoLayout -> IO CInt

newtype Spacing = Spacing { getSpacing :: PangoFixed } deriving Show

instance PangoLayoutSetting Spacing where
	pangoLayoutSet l = pangoLayoutSetSpacing l . toCInt . getSpacing
	pangoLayoutGet l = Spacing . fromCInt $ pangoLayoutGetSpacing l

pangoLayoutSetSpacing ::
	PrimMonad m => PangoLayoutPrim (PrimState m) -> CInt -> m ()
pangoLayoutSetSpacing (PangoLayoutPrim fl) sp = unsafeIOToPrim
	$ withForeignPtr fl \pl -> c_pango_layout_set_spacing pl sp

pangoLayoutGetSpacing :: PangoLayout -> CInt
pangoLayoutGetSpacing (PangoLayout_ fl) = unsafePerformIO
	$ withForeignPtr fl c_pango_layout_get_spacing

foreign import ccall "pango_layout_set_spacing" c_pango_layout_set_spacing ::
	Ptr PangoLayout -> CInt -> IO ()

foreign import ccall "pango_layout_get_spacing" c_pango_layout_get_spacing ::
	Ptr PangoLayout -> IO CInt

newtype LineSpacing = LineSpacing { getLineSpacing :: CFloat } deriving Show

instance PangoLayoutSetting LineSpacing where
	pangoLayoutSet l = pangoLayoutSetLineSpacing l . getLineSpacing
	pangoLayoutGet l = LineSpacing $ pangoLayoutGetLineSpacing l

pangoLayoutSetLineSpacing ::
	PrimMonad m => PangoLayoutPrim (PrimState m) -> CFloat -> m ()
pangoLayoutSetLineSpacing (PangoLayoutPrim fpl) fct = unsafeIOToPrim
	$ withForeignPtr fpl \pl -> c_pango_layout_set_line_spacing pl fct

pangoLayoutGetLineSpacing :: PangoLayout -> CFloat
pangoLayoutGetLineSpacing (PangoLayout_ fl) = unsafePerformIO
	$ withForeignPtr fl c_pango_layout_get_line_spacing

foreign import ccall "pango_layout_set_line_spacing" c_pango_layout_set_line_spacing ::
	Ptr PangoLayout -> CFloat -> IO ()

foreign import ccall "pango_layout_get_line_spacing" c_pango_layout_get_line_spacing ::
	Ptr PangoLayout -> IO CFloat

newtype Justify = Justify { getJustify :: Bool } deriving Show

instance PangoLayoutSetting Justify where
	pangoLayoutSet l = pangoLayoutSetJustify l . getJustify
	pangoLayoutGet l = Justify $ pangoLayoutGetJustify l

pangoLayoutSetJustify ::
	PrimMonad m => PangoLayoutPrim (PrimState m) -> Bool -> m ()
pangoLayoutSetJustify (PangoLayoutPrim fl) b = unsafeIOToPrim $ withForeignPtr fl \pl ->
	c_pango_layout_set_justify pl $ bool #{const FALSE} #{const TRUE} b

pangoLayoutGetJustify :: PangoLayout -> Bool
pangoLayoutGetJustify (PangoLayout_ fl) = unsafePerformIO
	$ (<$> withForeignPtr fl c_pango_layout_get_justify) \case
		#{const FALSE} -> False
		#{const TRUE} -> True
		_ -> error "never occur"

foreign import ccall "pango_layout_set_justify" c_pango_layout_set_justify ::
	Ptr PangoLayout -> #{type gboolean} -> IO ()

foreign import ccall "pango_layout_get_justify" c_pango_layout_get_justify ::
	Ptr PangoLayout -> IO #{type gboolean}

newtype AutoDir = AutoDir { getAutoDir :: Bool } deriving Show

instance PangoLayoutSetting AutoDir where
	pangoLayoutSet l = pangoLayoutSetAutoDir l . getAutoDir
	pangoLayoutGet l = AutoDir $ pangoLayoutGetAutoDir l

pangoLayoutSetAutoDir ::
	PrimMonad m => PangoLayoutPrim (PrimState m) -> Bool -> m ()
pangoLayoutSetAutoDir (PangoLayoutPrim fl) b = unsafeIOToPrim $ withForeignPtr fl \pl ->
	c_pango_layout_set_auto_dir pl $ bool #{const FALSE} #{const TRUE} b

pangoLayoutGetAutoDir :: PangoLayout -> Bool
pangoLayoutGetAutoDir (PangoLayout_ fl) = unsafePerformIO
	$ (<$> withForeignPtr fl c_pango_layout_get_auto_dir) \case
		#{const FALSE} -> False
		#{const TRUE} -> True
		_ -> error "never occur"

foreign import ccall "pango_layout_set_auto_dir" c_pango_layout_set_auto_dir ::
	Ptr PangoLayout -> #{type gboolean} -> IO ()

foreign import ccall "pango_layout_get_auto_dir" c_pango_layout_get_auto_dir ::
	Ptr PangoLayout -> IO #{type gboolean}

enum "PangoAlignment" ''#{type PangoAlignment} [''Show] [
	("PangoAlignLeft", #{const PANGO_ALIGN_LEFT}),
	("PangoAlignCenter", #{const PANGO_ALIGN_CENTER}),
	("PangoAlignRight", #{const PANGO_ALIGN_RIGHT}) ]

instance PangoLayoutSetting PangoAlignment where
	pangoLayoutSet = pangoLayoutSetAlignment
	pangoLayoutGet = pangoLayoutGetAlignment

pangoLayoutSetAlignment ::
	PrimMonad m => PangoLayoutPrim (PrimState m) -> PangoAlignment -> m ()
pangoLayoutSetAlignment (PangoLayoutPrim fpl) (PangoAlignment pa) = unsafeIOToPrim
	$ withForeignPtr fpl \pl -> c_pango_layout_set_alignment pl pa

pangoLayoutGetAlignment :: PangoLayout -> PangoAlignment
pangoLayoutGetAlignment (PangoLayout_ fl) = unsafePerformIO
	$ PangoAlignment <$> withForeignPtr fl c_pango_layout_get_alignment

foreign import ccall "pango_layout_set_alignment" c_pango_layout_set_alignment ::
	Ptr PangoLayout -> #{type PangoAlignment} -> IO ()

foreign import ccall "pango_layout_get_alignment" c_pango_layout_get_alignment ::
	Ptr PangoLayout -> IO #{type PangoAlignment}

instance PangoLayoutSetting PangoTabArrayNullable where
	pangoLayoutSet = pangoLayoutSetTabs
	pangoLayoutGet = pangoLayoutGetTabs

pangoLayoutSetTabs ::
	PrimMonad m => PangoLayoutPrim (PrimState m) -> PangoTabArrayNullable -> m ()
pangoLayoutSetTabs (PangoLayoutPrim fl) ta = unsafeIOToPrim
	$ withForeignPtr fl \pl -> case ta of
		PangoTabArrayNull -> c_pango_layout_set_tabs pl nullPtr
		PangoTabArrayNotNull fta -> do
			addForeignPtrFinalizer fl $ touchForeignPtr fta
			withForeignPtr fta $ c_pango_layout_set_tabs pl

pangoLayoutGetTabs :: PangoLayout -> PangoTabArrayNullable
pangoLayoutGetTabs (PangoLayout_ fl) = unsafePerformIO
	$ makePangoTabArrayNullable =<< withForeignPtr fl c_pango_layout_get_tabs

foreign import ccall "pango_layout_set_tabs" c_pango_layout_set_tabs ::
	Ptr PangoLayout -> Ptr PangoTabArray -> IO ()

foreign import ccall "pango_layout_get_tabs" c_pango_layout_get_tabs ::
	Ptr PangoLayout -> IO (Ptr PangoTabArray)

newtype SingleParagraphMode =
	SingleParagraphMode { getSingleParagraphMode :: Bool } deriving Show

instance PangoLayoutSetting SingleParagraphMode where
	pangoLayoutSet l =
		pangoLayoutSetSingleParagraphMode l . getSingleParagraphMode
	pangoLayoutGet l =
		SingleParagraphMode $ pangoLayoutGetSingleParagraphMode l

pangoLayoutSetSingleParagraphMode ::
	PrimMonad m => PangoLayoutPrim (PrimState m) -> Bool -> m ()
pangoLayoutSetSingleParagraphMode (PangoLayoutPrim fl) spm = unsafeIOToPrim
	$ withForeignPtr fl \pl ->
		c_pango_layout_set_single_paragraph_mode pl (boolToGboolean spm)

pangoLayoutGetSingleParagraphMode :: PangoLayout -> Bool
pangoLayoutGetSingleParagraphMode (PangoLayout_ fl) = unsafePerformIO
	$ (<$> withForeignPtr fl c_pango_layout_get_single_paragraph_mode) \case
		#{const FALSE} -> False; #{const TRUE} -> True
		_ -> error "never occur"

foreign import ccall "pango_layout_set_single_paragraph_mode"
	c_pango_layout_set_single_paragraph_mode ::
	Ptr PangoLayout -> #{type gboolean} -> IO ()

foreign import ccall "pango_layout_get_single_paragraph_mode"
	c_pango_layout_get_single_paragraph_mode ::
	Ptr PangoLayout -> IO #{type gboolean}

class PangoLayoutInfo i where pangoLayoutInfo :: PangoLayout -> i

newtype CharacterCount = CharacterCount CInt deriving Show

instance PangoLayoutInfo CharacterCount where
	pangoLayoutInfo = unsafePerformIO . (CharacterCount <$>) . pangoLayoutGetCharacterCount

pangoLayoutGetCharacterCount :: PangoLayout -> IO CInt
pangoLayoutGetCharacterCount (PangoLayout_ fpl) =
	withForeignPtr fpl c_pango_layout_get_character_count

foreign import ccall "pango_layout_get_character_count"
	c_pango_layout_get_character_count :: Ptr PangoLayout -> IO CInt

newtype IsEllipsized = IsEllipsized Bool deriving Show

instance PangoLayoutInfo IsEllipsized where
	pangoLayoutInfo = unsafePerformIO . (IsEllipsized <$>) . pangoLayoutIsEllipsized

pangoLayoutIsEllipsized :: PangoLayout -> IO Bool
pangoLayoutIsEllipsized (PangoLayout_ fl) =
	(<$> withForeignPtr fl c_pango_layout_is_ellipsized) \case
		#{const FALSE} -> False
		#{const TRUE} -> True
		_ -> error "never occur"

foreign import ccall "pango_layout_is_ellipsized"
	c_pango_layout_is_ellipsized :: Ptr PangoLayout -> IO #{type gboolean}

newtype IsWrapped = IsWrapped Bool deriving Show

instance PangoLayoutInfo IsWrapped where
	pangoLayoutInfo = unsafePerformIO . (IsWrapped <$>) . pangoLayoutIsWrapped

pangoLayoutIsWrapped :: PangoLayout -> IO Bool
pangoLayoutIsWrapped (PangoLayout_ fl) =
	(<$> withForeignPtr fl c_pango_layout_is_wrapped) \case
		#{const FALSE} -> False
		#{const TRUE} -> True
		_ -> error "never occur"

foreign import ccall "pango_layout_is_wrapped" c_pango_layout_is_wrapped ::
	Ptr PangoLayout -> IO #{type gboolean}

newtype UnknownGlyphsCount = UnknownGlyphsCount CInt deriving Show

instance PangoLayoutInfo UnknownGlyphsCount where
	pangoLayoutInfo = unsafePerformIO . (UnknownGlyphsCount <$>) . pangoLayoutGetUnknownGlyphsCount

pangoLayoutGetUnknownGlyphsCount :: PangoLayout -> IO CInt
pangoLayoutGetUnknownGlyphsCount (PangoLayout_ fpl) =
	withForeignPtr fpl c_pango_layout_get_unknown_glyphs_count

foreign import ccall "pango_layout_get_unknown_glyphs_count"
	c_pango_layout_get_unknown_glyphs_count :: Ptr PangoLayout -> IO CInt

data PangoLogAttr = PangoLogAttr_ (ForeignPtr PangoLogAttr) Int deriving Show
data PangoLogAttrStruct

pattern PangoLogAttr ::
	Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool ->
	Bool -> Bool -> Bool -> Bool -> PangoLogAttr
pattern PangoLogAttr { 
	pangoLogAttrIsLineBreak,
	pangoLogAttrIsMandatoryBreak,
	pangoLogAttrIsCharBreak,
	pangoLogAttrIsWhite,
	pangoLogAttrIsCursorPosition,
	pangoLogAttrIsWordStart,
	pangoLogAttrIsWordEnd,
	pangoLogAttrIsSentenceBoundary,
	pangoLogAttrIsSentenceStart,
	pangoLogAttrIsSentenceEnd,
	pangoLogAttrBackspaceDeleteCharacter,
	pangoLogAttrIsExpandableSpace,
	pangoLogAttrIsWordBoundary } <- (pangoLogAttrExpand -> PangoLogAttrExpand
		pangoLogAttrIsLineBreak
		pangoLogAttrIsMandatoryBreak
		pangoLogAttrIsCharBreak
		pangoLogAttrIsWhite
		pangoLogAttrIsCursorPosition
		pangoLogAttrIsWordStart
		pangoLogAttrIsWordEnd
		pangoLogAttrIsSentenceBoundary
		pangoLogAttrIsSentenceStart
		pangoLogAttrIsSentenceEnd
		pangoLogAttrBackspaceDeleteCharacter
		pangoLogAttrIsExpandableSpace
		pangoLogAttrIsWordBoundary)

data PangoLogAttrExpand = PangoLogAttrExpand {
	pangoLogAttrExpandIsLineBreak :: Bool,
	pangoLogAttrExpandIsMandatoryBreak :: Bool,
	pangoLogAttrExpandIsCharBreak :: Bool,
	pangoLogAttrExpandIsWhite :: Bool,
	pangoLogAttrExpandIsCursorPosition :: Bool,
	pangoLogAttrExpandIsWordStart :: Bool,
	pangoLogAttrExpandIsWordEnd :: Bool,
	pangoLogAttrExpandIsSentenceBoundary :: Bool,
	pangoLogAttrExpandIsSentenceStart :: Bool,
	pangoLogAttrExpandIsSentenceEnd :: Bool,
	pangoLogAttrExpandBackspaceDeletesCharacter :: Bool,
	pangoLogAttrExpandIsExpandableSpace :: Bool,
	pangoLogAttrExpandIsWordBoundary :: Bool
	} deriving Show

pangoLogAttrExpand :: PangoLogAttr -> PangoLogAttrExpand
pangoLogAttrExpand (PangoLogAttr_ fb i) = unsafePerformIO
	$ withForeignPtr fb \pb_ -> let pb = pb_ `plusPtr` (i * #{size PangoLogAttr}) in
		allocaBytes #{size PangoLogAttrStr} \ps -> do
			c_pango_log_attr_to_struct pb ps
			PangoLogAttrExpand
				<$> #{peek PangoLogAttrStr, is_line_break} ps
				<*> #{peek PangoLogAttrStr, is_mandatory_break} ps
				<*> #{peek PangoLogAttrStr, is_char_break} ps
				<*> #{peek PangoLogAttrStr, is_white} ps
				<*> #{peek PangoLogAttrStr, is_cursor_position} ps
				<*> #{peek PangoLogAttrStr, is_word_start} ps
				<*> #{peek PangoLogAttrStr, is_word_end} ps
				<*> #{peek PangoLogAttrStr, is_sentence_boundary} ps
				<*> #{peek PangoLogAttrStr, is_sentence_start} ps
				<*> #{peek PangoLogAttrStr, is_sentence_end} ps
				<*> #{peek PangoLogAttrStr, backspace_deletes_character} ps
				<*> #{peek PangoLogAttrStr, is_expandable_space} ps
				<*> #{peek PangoLogAttrStr, is_word_boundary} ps

foreign import ccall "pango_log_attr_to_struct" c_pango_log_attr_to_struct ::
	Ptr PangoLogAttr -> Ptr PangoLogAttrStruct -> IO ()

instance PangoLayoutInfo PangoLogAttrs where
	pangoLayoutInfo = pangoLayoutGetLogAttrs

data PangoLogAttrs = PangoLogAttrs (ForeignPtr PangoLogAttr) CInt deriving Show

mkPangoLogAttrs :: Ptr PangoLogAttr -> CInt -> IO PangoLogAttrs
mkPangoLogAttrs p n =
	(`PangoLogAttrs` n) <$> newForeignPtr p (c_g_free_pango_log_attr p)

foreign import ccall "g_free" c_g_free_pango_log_attr ::
	Ptr PangoLogAttr -> IO ()

pangoLayoutGetLogAttrs :: PangoLayout -> PangoLogAttrs
pangoLayoutGetLogAttrs (PangoLayout_ fl) = unsafePerformIO
	$ withForeignPtr fl \pl -> alloca \plas -> alloca \pn -> do
		c_pango_layout_get_log_attrs pl plas pn
		uncurry mkPangoLogAttrs =<< (,) <$> peek plas <*> peek pn

foreign import ccall "pango_layout_get_log_attrs" c_pango_layout_get_log_attrs ::
	Ptr PangoLayout -> Ptr (Ptr PangoLogAttr) -> Ptr CInt -> IO ()

pangoLogAttrsGetSize :: PangoLogAttrs -> Int
pangoLogAttrsGetSize (PangoLogAttrs _ sz) = fromIntegral sz

pangoLogAttrsGetLogAttr :: PangoLogAttrs -> Int -> Maybe PangoLogAttr
pangoLogAttrsGetLogAttr (PangoLogAttrs fla sz) i
	| 0 <= i && i < fromIntegral sz = Just $ PangoLogAttr_ fla i
	| otherwise = Nothing

instance PangoLayoutInfo Extents where
	pangoLayoutInfo = unsafePerformIO . (uncurry Extents <$>) . pangoLayoutGetExtents

pangoLayoutGetExtents :: PangoLayout -> IO (PangoRectangleFixed, PangoRectangleFixed)
pangoLayoutGetExtents (PangoLayout_ fpl) = withForeignPtr fpl \pl -> do
	irct <- mallocBytes #{size PangoRectangle}
	lrct <- mallocBytes #{size PangoRectangle}
	c_pango_layout_get_extents pl irct lrct
	(,)	<$> (PangoRectangleFixed_ <$> newForeignPtr irct (free irct))
		<*> (PangoRectangleFixed_ <$> newForeignPtr lrct (free lrct))

foreign import ccall "pango_layout_get_extents" c_pango_layout_get_extents ::
	Ptr PangoLayout -> Ptr PangoRectangleFixed -> Ptr PangoRectangleFixed -> IO ()

instance PangoLayoutInfo PixelExtents where
	pangoLayoutInfo = unsafePerformIO . (uncurry PixelExtents <$>) . pangoLayoutGetPixelExtents

pangoLayoutGetPixelExtents :: PangoLayout -> IO (PangoRectanglePixel, PangoRectanglePixel)
pangoLayoutGetPixelExtents (PangoLayout_ fpl) =
	withForeignPtr fpl \pl -> do
		irct <- mallocBytes #{size PangoRectangle}
		lrct <- mallocBytes #{size PangoRectangle}
		c_pango_layout_get_pixel_extents pl irct lrct
		(,)	<$> (PangoRectanglePixel_ <$> newForeignPtr irct (free irct))
			<*> (PangoRectanglePixel_ <$> newForeignPtr lrct (free lrct))

foreign import ccall "pango_layout_get_pixel_extents" c_pango_layout_get_pixel_extents ::
	Ptr PangoLayout -> Ptr PangoRectanglePixel -> Ptr PangoRectanglePixel -> IO ()

data LayoutSize = LayoutSize {
	layoutSizeWidth :: PangoFixed, layoutSizeHeight :: PangoFixed }
	deriving Show

instance PangoLayoutInfo LayoutSize where
	pangoLayoutInfo = unsafePerformIO . (uncurry LayoutSize <$>) . pangoLayoutGetSize

pangoLayoutGetSize :: PangoLayout -> IO (PangoFixed, PangoFixed)
pangoLayoutGetSize (PangoLayout_ fpl) =
	withForeignPtr fpl \pl -> alloca \pw -> alloca \ph -> do
		c_pango_layout_get_size pl pw ph
		(\w h -> (fromCInt w, fromCInt h)) <$> peek pw <*> peek ph

foreign import ccall "pango_layout_get_size" c_pango_layout_get_size ::
	Ptr PangoLayout -> Ptr CInt -> Ptr CInt -> IO ()

data LayoutPixelSize = LayoutPixelSize {
	layoutPixelSizeWidth :: CInt, layoutPixelSizeHeight :: CInt }
	deriving Show

instance PangoLayoutInfo LayoutPixelSize where
	pangoLayoutInfo = unsafePerformIO . (uncurry LayoutPixelSize <$>) . pangoLayoutGetPixelSize

pangoLayoutGetPixelSize :: PangoLayout -> IO (CInt, CInt)
pangoLayoutGetPixelSize (PangoLayout_ fpl) =
	withForeignPtr fpl \pl -> alloca \w -> alloca \h -> do
		c_pango_layout_get_pixel_size pl w h
		(,) <$> peek w <*> peek h

foreign import ccall "pango_layout_get_pixel_size" c_pango_layout_get_pixel_size ::
	Ptr PangoLayout -> Ptr CInt -> Ptr CInt -> IO ()

newtype Baseline = Baseline PangoFixed deriving Show

instance PangoLayoutInfo Baseline where
	pangoLayoutInfo = unsafePerformIO . (Baseline . fromCInt <$>) . pangoLayoutGetBaseline

pangoLayoutGetBaseline :: PangoLayout -> IO CInt
pangoLayoutGetBaseline (PangoLayout_ fpl) =
	withForeignPtr fpl c_pango_layout_get_baseline

foreign import ccall "pango_layout_get_baseline" c_pango_layout_get_baseline ::
	Ptr PangoLayout -> IO CInt

newtype LineCount = LineCount CInt deriving Show

instance PangoLayoutInfo LineCount where
	pangoLayoutInfo = unsafePerformIO . (LineCount <$>) . pangoLayoutGetLineCount

pangoLayoutGetLineCount :: PangoLayout -> IO CInt
pangoLayoutGetLineCount (PangoLayout_ fpl) =
	withForeignPtr fpl c_pango_layout_get_line_count

foreign import ccall "pango_layout_get_line_count" c_pango_layout_get_line_count ::
	Ptr PangoLayout -> IO CInt

pangoLayoutIndexToPos :: PangoLayout -> Int -> IO (Maybe PangoRectangleFixed)
pangoLayoutIndexToPos (PangoLayout_ fl) idx = withForeignPtr fl \pl -> do
	pos <- mallocBytes #{size PangoRectangle}
	t <- c_pango_layout_get_text pl
	is <- byteIndices =<< toCStringLen t
	case is `maybeIndex` idx of
		Nothing -> pure Nothing
		Just i -> do
			c_pango_layout_index_to_pos pl (fromIntegral i) pos
			Just . PangoRectangleFixed_ <$> newForeignPtr pos (free pos)

maybeIndex :: [a] -> Int -> Maybe a
maybeIndex _ i | i < 0 = Nothing
maybeIndex [] _ = Nothing
maybeIndex (x : _) 0 = Just x
maybeIndex (_ : xs) i = maybeIndex xs (i - 1)

foreign import ccall "pango_layout_index_to_pos" c_pango_layout_index_to_pos ::
	Ptr PangoLayout -> CInt -> Ptr PangoRectangleFixed -> IO ()

pangoLayoutIndexToLineX :: PangoLayout -> Int -> Bool -> IO (Maybe (CInt, PangoFixed))
pangoLayoutIndexToLineX (PangoLayout_ fpl) idx tr =
	withForeignPtr fpl \pl -> alloca \ln -> alloca \xpos -> do
		t <- c_pango_layout_get_text pl
		is <- byteIndices =<< toCStringLen t
		case is `maybeIndex` idx of
			Nothing -> pure Nothing
			Just i -> do
				c_pango_layout_index_to_line_x pl (fromIntegral i) (boolToGboolean tr) ln xpos
				Just <$> ((,) <$> peek ln <*> (fromCInt <$> peek xpos))

foreign import ccall "pango_layout_index_to_line_x"
	c_pango_layout_index_to_line_x ::
	Ptr PangoLayout -> CInt -> #{type gboolean} -> Ptr CInt -> Ptr CInt -> IO ()

pangoLayoutXyToIndex :: PangoLayout -> PangoFixed -> PangoFixed -> IO (Int, CInt, Bool)
pangoLayoutXyToIndex (PangoLayout_ fpl) x_ y_ =
	withForeignPtr fpl \pl -> alloca \idx -> alloca \tr -> do
		t <- c_pango_layout_get_text pl
		is <- byteIndices =<< toCStringLen t
		isd <- c_pango_layout_xy_to_index pl x y idx tr
		(,,) <$> (fromJust . (`elemIndex` is) . fromIntegral <$> peek idx) <*> peek tr <*> pure (gbooleanToBool isd)
	where
	[x, y] = toCInt <$> [x_, y_]

foreign import ccall "pango_layout_xy_to_index" c_pango_layout_xy_to_index ::
	Ptr PangoLayout -> CInt -> CInt -> Ptr CInt -> Ptr CInt -> IO #type gboolean

pangoLayoutGetCursorPos :: PangoLayout -> Int -> IO (Maybe (PangoRectangleFixed, PangoRectangleFixed))
pangoLayoutGetCursorPos (PangoLayout_ fpl) idx = withForeignPtr fpl \pl -> do
	spos <- mallocBytes #{size PangoRectangle}
	wpos <- mallocBytes #{size PangoRectangle}
	t <- c_pango_layout_get_text pl
	is <- byteIndices =<< toCStringLen t
	case is `maybeIndex` idx of
		Nothing -> pure Nothing
		Just i -> do
			c_pango_layout_get_cursor_pos pl (fromIntegral i) spos wpos
			(Just <$>) $ (,)
				<$> (PangoRectangleFixed_ <$> newForeignPtr spos (free spos))
				<*> (PangoRectangleFixed_ <$> newForeignPtr wpos (free wpos))

foreign import ccall "pango_layout_get_cursor_pos" c_pango_layout_get_cursor_pos ::
	Ptr PangoLayout -> CInt -> Ptr PangoRectangleFixed -> Ptr PangoRectangleFixed -> IO ()

data MinMax a = Min | Jst a | Max deriving Show
data Dir = L | R deriving Show

pangoLayoutMoveCursorVisually ::
	PangoLayout -> Bool -> Int -> Bool -> Dir -> IO (Maybe (MinMax Int, CInt))
pangoLayoutMoveCursorVisually (PangoLayout_ fpl) str oidx otr dir =
	withForeignPtr fpl \pl -> alloca \nidx -> alloca \ntr -> do
		t <- c_pango_layout_get_text pl
		is <- byteIndices =<< toCStringLen t
		case is `maybeIndex` oidx of
			Nothing -> pure Nothing
			Just i -> do
				c_pango_layout_move_cursor_visually pl
					(boolToGboolean str) (fromIntegral i) (bool 0 1 otr)
					(case dir of L -> - 1; R -> 1) nidx ntr
				nidx' <- peek nidx
				let	mnidx = case nidx of
						_	| nidx' < 0 -> Min
							| nidx' < maxBound -> Jst . fromJust . (`elemIndex` is) $ fromIntegral nidx'
							| otherwise -> Max
				ntr' <- peek ntr
				pure . Just $ (mnidx, ntr')

foreign import ccall "pango_layout_move_cursor_visually" c_pango_layout_move_cursor_visually ::
	Ptr PangoLayout -> #{type gboolean} -> CInt -> CInt -> CInt ->
	Ptr CInt -> Ptr CInt -> IO ()