[gtk+/wip/attach-params: 3/22] gdkattachparams: add GdkAttachParams



commit 63e982ccc5e426a1777ae850446e57635ba3c8aa
Author: William Hua <william hua canonical com>
Date:   Wed Jan 20 02:20:20 2016 -0600

    gdkattachparams: add GdkAttachParams
    
    https://bugzilla.gnome.org/show_bug.cgi?id=756579

 docs/reference/gdk/Makefile.am                |    5 +
 docs/reference/gdk/gdk-docs.sgml              |    1 +
 docs/reference/gdk/gdk3-sections.txt          |   22 +
 docs/reference/gdk/images/attach-anchors.png  |  Bin 0 -> 20500 bytes
 docs/reference/gdk/images/attach-flip-x.png   |  Bin 0 -> 8718 bytes
 docs/reference/gdk/images/attach-flip-y.png   |  Bin 0 -> 8409 bytes
 docs/reference/gdk/images/attach-margin.png   |  Bin 0 -> 6493 bytes
 docs/reference/gdk/images/attach-padding.png  |  Bin 0 -> 7018 bytes
 docs/reference/gdk/images/gdkattachparams.svg | 1060 +++++++++++++++++++++++++
 gdk/Makefile.am                               |    3 +
 gdk/gdk.h                                     |    1 +
 gdk/gdkattachparams.c                         |  853 ++++++++++++++++++++
 gdk/gdkattachparams.h                         |   97 +++
 gdk/gdkattachparamsprivate.h                  |   78 ++
 14 files changed, 2120 insertions(+), 0 deletions(-)
---
diff --git a/docs/reference/gdk/Makefile.am b/docs/reference/gdk/Makefile.am
index aef7cab..acfc41a 100644
--- a/docs/reference/gdk/Makefile.am
+++ b/docs/reference/gdk/Makefile.am
@@ -60,6 +60,11 @@ HTML_IMAGES =                                \
        images/rotated-text.png         \
        images/X_cursor.png             \
        images/arrow.png                \
+       images/attach-anchors.png       \
+       images/attach-margin.png        \
+       images/attach-flip-x.png        \
+       images/attach-flip-y.png        \
+       images/attach-padding.png       \
        images/based_arrow_down.png     \
        images/based_arrow_up.png       \
        images/boat.png                 \
diff --git a/docs/reference/gdk/gdk-docs.sgml b/docs/reference/gdk/gdk-docs.sgml
index fdd36a4..6962b4d 100644
--- a/docs/reference/gdk/gdk-docs.sgml
+++ b/docs/reference/gdk/gdk-docs.sgml
@@ -47,6 +47,7 @@
     <xi:include href="xml/wayland_interaction.xml" />
     <xi:include href="xml/gdkapplaunchcontext.xml" />
     <xi:include href="xml/gdktestutils.xml" />
+    <xi:include href="xml/gdkattachparams.xml" />
   </reference>
 
   <reference>
diff --git a/docs/reference/gdk/gdk3-sections.txt b/docs/reference/gdk/gdk3-sections.txt
index 7b1b9f7..3fe6c98 100644
--- a/docs/reference/gdk/gdk3-sections.txt
+++ b/docs/reference/gdk/gdk3-sections.txt
@@ -551,6 +551,28 @@ gdk_fullscreen_mode_get_type
 </SECTION>
 
 <SECTION>
+<TITLE>Attachment Parameters</TITLE>
+<FILE>gdkattachparams</FILE>
+GdkAttachParams
+GdkAttachCallback
+gdk_attach_params_new
+gdk_attach_params_free
+gdk_attach_params_set_attach_rect
+gdk_attach_params_set_anchors
+gdk_attach_params_get_anchors
+gdk_attach_params_set_attach_margin
+gdk_attach_params_set_window_padding
+gdk_attach_params_set_window_offset
+gdk_attach_params_set_position_callback
+
+<SUBSECTION Private>
+_GdkAttachParams
+gdk_attach_params_choose_position
+gdk_attach_params_choose_position_for_window
+gdk_attach_params_move_window
+</SECTION>
+
+<SECTION>
 <TITLE>Selections</TITLE>
 <FILE>selections</FILE>
 GDK_SELECTION_PRIMARY
diff --git a/docs/reference/gdk/images/attach-anchors.png b/docs/reference/gdk/images/attach-anchors.png
new file mode 100644
index 0000000..f2f71cb
Binary files /dev/null and b/docs/reference/gdk/images/attach-anchors.png differ
diff --git a/docs/reference/gdk/images/attach-flip-x.png b/docs/reference/gdk/images/attach-flip-x.png
new file mode 100644
index 0000000..6f73953
Binary files /dev/null and b/docs/reference/gdk/images/attach-flip-x.png differ
diff --git a/docs/reference/gdk/images/attach-flip-y.png b/docs/reference/gdk/images/attach-flip-y.png
new file mode 100644
index 0000000..32cb7b2
Binary files /dev/null and b/docs/reference/gdk/images/attach-flip-y.png differ
diff --git a/docs/reference/gdk/images/attach-margin.png b/docs/reference/gdk/images/attach-margin.png
new file mode 100644
index 0000000..9713dc5
Binary files /dev/null and b/docs/reference/gdk/images/attach-margin.png differ
diff --git a/docs/reference/gdk/images/attach-padding.png b/docs/reference/gdk/images/attach-padding.png
new file mode 100644
index 0000000..cf41c51
Binary files /dev/null and b/docs/reference/gdk/images/attach-padding.png differ
diff --git a/docs/reference/gdk/images/gdkattachparams.svg b/docs/reference/gdk/images/gdkattachparams.svg
new file mode 100644
index 0000000..5e27379
--- /dev/null
+++ b/docs/reference/gdk/images/gdkattachparams.svg
@@ -0,0 +1,1060 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="630"
+   height="1010"
+   viewBox="0 0 630.00001 1010"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="gdkattachparams.svg">
+  <defs
+     id="defs4">
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker9053"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="TriangleOutM">
+      <path
+         transform="scale(0.4,0.4)"
+         
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
+         id="path9055"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="TriangleOutM"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker8166"
+       style="overflow:visible"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         id="path8168"
+         d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
+         
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="scale(0.4,0.4)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker7177"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="TriangleOutL">
+      <path
+         transform="scale(0.8,0.8)"
+         
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
+         id="path7179"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker6691"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="TriangleOutL">
+      <path
+         transform="scale(0.8,0.8)"
+         
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
+         id="path6693"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="TriangleOutL"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="TriangleOutL"
+       style="overflow:visible"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         id="path4790"
+         d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
+         
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="scale(0.8,0.8)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker6275"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path6277"
+         
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 
-1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="TriangleOutM"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker6229"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path6231"
+         d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
+         
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="scale(0.4,0.4)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker6189"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path6191"
+         
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 
-1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker5276"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="TriangleOutM"
+       inkscape:collect="always">
+      <path
+         transform="scale(0.4,0.4)"
+         
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
+         id="path5278"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="TriangleOutM"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="TriangleOutM"
+       style="overflow:visible"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         id="path4793"
+         d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
+         
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="scale(0.4,0.4)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lend"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4669"
+         
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 
-1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.6019802"
+     inkscape:cx="315"
+     inkscape:cy="505"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1215"
+     inkscape:window-height="776"
+     inkscape:window-x="65"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1"
+     units="px" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-42.362183)">
+    <g
+       id="gdkattachparamsanchors"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90">
+      <g
+         id="g4427">
+        <rect
+           
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+           id="rect4353"
+           width="420"
+           height="360"
+           x="0"
+           y="692.36218" />
+        <g
+           id="g4318"
+           transform="translate(105,-8)">
+          <g
+             transform="translate(7,2.2107222e-5)"
+             id="g4227">
+            <g
+               id="g4170">
+              <rect
+                 y="915.36218"
+                 x="1"
+                 height="38"
+                 width="138"
+                 id="rect3336"
+                 
style="fill:#8080ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+              <rect
+                 y="953.36218"
+                 x="1"
+                 height="98"
+                 width="198"
+                 id="rect4138"
+                 
style="fill:#ff80ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+              <circle
+                 r="7"
+                 cy="953.36218"
+                 cx="1"
+                 id="circle4168"
+                 
style="fill:#ff0000;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+            </g>
+            <text
+               sodipodi:linespacing="125%"
+               id="text4195"
+               y="937.12097"
+               x="15.678711"
+               
style="font-style:normal;font-weight:normal;font-size:10px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+               xml:space="preserve"><tspan
+                 y="937.12097"
+                 x="15.678711"
+                 id="tspan4197"
+                 sodipodi:role="line">attachment rectangle</tspan></text>
+            <text
+               sodipodi:linespacing="125%"
+               id="text4199"
+               y="1006.0902"
+               x="81.027832"
+               
style="font-style:normal;font-weight:normal;font-size:10px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+               xml:space="preserve"><tspan
+                 y="1006.0902"
+                 x="81.027832"
+                 id="tspan4201"
+                 sodipodi:role="line">window</tspan></text>
+          </g>
+          <g
+             transform="translate(0,-40)"
+             id="g4295">
+            <g
+               id="g4237"
+               transform="translate(23,7.0000221)">
+              <g
+                 id="g4207">
+                <g
+                   id="g4158"
+                   transform="translate(-14,50)">
+                  <rect
+                     
style="fill:#8080ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+                     id="rect4140"
+                     width="138"
+                     height="38"
+                     x="-96"
+                     y="762.36218" />
+                  <circle
+                     r="7"
+                     cy="800.36218"
+                     cx="-96"
+                     id="path4148"
+                     
style="fill:#ff0000;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+                </g>
+                <text
+                   xml:space="preserve"
+                   
style="font-style:normal;font-weight:normal;font-size:10px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+                   x="-95.321289"
+                   y="834.12097"
+                   id="text4179"
+                   sodipodi:linespacing="125%"><tspan
+                     sodipodi:role="line"
+                     id="tspan4181"
+                     x="-95.321289"
+                     y="834.12097">attachment rectangle</tspan></text>
+                <text
+                   xml:space="preserve"
+                   
style="font-style:normal;font-weight:normal;font-size:10px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+                   x="-111.56152"
+                   y="870.50378"
+                   id="text4187"
+                   sodipodi:linespacing="125%"><tspan
+                     sodipodi:role="line"
+                     id="tspan4189"
+                     x="-111.56152"
+                     y="870.50378">attachment rectangle anchor:</tspan><tspan
+                     sodipodi:role="line"
+                     x="-111.56152"
+                     y="883.00378"
+                     id="tspan4205">GDK_WINDOW_EDGE_SOUTH_WEST</tspan></text>
+              </g>
+              <g
+                 transform="translate(-46,-24)"
+                 id="g4217">
+                <g
+                   id="g4175"
+                   transform="translate(-22,9)">
+                  <rect
+                     
style="fill:#ff80ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+                     id="rect4142"
+                     width="198"
+                     height="98"
+                     x="151"
+                     y="767.36218" />
+                  <circle
+                     
style="fill:#ff0000;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+                     id="circle4156"
+                     cx="151"
+                     cy="767.36218"
+                     r="7" />
+                </g>
+                <text
+                   xml:space="preserve"
+                   
style="font-style:normal;font-weight:normal;font-size:10px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+                   x="209.02783"
+                   y="829.09021"
+                   id="text4183"
+                   sodipodi:linespacing="125%"><tspan
+                     sodipodi:role="line"
+                     id="tspan4185"
+                     x="209.02783"
+                     y="829.09021">window</tspan></text>
+                <text
+                   xml:space="preserve"
+                   
style="font-style:normal;font-weight:normal;font-size:10px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+                   x="114.2041"
+                   y="772.73181"
+                   id="text4191"
+                   sodipodi:linespacing="125%"><tspan
+                     sodipodi:role="line"
+                     id="tspan4193"
+                     x="114.2041"
+                     y="772.73181">window anchor:</tspan><tspan
+                     sodipodi:role="line"
+                     x="114.2041"
+                     y="785.23181"
+                     id="tspan4203">GDK_WINDOW_EDGE_NORTH_WEST</tspan></text>
+              </g>
+            </g>
+            <text
+               xml:space="preserve"
+               
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+               x="60.761719"
+               y="820.90125"
+               id="text4287"
+               sodipodi:linespacing="125%"><tspan
+                 sodipodi:role="line"
+                 id="tspan4289"
+                 x="60.761719"
+                 y="820.90125">+</tspan></text>
+          </g>
+          <text
+             sodipodi:linespacing="125%"
+             id="text4291"
+             y="894.23718"
+             x="86.741722"
+             
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             xml:space="preserve"><tspan
+               y="894.23718"
+               x="86.741722"
+               id="tspan4293"
+               sodipodi:role="line">=</tspan></text>
+        </g>
+      </g>
+      <g
+         id="g4476">
+        <g
+           id="g4468"
+           transform="translate(30.144517,4.4342879)">
+          <path
+             sodipodi:nodetypes="cccc"
+             inkscape:connector-curvature="0"
+             id="path4464"
+             d="m 340.34868,867.70431 -10,60 10,-20 z"
+             
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 />
+          <path
+             
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             d="m 340.349,867.70431 10,60 -10,-20 z"
+             id="path4466"
+             inkscape:connector-curvature="0"
+             sodipodi:nodetypes="cccc" />
+        </g>
+        <text
+           xml:space="preserve"
+           
style="font-style:normal;font-weight:normal;font-size:20px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="363.01288"
+           y="862.36218"
+           id="text4472"
+           sodipodi:linespacing="125%"><tspan
+             sodipodi:role="line"
+             id="tspan4474"
+             x="363.01288"
+             y="862.36218">N</tspan></text>
+      </g>
+    </g>
+    <g
+       id="gdkattachparamswindowpadding"
+       transform="translate(-55.76419,-456.49685)"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90">
+      <rect
+         y="508.85904"
+         x="335.76419"
+         height="190"
+         width="260"
+         id="rect10017"
+         
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+      <g
+         transform="translate(-1.8022156,-3.0031544)"
+         id="g5841">
+        <g
+           id="g4614">
+          <g
+             transform="translate(-7,-12.999978)"
+             id="g4582">
+            <g
+               id="g4545"
+               transform="translate(105,51)">
+              <rect
+                 y="515.36218"
+                 x="253"
+                 height="98"
+                 width="198"
+                 id="rect4543"
+                 
style="fill:#ff80ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+              <text
+                 sodipodi:linespacing="125%"
+                 id="text4539"
+                 y="568.09021"
+                 x="333.02783"
+                 
style="font-style:normal;font-weight:normal;font-size:10px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+                 xml:space="preserve"><tspan
+                   y="568.09021"
+                   x="333.02783"
+                   id="tspan4541"
+                   sodipodi:role="line">window</tspan></text>
+            </g>
+            <rect
+               
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:4,
 4;stroke-dashoffset:0;stroke-opacity:1"
+               id="rect4580"
+               width="178"
+               height="78"
+               x="368"
+               y="576.36218" />
+          </g>
+          <g
+             transform="translate(289,3.0000221)"
+             id="g4550">
+            <rect
+               
style="fill:#8080ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+               id="rect4527"
+               width="138"
+               height="38"
+               x="72"
+               y="522.36218" />
+            <text
+               xml:space="preserve"
+               
style="font-style:normal;font-weight:normal;font-size:10px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+               x="86.678711"
+               y="544.12097"
+               id="text4535"
+               sodipodi:linespacing="125%"><tspan
+                 sodipodi:role="line"
+                 id="tspan4537"
+                 x="86.678711"
+                 y="544.12097">attachment rectangle</tspan></text>
+          </g>
+        </g>
+        <text
+           xml:space="preserve"
+           
style="font-style:normal;font-weight:normal;font-size:10px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="502.44727"
+           y="687.00378"
+           id="text4630"
+           sodipodi:linespacing="125%"><tspan
+             sodipodi:role="line"
+             id="tspan4632"
+             x="502.44727"
+             y="687.00378">window_padding</tspan></text>
+        <g
+           id="g5800">
+          <g
+             id="g4638">
+            <path
+               
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.99999988;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+               d="m 539,642.3622 0,30"
+               id="path4634"
+               inkscape:connector-curvature="0" />
+            <path
+               inkscape:connector-curvature="0"
+               id="path4636"
+               d="m 549,642.3622 0,30"
+               
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.99999988;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 />
+          </g>
+          <path
+             inkscape:connector-curvature="0"
+             id="path4642"
+             d="m 516.38489,663.49566 16,0"
+             
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#TriangleOutM)"
 />
+          <path
+             
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker5276)"
+             d="m 571.61511,663.49566 -16,0"
+             id="path5274"
+             inkscape:connector-curvature="0" />
+        </g>
+      </g>
+    </g>
+    <g
+       id="gdkattachparamsattachmargin"
+       transform="translate(-36.233547,-414.5621)"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90">
+      <rect
+         y="456.92429"
+         x="36.233547"
+         height="200"
+         width="280"
+         id="rect10042"
+         
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+      <g
+         transform="translate(-1.6692848,-5.5728114)"
+         id="g5819">
+        <g
+           id="g4601">
+          <g
+             id="g4589">
+            <g
+               id="g4555"
+               transform="translate(-192,38.000022)">
+              <rect
+                 
style="fill:#ff80ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+                 id="rect4557"
+                 width="198"
+                 height="98"
+                 x="253"
+                 y="515.36218" />
+              <text
+                 xml:space="preserve"
+                 
style="font-style:normal;font-weight:normal;font-size:10px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+                 x="333.02783"
+                 y="568.09021"
+                 id="text4559"
+                 sodipodi:linespacing="125%"><tspan
+                   sodipodi:role="line"
+                   id="tspan4561"
+                   x="333.02783"
+                   y="568.09021">window</tspan></text>
+            </g>
+            <g
+               id="g4573"
+               transform="translate(-7,6.9999916)">
+              <g
+                 transform="translate(-4,-23.999969)"
+                 id="g4563">
+                <rect
+                   y="522.36218"
+                   x="72"
+                   height="38"
+                   width="138"
+                   id="rect4565"
+                   
style="fill:#8080ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+                <text
+                   sodipodi:linespacing="125%"
+                   id="text4567"
+                   y="544.12097"
+                   x="86.678711"
+                   
style="font-style:normal;font-weight:normal;font-size:10px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+                   xml:space="preserve"><tspan
+                     y="544.12097"
+                     x="86.678711"
+                     id="tspan4569"
+                     sodipodi:role="line">attachment rectangle</tspan></text>
+              </g>
+              <rect
+                 y="488.36221"
+                 x="58"
+                 height="58"
+                 width="158"
+                 id="rect4571"
+                 
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:4,
 4;stroke-dashoffset:0;stroke-opacity:1" />
+            </g>
+          </g>
+        </g>
+        <text
+           xml:space="preserve"
+           
style="font-style:normal;font-weight:normal;font-size:10px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="234.39941"
+           y="502.86673"
+           id="text4626"
+           sodipodi:linespacing="125%"><tspan
+             sodipodi:role="line"
+             id="tspan4628"
+             x="234.39941"
+             y="502.86673">attach_margin</tspan></text>
+        <g
+           id="g5807"
+           transform="matrix(0,-1,1,0,-442.3622,1044.2471)">
+          <g
+             id="g5809">
+            <path
+               inkscape:connector-curvature="0"
+               id="path5811"
+               d="m 539,642.3622 0,30"
+               
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.99999988;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 />
+            <path
+               
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.99999988;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+               d="m 549,642.3622 0,30"
+               id="path5813"
+               inkscape:connector-curvature="0" />
+          </g>
+          <path
+             
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#TriangleOutM)"
+             d="m 516.38489,663.49566 16,0"
+             id="path5815"
+             inkscape:connector-curvature="0" />
+          <path
+             inkscape:connector-curvature="0"
+             id="path5817"
+             d="m 571.61511,663.49566 -16,0"
+             
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker5276)"
 />
+        </g>
+      </g>
+    </g>
+    <g
+       id="gdkattachparamsflipy"
+       transform="translate(-422.33673,-429.04089)"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90">
+      <rect
+         y="671.40308"
+         x="422.33673"
+         height="260"
+         width="630"
+         id="rect9978"
+         
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+      <g
+         transform="translate(-14.663269,43.040874)"
+         id="g9903">
+        <g
+           id="g8089">
+          <g
+             transform="translate(507,-175.99998)"
+             id="g6107">
+            <g
+               id="g6109">
+              <rect
+                 y="915.36218"
+                 x="1"
+                 height="38"
+                 width="138"
+                 id="rect6111"
+                 
style="fill:#8080ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+              <rect
+                 y="953.36218"
+                 x="1"
+                 height="98"
+                 width="198"
+                 id="rect6113"
+                 
style="fill:#ff80ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+              <circle
+                 r="7"
+                 cy="953.36218"
+                 cx="1"
+                 id="circle6115"
+                 
style="fill:#ff0000;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+            </g>
+            <text
+               sodipodi:linespacing="125%"
+               id="text6117"
+               y="937.12097"
+               x="15.678711"
+               
style="font-style:normal;font-weight:normal;font-size:10px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+               xml:space="preserve"><tspan
+                 y="937.12097"
+                 x="15.678711"
+                 id="tspan6119"
+                 sodipodi:role="line">attachment rectangle</tspan></text>
+            <text
+               sodipodi:linespacing="125%"
+               id="text6121"
+               y="1006.0902"
+               x="81.027832"
+               
style="font-style:normal;font-weight:normal;font-size:10px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+               xml:space="preserve"><tspan
+                 y="1006.0902"
+                 x="81.027832"
+                 id="tspan6123"
+                 sodipodi:role="line">window</tspan></text>
+          </g>
+          <g
+             id="g8055">
+            <path
+               
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:8.00000003,
 8.00000003;stroke-dashoffset:0;stroke-opacity:1"
+               d="m 480,810.3622 260,0"
+               id="path6185"
+               inkscape:connector-curvature="0" />
+            <path
+               
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#TriangleOutL)"
+               d="m 724.73308,809.36222 0,-16.23251"
+               id="path6187"
+               inkscape:connector-curvature="0"
+               sodipodi:nodetypes="cc" />
+          </g>
+          <text
+             sodipodi:linespacing="125%"
+             id="text8069"
+             y="812.36218"
+             x="443.45801"
+             
style="font-style:normal;font-weight:normal;font-size:10px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             xml:space="preserve"><tspan
+               id="tspan8073"
+               y="812.36218"
+               x="443.45801"
+               sodipodi:role="line">screen</tspan></text>
+        </g>
+        <g
+           id="g8105">
+          <g
+             transform="translate(16,2.00002)"
+             id="g6172">
+            <rect
+               
style="fill:#8080ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+               id="rect6129"
+               width="138"
+               height="38"
+               x="812"
+               y="737.36218" />
+            <rect
+               
style="fill:#ff80ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+               id="rect6131"
+               width="198"
+               height="98"
+               x="812"
+               y="639.36218" />
+            <circle
+               
style="fill:#ff0000;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+               id="circle6133"
+               cx="812"
+               cy="737.36218"
+               r="7" />
+            <text
+               sodipodi:linespacing="125%"
+               id="text6135"
+               y="759.12097"
+               x="826.67871"
+               
style="font-style:normal;font-weight:normal;font-size:10px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+               xml:space="preserve"><tspan
+                 y="759.12097"
+                 x="826.67871"
+                 id="tspan6137"
+                 sodipodi:role="line">attachment rectangle</tspan></text>
+            <text
+               sodipodi:linespacing="125%"
+               id="text6139"
+               y="692.09021"
+               x="892.02783"
+               
style="font-style:normal;font-weight:normal;font-size:10px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+               xml:space="preserve"><tspan
+                 y="692.09021"
+                 x="892.02783"
+                 id="tspan6141"
+                 sodipodi:role="line">window</tspan></text>
+          </g>
+          <g
+             transform="translate(20,0)"
+             id="g8065">
+            <path
+               inkscape:connector-curvature="0"
+               id="path6183"
+               d="m 780,810.3622 260,0"
+               
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:8.00000003,
 8.00000003;stroke-dashoffset:0;stroke-opacity:1" />
+            <path
+               sodipodi:nodetypes="cc"
+               inkscape:connector-curvature="0"
+               id="path6689"
+               d="m 1024.7331,809.36222 0,-16.23251"
+               
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker6691)"
 />
+          </g>
+          <text
+             xml:space="preserve"
+             
style="font-style:normal;font-weight:normal;font-size:10px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="763.45801"
+             y="812.36218"
+             id="text8077"
+             sodipodi:linespacing="125%"><tspan
+               sodipodi:role="line"
+               x="763.45801"
+               y="812.36218"
+               id="tspan8079">screen</tspan></text>
+        </g>
+        <g
+           id="g9830"
+           transform="translate(17,9.9442967)">
+          <text
+             sodipodi:linespacing="125%"
+             id="text8085"
+             y="755.98181"
+             x="722"
+             
style="font-style:normal;font-weight:normal;font-size:10px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             xml:space="preserve"><tspan
+               y="755.98181"
+               x="722"
+               id="tspan8087"
+               sodipodi:role="line">flip_y</tspan></text>
+          <path
+             inkscape:connector-curvature="0"
+             id="path8164"
+             d="m 720.13981,743.36214 24.38457,0"
+             
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.99999976;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker8166)"
 />
+        </g>
+      </g>
+    </g>
+    <g
+       id="gdkattachparamsflipx"
+       transform="translate(-476.55563,-431.57452)"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90">
+      <rect
+         y="933.93671"
+         x="476.55563"
+         height="190"
+         width="590"
+         id="rect9938"
+         
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+      <g
+         transform="translate(-10.645721,9.945609)"
+         id="g9867">
+        <g
+           id="g9851">
+          <g
+             transform="translate(507,36.000022)"
+             id="g6143">
+            <g
+               id="g6145">
+              <rect
+                 y="915.36218"
+                 x="1"
+                 height="38"
+                 width="138"
+                 id="rect6147"
+                 
style="fill:#8080ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+              <rect
+                 y="953.36218"
+                 x="1"
+                 height="98"
+                 width="198"
+                 id="rect6149"
+                 
style="fill:#ff80ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+              <circle
+                 r="7"
+                 cy="953.36218"
+                 cx="1"
+                 id="circle6151"
+                 
style="fill:#ff0000;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+            </g>
+            <text
+               sodipodi:linespacing="125%"
+               id="text6153"
+               y="937.12097"
+               x="15.678711"
+               
style="font-style:normal;font-weight:normal;font-size:10px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+               xml:space="preserve"><tspan
+                 y="937.12097"
+                 x="15.678711"
+                 id="tspan6155"
+                 sodipodi:role="line">attachment rectangle</tspan></text>
+            <text
+               sodipodi:linespacing="125%"
+               id="text6157"
+               y="1006.0902"
+               x="81.027832"
+               
style="font-style:normal;font-weight:normal;font-size:10px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+               xml:space="preserve"><tspan
+                 y="1006.0902"
+                 x="81.027832"
+                 id="tspan6159"
+                 sodipodi:role="line">window</tspan></text>
+          </g>
+          <g
+             transform="translate(-16.537266,0)"
+             id="g8045">
+            <path
+               
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:8.00000009,
 8.00000009;stroke-dashoffset:0;stroke-opacity:1"
+               d="m 712,943.3622 0,160"
+               id="path6181"
+               inkscape:connector-curvature="0" />
+            <path
+               sodipodi:nodetypes="cc"
+               inkscape:connector-curvature="0"
+               id="path7175"
+               d="m 714,962.62913 -16.23251,0"
+               
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker7177)"
 />
+          </g>
+          <text
+             sodipodi:linespacing="125%"
+             id="text8152"
+             y="940.22058"
+             x="678.98077"
+             
style="font-style:normal;font-weight:normal;font-size:10px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             xml:space="preserve"><tspan
+               id="tspan8156"
+               y="940.22058"
+               x="678.98077"
+               sodipodi:role="line">screen</tspan></text>
+        </g>
+        <g
+           id="g9835"
+           transform="translate(-20,0)">
+          <g
+             id="g5870"
+             transform="translate(880,36.000022)">
+            <g
+               id="g5872">
+              <rect
+                 
style="fill:#8080ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+                 id="rect5874"
+                 width="138"
+                 height="38"
+                 x="1"
+                 y="915.36218" />
+              <rect
+                 
style="fill:#ff80ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+                 id="rect5876"
+                 width="198"
+                 height="98"
+                 x="-59"
+                 y="953.36218" />
+              <circle
+                 
style="fill:#ff0000;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+                 id="circle5878"
+                 cx="139"
+                 cy="953.36218"
+                 r="7" />
+            </g>
+            <text
+               xml:space="preserve"
+               
style="font-style:normal;font-weight:normal;font-size:10px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+               x="15.678711"
+               y="937.12097"
+               id="text5880"
+               sodipodi:linespacing="125%"><tspan
+                 sodipodi:role="line"
+                 id="tspan5882"
+                 x="15.678711"
+                 y="937.12097">attachment rectangle</tspan></text>
+            <text
+               xml:space="preserve"
+               
style="font-style:normal;font-weight:normal;font-size:10px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+               x="21.027832"
+               y="1006.0902"
+               id="text5884"
+               sodipodi:linespacing="125%"><tspan
+                 sodipodi:role="line"
+                 id="tspan5886"
+                 x="21.027832"
+                 y="1006.0902">window</tspan></text>
+          </g>
+          <g
+             id="g8146"
+             transform="translate(356.46273,0)">
+            <path
+               inkscape:connector-curvature="0"
+               id="path8148"
+               d="m 712,943.3622 0,160"
+               
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:8.00000009,
 8.00000009;stroke-dashoffset:0;stroke-opacity:1" />
+            <path
+               
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker7177)"
+               d="m 714,962.62913 -16.23251,0"
+               id="path8150"
+               inkscape:connector-curvature="0"
+               sodipodi:nodetypes="cc" />
+          </g>
+          <text
+             xml:space="preserve"
+             
style="font-style:normal;font-weight:normal;font-size:10px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="1051.9808"
+             y="940.22058"
+             id="text8160"
+             sodipodi:linespacing="125%"><tspan
+               sodipodi:role="line"
+               x="1051.9808"
+               y="940.22058"
+               id="tspan8162">screen</tspan></text>
+        </g>
+        <g
+           id="g9819"
+           transform="translate(-7.98535,-9)">
+          <text
+             sodipodi:linespacing="125%"
+             id="text8081"
+             y="1029.3622"
+             x="747"
+             
style="font-style:normal;font-weight:normal;font-size:10px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             xml:space="preserve"><tspan
+               y="1029.3622"
+               x="747"
+               id="tspan8083"
+               sodipodi:role="line">flip_x</tspan></text>
+          <path
+             
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.99999976;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker9053)"
+             d="m 745.12516,1015.3621 24.38457,0"
+             id="path9051"
+             inkscape:connector-curvature="0" />
+        </g>
+      </g>
+    </g>
+  </g>
+</svg>
diff --git a/gdk/Makefile.am b/gdk/Makefile.am
index 84942ce..4314865 100644
--- a/gdk/Makefile.am
+++ b/gdk/Makefile.am
@@ -63,6 +63,7 @@ gdk_public_h_sources =                                \
        gdk.h                                   \
        gdk-autocleanup.h                       \
        gdkapplaunchcontext.h                   \
+       gdkattachparams.h                       \
        gdkcairo.h                              \
        gdkcursor.h                             \
        gdkdevice.h                             \
@@ -103,6 +104,7 @@ gdk_h_sources =                                     \
 gdk_private_headers =                          \
        gdk-private.h                           \
        gdkapplaunchcontextprivate.h            \
+       gdkattachparamsprivate.h                \
        gdkcursorprivate.h                      \
        gdkdevicemanagerprivate.h               \
        gdkdeviceprivate.h                      \
@@ -129,6 +131,7 @@ gdk_c_sources =                             \
        gdk-private.c                           \
        gdk.c                                   \
        gdkapplaunchcontext.c                   \
+       gdkattachparams.c                       \
        gdkcairo.c                              \
        gdkcursor.c                             \
        gdkdeprecated.c                         \
diff --git a/gdk/gdk.h b/gdk/gdk.h
index 81ba765..869f6bf 100644
--- a/gdk/gdk.h
+++ b/gdk/gdk.h
@@ -30,6 +30,7 @@
 #include <gdk/gdkconfig.h>
 #include <gdk/gdkversionmacros.h>
 #include <gdk/gdkapplaunchcontext.h>
+#include <gdk/gdkattachparams.h>
 #include <gdk/gdkcairo.h>
 #include <gdk/gdkcursor.h>
 #include <gdk/gdkdevice.h>
diff --git a/gdk/gdkattachparams.c b/gdk/gdkattachparams.c
new file mode 100644
index 0000000..f6060f5
--- /dev/null
+++ b/gdk/gdkattachparams.c
@@ -0,0 +1,853 @@
+#include "config.h"
+
+#include "gdkattachparamsprivate.h"
+#include "gdkscreen.h"
+#include "gdkwindow.h"
+
+/**
+ * SECTION: gdkattachparams
+ * @section_id: gdkattachparams
+ * @title: Attachment Parameters
+ * @short_description: Describing relative window position
+ * @stability: Unstable
+ * @include: gdk/gdkattachparams.h
+ *
+ * A full description of how a window should be positioned relative to an
+ * attachment rectangle.
+ *
+ * Certain widgets such as menus and combo boxes don't require explicit
+ * absolute positioning; they only need to be aligned with respect to another
+ * anchoring widget, such as a menu item, in such a way to not overflow
+ * off-screen. GTK+ cannot always determine such an optimal position since it
+ * requires knowledge of the geometry of the monitor workarea as well as the
+ * ability to position windows in absolute root window coordinates, which some
+ * GDK backends do not support.
+ *
+ * A minimal #GdkAttachParams description should have an attachment rectangle,
+ * an attachment rectangle anchor, and a window anchor. The attachment
+ * rectangle is the allocation of the anchoring widget, which can be a menu
+ * item, menu button, combo box, etc. It can even be a 1x1 pixel at
+ * the current cursor position. The attachment rectangle anchor is a
+ * #GdkWindowEdge describing a point on the attachment rectangle that the
+ * window should be anchored to. The window anchor is the point on the window
+ * that should anchor onto the attachment rectangle's anchor.
+ *
+ * ![](attach-anchors.png)
+ *
+ * In addition, you can also specify additional "flip" parameters that hint to
+ * the backend that the anchors can be mirrored if not enough space is
+ * available horizontally or vertically on-screen.
+ *
+ * ![](attach-flip-x.png)
+ *
+ * ![](attach-flip-y.png)
+ *
+ * There are also additional parameters that can be set to fine tune the
+ * positioning of the window, such as margin and padding, as well as a
+ * callback to obtain the final position of the window.
+ *
+ * ![](attach-margin.png)
+ *
+ * ![](attach-padding.png)
+ *
+ * Since: 3.20
+ */
+
+/**
+ * gdk_attach_params_new:
+ *
+ * Creates a new #GdkAttachParams for describing the position of a #GdkWindow
+ * relative to an attachment #GdkRectangle.
+ *
+ * Returns: (transfer full): a new #GdkAttachParams, to be freed with
+ *          gdk_attach_params_free()
+ *
+ * Since: 3.20
+ */
+GdkAttachParams *
+gdk_attach_params_new (void)
+{
+  GdkAttachParams *params = g_new0 (GdkAttachParams, 1);
+
+  params->attach_anchor = GDK_WINDOW_EDGE_CENTER;
+  params->window_anchor = GDK_WINDOW_EDGE_CENTER;
+
+  return params;
+}
+
+/**
+ * gdk_attach_params_free:
+ * @data: the #GdkAttachParams to free
+ *
+ * Releases @data.
+ *
+ * Since: 3.20
+ */
+void
+gdk_attach_params_free (gpointer data)
+{
+  GdkAttachParams *params;
+
+  g_return_if_fail (data);
+
+  params = data;
+
+  if (params->attach_user_data && params->attach_destroy_notify)
+    params->attach_destroy_notify (params->attach_user_data);
+
+  g_clear_object (&params->attach_parent);
+
+  g_free (params);
+}
+
+/**
+ * gdk_attach_params_set_attach_rect:
+ * @params: a #GdkAttachParams
+ * @rectangle: (nullable): the attachment rectangle
+ * @parent: (nullable): the #GdkWindow that @rectangle is relative to
+ *
+ * Sets the attachment rectangle the window needs to be aligned relative to.
+ *
+ * Since: 3.20
+ */
+void
+gdk_attach_params_set_attach_rect (GdkAttachParams    *params,
+                                   const GdkRectangle *rectangle,
+                                   GdkWindow          *parent)
+{
+  g_return_if_fail (params);
+
+  if (rectangle)
+    {
+      params->has_attach_rect = TRUE;
+      params->attach_rect = *rectangle;
+      g_set_object (&params->attach_parent, parent);
+    }
+  else
+    {
+      params->has_attach_rect = FALSE;
+      g_clear_object (&params->attach_parent);
+    }
+}
+
+/**
+ * gdk_attach_params_set_anchors:
+ * @params: a #GdkAttachParams
+ * @attach_anchor: the anchor on the attachment rectangle
+ * @window_anchor: the anchor on the window
+ * @flip_x: %TRUE if the backend should try flipping the anchors horizontally
+ *          when not enough space is available
+ * @flip_y: %TRUE if the backend should try flipping the anchors vertically
+ *          when not enough space is available
+ *
+ * Sets how the attachment rectangle and window should be anchored to each
+ * other.
+ *
+ * ![](attach-anchors.png)
+ *
+ * ![](attach-flip-x.png)
+ *
+ * ![](attach-flip-y.png)
+ *
+ * Since: 3.20
+ */
+void
+gdk_attach_params_set_anchors (GdkAttachParams *params,
+                               GdkWindowEdge    attach_anchor,
+                               GdkWindowEdge    window_anchor,
+                               gboolean         flip_x,
+                               gboolean         flip_y)
+{
+  g_return_if_fail (params);
+
+  params->attach_anchor = attach_anchor;
+  params->window_anchor = window_anchor;
+  params->flip_x = flip_x;
+  params->flip_y = flip_y;
+}
+
+/**
+ * gdk_attach_params_get_anchors:
+ * @params: a #GdkAttachParams
+ * @attach_anchor: (out) (optional): the anchor on the attachment rectangle
+ * @window_anchor: (out) (optional): the anchor on the window
+ * @flip_x: (out) (optional): %TRUE if the backend should try flipping the
+ *          anchors horizontally when not enough space is available
+ * @flip_y: (out) (optional): %TRUE if the backend should try flipping the
+ *          anchors vertically when not enough space is available
+ *
+ * Gets how the attachment rectangle and window should be anchored to each
+ * other.
+ *
+ * ![](attach-anchors.png)
+ *
+ * ![](attach-flip-x.png)
+ *
+ * ![](attach-flip-y.png)
+ *
+ * Since: 3.20
+ */
+void
+gdk_attach_params_get_anchors (const GdkAttachParams *params,
+                               GdkWindowEdge         *attach_anchor,
+                               GdkWindowEdge         *window_anchor,
+                               gboolean              *flip_x,
+                               gboolean              *flip_y)
+{
+  g_return_if_fail (params);
+
+  if (attach_anchor)
+    *attach_anchor = params->attach_anchor;
+
+  if (window_anchor)
+    *window_anchor = params->window_anchor;
+
+  if (flip_x)
+    *flip_x = params->flip_x;
+
+  if (flip_y)
+    *flip_y = params->flip_y;
+}
+
+/**
+ * gdk_attach_params_set_attach_margin:
+ * @params: a #GdkAttachParams
+ * @margin: (nullable): the space around the attachment rectangle
+ *
+ * Sets the amount of space to leave around the attachment rectangle.
+ *
+ * ![](attach-margin.png)
+ *
+ * Since: 3.20
+ */
+void
+gdk_attach_params_set_attach_margin (GdkAttachParams *params,
+                                     const GdkBorder *margin)
+{
+  GdkBorder zero = { 0 };
+
+  g_return_if_fail (params);
+
+  params->attach_margin = margin ? *margin : zero;
+}
+
+/**
+ * gdk_attach_params_set_window_padding:
+ * @params: a #GdkAttachParams
+ * @padding: (nullable): the space between the window and its
+ *           contents.
+ *
+ * Sets the amount of space between the window and its contents.
+ *
+ * ![](attach-padding.png)
+ *
+ * Since: 3.20
+ */
+void
+gdk_attach_params_set_window_padding (GdkAttachParams *params,
+                                      const GdkBorder *padding)
+{
+  GdkBorder zero = { 0 };
+
+  g_return_if_fail (params);
+
+  params->window_padding = padding ? *padding : zero;
+}
+
+/**
+ * gdk_attach_params_set_window_offset:
+ * @params: a #GdkAttachParams
+ * @x: horizontal displacement
+ * @y: vertical displacement
+ *
+ * Sets the offset to displace the window by.
+ *
+ * Since: 3.20
+ */
+void
+gdk_attach_params_set_window_offset (GdkAttachParams *params,
+                                     gint             x,
+                                     gint             y)
+{
+  g_return_if_fail (params);
+
+  params->offset_x = x;
+  params->offset_y = y;
+}
+
+/**
+ * gdk_attach_params_set_position_callback:
+ * @params: a #GdkAttachParams
+ * @callback: (nullable): a function to be called when the final position of
+ *            the window is known
+ * @user_data: (transfer full) (nullable): additional data to pass to @callback
+ * @destroy_notify: (nullable): a function to release @user_data
+ *
+ * Sets the function to be called when the final position of the window is
+ * known.
+ *
+ * Since: 3.20
+ */
+void
+gdk_attach_params_set_position_callback (GdkAttachParams   *params,
+                                         GdkAttachCallback  callback,
+                                         gpointer           user_data,
+                                         GDestroyNotify     destroy_notify)
+{
+  g_return_if_fail (params);
+
+  params->attach_callback = callback;
+
+  if (user_data != params->attach_user_data)
+    {
+      if (params->attach_user_data && params->attach_destroy_notify)
+        params->attach_destroy_notify (params->attach_user_data);
+
+      params->attach_user_data = user_data;
+      params->attach_destroy_notify = destroy_notify;
+    }
+  else if (user_data)
+    g_warning ("%s (): params already owns user data", G_STRFUNC);
+}
+
+static GdkWindowEdge
+get_horizontal_edge (GdkWindowEdge edge)
+{
+  switch (edge)
+    {
+    case GDK_WINDOW_EDGE_NORTH_WEST:
+    case GDK_WINDOW_EDGE_WEST:
+    case GDK_WINDOW_EDGE_SOUTH_WEST:
+      return GDK_WINDOW_EDGE_WEST;
+
+    default:
+    case GDK_WINDOW_EDGE_NORTH:
+    case GDK_WINDOW_EDGE_CENTER:
+    case GDK_WINDOW_EDGE_SOUTH:
+      return GDK_WINDOW_EDGE_CENTER;
+
+    case GDK_WINDOW_EDGE_NORTH_EAST:
+    case GDK_WINDOW_EDGE_EAST:
+    case GDK_WINDOW_EDGE_SOUTH_EAST:
+      return GDK_WINDOW_EDGE_EAST;
+    }
+}
+
+static GdkWindowEdge
+get_vertical_edge (GdkWindowEdge edge)
+{
+  switch (edge)
+    {
+    case GDK_WINDOW_EDGE_NORTH_WEST:
+    case GDK_WINDOW_EDGE_NORTH:
+    case GDK_WINDOW_EDGE_NORTH_EAST:
+      return GDK_WINDOW_EDGE_NORTH;
+
+    default:
+    case GDK_WINDOW_EDGE_WEST:
+    case GDK_WINDOW_EDGE_CENTER:
+    case GDK_WINDOW_EDGE_EAST:
+      return GDK_WINDOW_EDGE_CENTER;
+
+    case GDK_WINDOW_EDGE_SOUTH_WEST:
+    case GDK_WINDOW_EDGE_SOUTH:
+    case GDK_WINDOW_EDGE_SOUTH_EAST:
+      return GDK_WINDOW_EDGE_SOUTH;
+    }
+}
+
+static GdkWindowEdge
+get_opposite_edge (GdkWindowEdge edge)
+{
+  switch (edge)
+    {
+    case GDK_WINDOW_EDGE_NORTH_WEST:
+      return GDK_WINDOW_EDGE_SOUTH_EAST;
+
+    case GDK_WINDOW_EDGE_NORTH:
+      return GDK_WINDOW_EDGE_SOUTH;
+
+    case GDK_WINDOW_EDGE_NORTH_EAST:
+      return GDK_WINDOW_EDGE_SOUTH_WEST;
+
+    case GDK_WINDOW_EDGE_WEST:
+      return GDK_WINDOW_EDGE_EAST;
+
+    case GDK_WINDOW_EDGE_CENTER:
+      return GDK_WINDOW_EDGE_CENTER;
+
+    case GDK_WINDOW_EDGE_EAST:
+      return GDK_WINDOW_EDGE_WEST;
+
+    case GDK_WINDOW_EDGE_SOUTH_WEST:
+      return GDK_WINDOW_EDGE_NORTH_EAST;
+
+    case GDK_WINDOW_EDGE_SOUTH:
+      return GDK_WINDOW_EDGE_NORTH;
+
+    case GDK_WINDOW_EDGE_SOUTH_EAST:
+      return GDK_WINDOW_EDGE_NORTH_WEST;
+
+    default:
+      return edge;
+    }
+}
+
+static void
+get_margin_usage (GdkWindowEdge  attach_anchor,
+                  GdkWindowEdge  window_anchor,
+                  gint          *margin_x,
+                  gint          *margin_y)
+{
+  GdkWindowEdge attach_edge;
+  GdkWindowEdge window_edge;
+
+  if (margin_x)
+    {
+      attach_edge = get_horizontal_edge (attach_anchor);
+      window_edge = get_horizontal_edge (window_anchor);
+
+      if (attach_edge == GDK_WINDOW_EDGE_WEST && window_edge == GDK_WINDOW_EDGE_EAST)
+        *margin_x = -1;
+      else if (attach_edge == GDK_WINDOW_EDGE_EAST && window_edge == GDK_WINDOW_EDGE_WEST)
+        *margin_x = 1;
+      else
+        *margin_x = 0;
+    }
+
+  if (margin_y)
+    {
+      attach_edge = get_vertical_edge (attach_anchor);
+      window_edge = get_vertical_edge (window_anchor);
+
+      if (attach_edge == GDK_WINDOW_EDGE_NORTH && window_edge == GDK_WINDOW_EDGE_SOUTH)
+        *margin_y = -1;
+      else if (attach_edge == GDK_WINDOW_EDGE_SOUTH && window_edge == GDK_WINDOW_EDGE_NORTH)
+        *margin_y = 1;
+      else
+        *margin_y = 0;
+    }
+}
+
+static void
+get_anchor_point (gint           x,
+                  gint           y,
+                  gint           width,
+                  gint           height,
+                  GdkWindowEdge  edge,
+                  gint          *out_x,
+                  gint          *out_y)
+{
+  if (out_x)
+    {
+      switch (get_horizontal_edge (edge))
+        {
+        case GDK_WINDOW_EDGE_WEST:
+          *out_x = x;
+          break;
+
+        default:
+        case GDK_WINDOW_EDGE_CENTER:
+          *out_x = x + width / 2;
+          break;
+
+        case GDK_WINDOW_EDGE_EAST:
+          *out_x = x + width;
+          break;
+        }
+    }
+
+  if (out_y)
+    {
+      switch (get_vertical_edge (edge))
+        {
+        case GDK_WINDOW_EDGE_NORTH:
+          *out_y = y;
+          break;
+
+        default:
+        case GDK_WINDOW_EDGE_CENTER:
+          *out_y = y + height / 2;
+          break;
+
+        case GDK_WINDOW_EDGE_SOUTH:
+          *out_y = y + height;
+          break;
+        }
+    }
+}
+
+static gint
+clamp_with_feedback (gint  val,
+                     gint  min,
+                     gint  max,
+                     gint *offset)
+{
+  if (val < min && val > max)
+    {
+      if (min - val <= val - max)
+        {
+          *offset = min - val;
+          return min;
+        }
+      else
+        {
+          *offset = max - val;
+          return max;
+        }
+    }
+  else if (val < min)
+    {
+      *offset = min - val;
+      return min;
+    }
+  else if (val > max)
+    {
+      *offset = max - val;
+      return max;
+    }
+  else
+    {
+      *offset = 0;
+      return val;
+    }
+}
+
+/**
+ * gdk_attach_params_choose_position:
+ * @params: a #GdkAttachParams
+ * @width: window width
+ * @height: window height
+ * @bounds: (nullable): monitor geometry
+ * @x: (out) (optional): the best x-coordinate for the window
+ * @y: (out) (optional): the best y-coordinate for the window
+ * @offset_x: (out) (optional): the horizontal displacement needed to push the
+ *            window on-screen
+ * @offset_y: (out) (optional): the vertical displacement needed to push the
+ *            window on-screen
+ * @flipped_x: (out) (optional): %TRUE if the window was flipped horizontally
+ * @flipped_y: (out) (optional): %TRUE if the window was flipped vertically
+ *
+ * Finds the best position for a window of size @width and @height on a screen
+ * with @bounds using the given @params.
+ *
+ * Since: 3.20
+ */
+void
+gdk_attach_params_choose_position (const GdkAttachParams *params,
+                                   gint                   width,
+                                   gint                   height,
+                                   const GdkRectangle    *bounds,
+                                   gint                  *x,
+                                   gint                  *y,
+                                   gint                  *offset_x,
+                                   gint                  *offset_y,
+                                   gboolean              *flipped_x,
+                                   gboolean              *flipped_y)
+{
+  gint tmp_x;
+  gint tmp_y;
+  gint tmp_offset_x;
+  gint tmp_offset_y;
+  gboolean tmp_flipped_x;
+  gboolean tmp_flipped_y;
+  GdkRectangle padded_bounds;
+  gint rect_x;
+  gint rect_y;
+  gint first_x;
+  gint first_y;
+  gint second_x;
+  gint second_y;
+  gint margin_x;
+  gint margin_y;
+
+  g_return_if_fail (params);
+  g_return_if_fail (params->has_attach_rect);
+
+  if (!x)
+    x = &tmp_x;
+
+  if (!y)
+    y = &tmp_y;
+
+  if (!offset_x)
+    offset_x = &tmp_offset_x;
+
+  if (!offset_y)
+    offset_y = &tmp_offset_y;
+
+  if (!flipped_x)
+    flipped_x = &tmp_flipped_x;
+
+  if (!flipped_y)
+    flipped_y = &tmp_flipped_y;
+
+  *offset_x = 0;
+  *offset_y = 0;
+  *flipped_x = FALSE;
+  *flipped_y = FALSE;
+
+  get_margin_usage (params->attach_anchor, params->window_anchor, &margin_x, &margin_y);
+
+  if (params->attach_parent)
+    gdk_window_get_origin (params->attach_parent, &rect_x, &rect_y);
+  else
+    {
+      rect_x = 0;
+      rect_y = 0;
+    }
+
+  rect_x += params->attach_rect.x;
+  rect_y += params->attach_rect.y;
+
+  get_anchor_point (rect_x,
+                    rect_y,
+                    params->attach_rect.width,
+                    params->attach_rect.height,
+                    params->attach_anchor,
+                    &first_x,
+                    &first_y);
+
+  get_anchor_point (first_x - params->window_padding.left,
+                    first_y - params->window_padding.top,
+                    -(width - params->window_padding.left - params->window_padding.right),
+                    -(height - params->window_padding.top - params->window_padding.bottom),
+                    params->window_anchor,
+                    &first_x,
+                    &first_y);
+
+  switch (margin_x)
+    {
+    case -1:
+      first_x -= params->attach_margin.left;
+      break;
+
+    case 1:
+      first_x += params->attach_margin.right;
+      break;
+    }
+
+  switch (margin_y)
+    {
+    case -1:
+      first_y -= params->attach_margin.top;
+      break;
+
+    case 1:
+      first_y += params->attach_margin.bottom;
+      break;
+    }
+
+  first_x += params->offset_x;
+  first_y += params->offset_y;
+
+  if (bounds)
+    {
+      padded_bounds = *bounds;
+      padded_bounds.x -= params->window_padding.left;
+      padded_bounds.y -= params->window_padding.top;
+      padded_bounds.width += params->window_padding.left + params->window_padding.right;
+      padded_bounds.height += params->window_padding.top + params->window_padding.bottom;
+    }
+
+  if (bounds && (first_x < padded_bounds.x || first_x + width > padded_bounds.x + padded_bounds.width) && 
params->flip_x)
+    {
+      get_anchor_point (rect_x,
+                        0,
+                        params->attach_rect.width,
+                        0,
+                        get_opposite_edge (params->attach_anchor),
+                        &second_x,
+                        NULL);
+
+      get_anchor_point (second_x - params->window_padding.left,
+                        0,
+                        -(width - params->window_padding.left - params->window_padding.right),
+                        0,
+                        get_opposite_edge (params->window_anchor),
+                        &second_x,
+                        NULL);
+
+      switch (margin_x)
+        {
+        case -1:
+          second_x += params->attach_margin.right;
+          break;
+
+        case 1:
+          second_x -= params->attach_margin.left;
+          break;
+        }
+
+      second_x -= params->offset_x;
+
+      if (second_x >= padded_bounds.x && second_x + width <= padded_bounds.x + padded_bounds.width)
+        {
+          *x = second_x;
+          *flipped_x = TRUE;
+        }
+      else
+        *x = first_x;
+    }
+  else
+    *x = first_x;
+
+  if (bounds && (first_y < padded_bounds.y || first_y + height > padded_bounds.y + padded_bounds.height) && 
params->flip_y)
+    {
+      get_anchor_point (0,
+                        rect_y,
+                        0,
+                        params->attach_rect.height,
+                        get_opposite_edge (params->attach_anchor),
+                        NULL,
+                        &second_y);
+
+      get_anchor_point (0,
+                        second_y - params->window_padding.top,
+                        0,
+                        -(height - params->window_padding.top - params->window_padding.bottom),
+                        get_opposite_edge (params->window_anchor),
+                        NULL,
+                        &second_y);
+
+      switch (margin_y)
+        {
+        case -1:
+          second_y += params->attach_margin.bottom;
+          break;
+
+        case 1:
+          second_y -= params->attach_margin.top;
+          break;
+        }
+
+      second_y -= params->offset_y;
+
+      if (second_y >= padded_bounds.y && second_y + height <= padded_bounds.y + padded_bounds.height)
+        {
+          *y = second_y;
+          *flipped_y = TRUE;
+        }
+      else
+        *y = first_y;
+    }
+  else
+    *y = first_y;
+
+  if (bounds)
+    {
+      *x = clamp_with_feedback (*x, padded_bounds.x, padded_bounds.x + padded_bounds.width - width, 
offset_x);
+      *y = clamp_with_feedback (*y, padded_bounds.y, padded_bounds.y + padded_bounds.height - height, 
offset_y);
+    }
+}
+
+/**
+ * gdk_attach_params_choose_position_for_window:
+ * @params: a #GdkAttachParams
+ * @window: (transfer none) (not nullable): the #GdkWindow to find the best
+ *          position for
+ * @x: (out) (optional): the best x-coordinate for the window
+ * @y: (out) (optional): the best y-coordinate for the window
+ * @offset_x: (out) (optional): the horizontal displacement needed to push the
+ *            window on-screen
+ * @offset_y: (out) (optional): the vertical displacement needed to push the
+ *            window on-screen
+ * @flipped_x: (out) (optional): %TRUE if the window was flipped horizontally
+ * @flipped_y: (out) (optional): %TRUE if the window was flipped vertically
+ *
+ * Finds the best position for @window according to @params.
+ *
+ * Since: 3.20
+ */
+void
+gdk_attach_params_choose_position_for_window (const GdkAttachParams *params,
+                                              GdkWindow             *window,
+                                              gint                  *x,
+                                              gint                  *y,
+                                              gint                  *offset_x,
+                                              gint                  *offset_y,
+                                              gboolean              *flipped_x,
+                                              gboolean              *flipped_y)
+{
+  GdkScreen *screen;
+  gint origin_x;
+  gint origin_y;
+  gint center_x;
+  gint center_y;
+  gint monitor;
+  GdkRectangle bounds;
+  gint width;
+  gint height;
+
+  g_return_if_fail (params);
+  g_return_if_fail (params->has_attach_rect);
+  g_return_if_fail (window);
+
+  screen = gdk_window_get_screen (window);
+  gdk_window_get_origin (window, &origin_x, &origin_y);
+  center_x = origin_x + params->attach_rect.x + params->attach_rect.width / 2;
+  center_y = origin_y + params->attach_rect.y + params->attach_rect.height / 2;
+  monitor = gdk_screen_get_monitor_at_point (screen, center_x, center_y);
+  gdk_screen_get_monitor_workarea (screen, monitor, &bounds);
+  width = gdk_window_get_width (window);
+  height = gdk_window_get_height (window);
+
+  return gdk_attach_params_choose_position (params,
+                                            width,
+                                            height,
+                                            &bounds,
+                                            x,
+                                            y,
+                                            offset_x,
+                                            offset_y,
+                                            flipped_x,
+                                            flipped_y);
+}
+
+/**
+ * gdk_attach_params_move_window:
+ * @params: a #GdkAttachParams
+ * @window: (transfer none) (not nullable): the #GdkWindow to position
+ *
+ * Moves @window to the best position according to @params.
+ *
+ * Since: 3.20
+ */
+void
+gdk_attach_params_move_window (const GdkAttachParams *params,
+                               GdkWindow             *window)
+{
+  gint x;
+  gint y;
+  gint offset_x;
+  gint offset_y;
+  gboolean flipped_x;
+  gboolean flipped_y;
+
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  if (!params || !params->has_attach_rect)
+    return;
+
+  gdk_attach_params_choose_position_for_window (params,
+                                                window,
+                                                &x,
+                                                &y,
+                                                &offset_x,
+                                                &offset_y,
+                                                &flipped_x,
+                                                &flipped_y);
+
+  gdk_window_move (window, x, y);
+
+  if (params->attach_callback)
+    params->attach_callback (window,
+                             params,
+                             x,
+                             y,
+                             offset_x,
+                             offset_y,
+                             flipped_x,
+                             flipped_y,
+                             params->attach_user_data);
+}
diff --git a/gdk/gdkattachparams.h b/gdk/gdkattachparams.h
new file mode 100644
index 0000000..d3935fe
--- /dev/null
+++ b/gdk/gdkattachparams.h
@@ -0,0 +1,97 @@
+#ifndef __GDK_ATTACH_PARAMS_H__
+#define __GDK_ATTACH_PARAMS_H__
+
+#if !defined (__GDK_H_INSIDE__) && !defined (GDK_COMPILATION)
+#error "Only <gdk/gdk.h> can be included directly."
+#endif
+
+#include <gdk/gdktypes.h>
+#include <gdk/gdkversionmacros.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GdkAttachParams:
+ *
+ * Opaque type containing the information needed to position a window relative
+ * to an attachment rectangle.
+ *
+ * Since: 3.20
+ */
+typedef struct _GdkAttachParams GdkAttachParams;
+
+/**
+ * GdkAttachCallback:
+ * @window: the #GdkWindow that was moved
+ * @params: (transfer none) (nullable): the #GdkAttachParams that was used
+ * @x: the final x-coordinate of @window
+ * @y: the final y-coordinate of @window
+ * @offset_x: the horizontal displacement applied to keep @window on-screen
+ * @offset_y: the vertical displacement applied to keep @window on-screen
+ * @flipped_x: %TRUE if the backend flipped @window horizontally
+ * @flipped_y: %TRUE if the backend flipped @window vertically
+ * @user_data: (transfer none) (nullable): the user data that was set on
+ *             @params
+ *
+ * A function that can be used to receive information about the final position
+ * of a window.
+ *
+ * Since: 3.20
+ */
+typedef void (*GdkAttachCallback) (GdkWindow             *window,
+                                   const GdkAttachParams *params,
+                                   gint                   x,
+                                   gint                   y,
+                                   gint                   offset_x,
+                                   gint                   offset_y,
+                                   gboolean               flipped_x,
+                                   gboolean               flipped_y,
+                                   gpointer               user_data);
+
+GDK_AVAILABLE_IN_3_20
+GdkAttachParams * gdk_attach_params_new                   (void);
+
+GDK_AVAILABLE_IN_3_20
+void              gdk_attach_params_free                  (gpointer               data);
+
+GDK_AVAILABLE_IN_3_20
+void              gdk_attach_params_set_attach_rect       (GdkAttachParams       *params,
+                                                           const GdkRectangle    *rectangle,
+                                                           GdkWindow             *parent);
+
+GDK_AVAILABLE_IN_3_20
+void              gdk_attach_params_set_anchors           (GdkAttachParams       *params,
+                                                           GdkWindowEdge          attach_anchor,
+                                                           GdkWindowEdge          window_anchor,
+                                                           gboolean               flip_x,
+                                                           gboolean               flip_y);
+
+GDK_AVAILABLE_IN_3_20
+void              gdk_attach_params_get_anchors           (const GdkAttachParams *params,
+                                                           GdkWindowEdge         *attach_anchor,
+                                                           GdkWindowEdge         *window_anchor,
+                                                           gboolean              *flip_x,
+                                                           gboolean              *flip_y);
+
+GDK_AVAILABLE_IN_3_20
+void              gdk_attach_params_set_attach_margin     (GdkAttachParams       *params,
+                                                           const GdkBorder       *margin);
+
+GDK_AVAILABLE_IN_3_20
+void              gdk_attach_params_set_window_padding    (GdkAttachParams       *params,
+                                                           const GdkBorder       *padding);
+
+GDK_AVAILABLE_IN_3_20
+void              gdk_attach_params_set_window_offset     (GdkAttachParams       *params,
+                                                           gint                   x,
+                                                           gint                   y);
+
+GDK_AVAILABLE_IN_3_20
+void              gdk_attach_params_set_position_callback (GdkAttachParams       *params,
+                                                           GdkAttachCallback      callback,
+                                                           gpointer               user_data,
+                                                           GDestroyNotify         destroy_notify);
+
+G_END_DECLS
+
+#endif /* __GDK_ATTACH_PARAMS_H__ */
diff --git a/gdk/gdkattachparamsprivate.h b/gdk/gdkattachparamsprivate.h
new file mode 100644
index 0000000..bd6dd3e
--- /dev/null
+++ b/gdk/gdkattachparamsprivate.h
@@ -0,0 +1,78 @@
+#ifndef __GDK_ATTACH_PARAMS_PRIVATE_H__
+#define __GDK_ATTACH_PARAMS_PRIVATE_H__
+
+#include "gdkattachparams.h"
+
+G_BEGIN_DECLS
+
+/*
+ * GdkAttachParams:
+ * @has_attach_rect: %TRUE if @attach_rect is valid
+ * @attach_rect: the attachment rectangle to attach the window to
+ * @attach_parent: the #GdkWindow that @attach_rect is relative to
+ * @attach_anchor: the anchoring point on @attach_rect
+ * @window_anchor: the anchoring point on the window
+ * @flip_x: %TRUE if the backend can try flipping the window horizontally
+ * @flip_y: %TRUE if the backend can try flipping the window vertically
+ * @attach_margin: the space to leave around @attach_rect
+ * @window_padding: the space between the window and its contents
+ * @offset_x: the horizontal offset to displace the window by
+ * @offset_y: the vertical offset to displace the window by
+ * @position_callback: a function to call when the final position is known
+ * @position_callback_user_data: additional data to pass to @position_callback
+ * @position_callback_destroy_notify: a function to free
+ *                                    @position_callback_user_data
+ *
+ * Opaque type containing the information needed to position a window relative
+ * to an attachment rectangle.
+ *
+ * Since: 3.20
+ */
+struct _GdkAttachParams
+{
+  /*< private >*/
+  gboolean has_attach_rect;
+  GdkRectangle attach_rect;
+  GdkWindow *attach_parent;
+
+  GdkWindowEdge attach_anchor;
+  GdkWindowEdge window_anchor;
+  gboolean flip_x;
+  gboolean flip_y;
+
+  GdkBorder attach_margin;
+  GdkBorder window_padding;
+  gint offset_x;
+  gint offset_y;
+
+  GdkAttachCallback attach_callback;
+  gpointer attach_user_data;
+  GDestroyNotify attach_destroy_notify;
+};
+
+void gdk_attach_params_choose_position            (const GdkAttachParams *params,
+                                                   gint                   width,
+                                                   gint                   height,
+                                                   const GdkRectangle    *bounds,
+                                                   gint                  *x,
+                                                   gint                  *y,
+                                                   gint                  *offset_x,
+                                                   gint                  *offset_y,
+                                                   gboolean              *flipped_x,
+                                                   gboolean              *flipped_y);
+
+void gdk_attach_params_choose_position_for_window (const GdkAttachParams *params,
+                                                   GdkWindow             *window,
+                                                   gint                  *x,
+                                                   gint                  *y,
+                                                   gint                  *offset_x,
+                                                   gint                  *offset_y,
+                                                   gboolean              *flipped_x,
+                                                   gboolean              *flipped_y);
+
+void gdk_attach_params_move_window                (const GdkAttachParams *params,
+                                                   GdkWindow             *window);
+
+G_END_DECLS
+
+#endif /* __GDK_ATTACH_PARAMS_PRIVATE_H__ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]