[gcompris] play piano, removed the need for the OK button



commit e1b18e4e22299f0caaf16506490874b04e6269b4
Author: Bruno Coudoin <bruno coudoin free fr>
Date:   Fri Sep 14 00:46:48 2012 +0200

    play piano, removed the need for the OK button
    
    Now we mark each note with a pass or fail icon so
    that we have been able to remove the OK button.
    
    Sadly the staff is scaled by 2 and thus my icons
    look terrible bad.

 src/piano_composition-activity/gcomprismusic.py    |   24 +++
 .../resources/piano_composition/Makefile.am        |    2 +
 .../resources/piano_composition/failed.svg         |  160 ++++++++++++++++++++
 .../resources/piano_composition/passed.svg         |  160 ++++++++++++++++++++
 src/play_piano-activity/play_piano.py              |   53 ++++---
 5 files changed, 374 insertions(+), 25 deletions(-)
---
diff --git a/src/piano_composition-activity/gcomprismusic.py b/src/piano_composition-activity/gcomprismusic.py
index d672ce9..bdfc64b 100644
--- a/src/piano_composition-activity/gcomprismusic.py
+++ b/src/piano_composition-activity/gcomprismusic.py
@@ -842,6 +842,7 @@ class Note():
         self.timers = []
         self.sharpNotation = sharpNotation # toggle to switch note between sharp notation
         # and flat notation, if applicable
+        self.success = None
 
     def play(self, widget=None, target=None, event=None):
         '''
@@ -968,6 +969,29 @@ class Note():
                   height=20,
                   )
 
+    def statusPassed(self):
+        ''' Mark this note with a passed icon'''
+        self.success = goocanvas.Image(
+            parent = self.rootitem,
+            pixbuf = gcompris.utils.load_pixmap("piano_composition/passed.svg"),
+            x = self.x,
+            y = self.y,
+            )
+
+    def statusFailed(self):
+        ''' Mark this note with a failed icon'''
+        self.success = goocanvas.Image(
+            parent = self.rootitem,
+            pixbuf = gcompris.utils.load_pixmap("piano_composition/failed.svg"),
+            x = self.x,
+            y = self.y,
+            )
+
+    def statusNone(self):
+        ''' Mark this note with no status icon'''
+        if self.success:
+            self.success.remove()
+
 class EighthNote(Note):
     '''
     an object inherited from Note, of specific duration (eighth length)
diff --git a/src/piano_composition-activity/resources/piano_composition/Makefile.am b/src/piano_composition-activity/resources/piano_composition/Makefile.am
index 5da3167..ef2dcd3 100644
--- a/src/piano_composition-activity/resources/piano_composition/Makefile.am
+++ b/src/piano_composition-activity/resources/piano_composition/Makefile.am
@@ -9,6 +9,7 @@ img_DATA = \
 	blacksharp.png \
 	button_front.svg \
 	eighthNote.png \
+	failed.svg \
 	flag.png \
 	gsoc2012.jpg \
 	halfNote.png \
@@ -17,6 +18,7 @@ img_DATA = \
 	melodies.desktop.in \
 	mozart.jpg \
 	note_highlight.png \
+	passed.svg \
 	quarterNote.png \
 	save.png \
 	trebleClef.png \
diff --git a/src/piano_composition-activity/resources/piano_composition/failed.svg b/src/piano_composition-activity/resources/piano_composition/failed.svg
new file mode 100644
index 0000000..e1b143e
--- /dev/null
+++ b/src/piano_composition-activity/resources/piano_composition/failed.svg
@@ -0,0 +1,160 @@
+<?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:xlink="http://www.w3.org/1999/xlink";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="10.000082"
+   height="10.070951"
+   id="svg4191"
+   version="1.1"
+   inkscape:version="0.48.3.1 r9886"
+   sodipodi:docname="failed.svg">
+  <defs
+     id="defs4193">
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2806"
+       id="linearGradient3179"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.122817,0,0,0.122817,678.77995,189.36269)"
+       x1="274.81769"
+       y1="284.70993"
+       x2="324.22366"
+       y2="480.46399" />
+    <linearGradient
+       id="linearGradient2806">
+      <stop
+         style="stop-color:white;stop-opacity:1;"
+         offset="0"
+         id="stop2808" />
+      <stop
+         style="stop-color:white;stop-opacity:0;"
+         offset="1"
+         id="stop2810" />
+    </linearGradient>
+    <linearGradient
+       y2="480.46399"
+       x2="324.22366"
+       y1="284.70993"
+       x1="274.81769"
+       gradientTransform="matrix(0.122817,0,0,0.122817,678.77995,189.36269)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4189"
+       xlink:href="#linearGradient2806"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2806-1"
+       id="linearGradient2490"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.122805,0,0,0.122805,678.81385,294.86499)"
+       x1="274.81769"
+       y1="284.70993"
+       x2="324.22366"
+       y2="480.46399" />
+    <linearGradient
+       id="linearGradient2806-1">
+      <stop
+         style="stop-color:white;stop-opacity:1;"
+         offset="0"
+         id="stop2808-6" />
+      <stop
+         style="stop-color:white;stop-opacity:0;"
+         offset="1"
+         id="stop2810-7" />
+    </linearGradient>
+    <linearGradient
+       y2="480.46399"
+       x2="324.22366"
+       y1="284.70993"
+       x1="274.81769"
+       gradientTransform="matrix(0.122805,0,0,0.122805,678.81385,294.86499)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4240"
+       xlink:href="#linearGradient2806-1"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2806-1"
+       id="linearGradient4303"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.122805,0,0,0.122805,678.81385,294.86499)"
+       x1="274.81769"
+       y1="284.70993"
+       x2="324.22366"
+       y2="480.46399" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="2.8"
+     inkscape:cx="-31.693362"
+     inkscape:cy="15.66458"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1210"
+     inkscape:window-height="871"
+     inkscape:window-x="83"
+     inkscape:window-y="88"
+     inkscape:window-maximized="0"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0" />
+  <metadata
+     id="metadata4196">
+    <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="Calque 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-303.72796,-387.95169)">
+    <g
+       style="display:inline"
+       transform="matrix(0.24390445,0,0,0.24563296,132.17167,332.1623)"
+       id="g3171">
+      <path
+         sodipodi:type="arc"
+         style="fill:#aa0000;fill-opacity:1;stroke-linecap:round"
+         id="path3173"
+         sodipodi:cx="-325.76718"
+         sodipodi:cy="322.08786"
+         sodipodi:rx="168.28732"
+         sodipodi:ry="168.28732"
+         d="m -157.47986,322.08786 c 0,92.94252 -75.3448,168.28732 -168.28732,168.28732 -92.94252,0 -168.28732,-75.3448 -168.28732,-168.28732 0,-92.94252 75.3448,-168.28732 168.28732,-168.28732 92.94252,0 168.28732,75.3448 168.28732,168.28732 z"
+         transform="matrix(-0.1087594,0,0,0.1087594,688.40496,212.75828)" />
+      <path
+         inkscape:connector-curvature="0"
+         style="fill:#f8d600;fill-opacity:1;stroke:#000000;stroke-linecap:round"
+         d="m 723.88565,227.61682 c -11.0409,0 -20,8.95908 -20,19.99998 0,11.04089 8.9591,20.00382 20,20.00382 11.0409,0 20,-8.96292 20,-20.00382 0,-11.0409 -8.9591,-19.99998 -20,-19.99998 z m -9.7026,7.93321 c 0.8498,-0.0315 1.749,0.30966 2.4449,1.00556 l 7.2577,7.25772 7.2577,-7.25772 c 1.2372,-1.23717 3.1183,-1.34918 4.218,-0.24947 1.0997,1.0997 0.9877,2.98082 -0.2495,4.21799 l -7.0888,7.09269 7.0888,7.09268 c 1.2372,1.23716 1.3492,3.11829 0.2495,4.21799 -1.0997,1.09971 -2.9808,0.98769 -4.218,-0.24947 l -7.2577,-7.25771 -7.2577,7.25771 c -1.2372,1.23718 -3.1183,1.34918 -4.218,0.24947 -1.0997,-1.0997 -0.9877,-2.98082 0.2494,-4.21799 l 7.0889,-7.09268 -7.0889,-7.09269 c -1.2371,-1.23716 -1.3491,-3.11829 -0.2494,-4.21799 0.4811,-0.48112 1.1122,-0.73159 1.7731,-0.75609 z"
+         id="path3175" />
+      <path
+         inkscape:connector-curvature="0"
+         style="fill:url(#linearGradient4189);fill-opacity:1;stroke-linecap:round"
+         d="m 742.49205,245.27303 c 0.0411,4.32814 -37.1605,3.93056 -37.1654,0 -0.0117,-9.34057 8.325,-16.92134 18.5827,-16.92134 10.2577,0 18.4942,7.58284 18.5827,16.92134 z"
+         id="path3177"
+         sodipodi:nodetypes="csss" />
+    </g>
+  </g>
+</svg>
diff --git a/src/piano_composition-activity/resources/piano_composition/passed.svg b/src/piano_composition-activity/resources/piano_composition/passed.svg
new file mode 100644
index 0000000..53e0718
--- /dev/null
+++ b/src/piano_composition-activity/resources/piano_composition/passed.svg
@@ -0,0 +1,160 @@
+<?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:xlink="http://www.w3.org/1999/xlink";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="9.9991474"
+   height="10.00092"
+   id="svg4191"
+   version="1.1"
+   inkscape:version="0.48.3.1 r9886"
+   sodipodi:docname="passed.svg">
+  <defs
+     id="defs4193">
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2806"
+       id="linearGradient3179"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.122817,0,0,0.122817,678.77995,189.36269)"
+       x1="274.81769"
+       y1="284.70993"
+       x2="324.22366"
+       y2="480.46399" />
+    <linearGradient
+       id="linearGradient2806">
+      <stop
+         style="stop-color:white;stop-opacity:1;"
+         offset="0"
+         id="stop2808" />
+      <stop
+         style="stop-color:white;stop-opacity:0;"
+         offset="1"
+         id="stop2810" />
+    </linearGradient>
+    <linearGradient
+       y2="480.46399"
+       x2="324.22366"
+       y1="284.70993"
+       x1="274.81769"
+       gradientTransform="matrix(0.122817,0,0,0.122817,678.77995,189.36269)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4189"
+       xlink:href="#linearGradient2806"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2806-1"
+       id="linearGradient2490"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.122805,0,0,0.122805,678.81385,294.86499)"
+       x1="274.81769"
+       y1="284.70993"
+       x2="324.22366"
+       y2="480.46399" />
+    <linearGradient
+       id="linearGradient2806-1">
+      <stop
+         style="stop-color:white;stop-opacity:1;"
+         offset="0"
+         id="stop2808-6" />
+      <stop
+         style="stop-color:white;stop-opacity:0;"
+         offset="1"
+         id="stop2810-7" />
+    </linearGradient>
+    <linearGradient
+       y2="480.46399"
+       x2="324.22366"
+       y1="284.70993"
+       x1="274.81769"
+       gradientTransform="matrix(0.122805,0,0,0.122805,678.81385,294.86499)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4240"
+       xlink:href="#linearGradient2806-1"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2806-1"
+       id="linearGradient4389"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.122805,0,0,0.122805,678.81385,294.86499)"
+       x1="274.81769"
+       y1="284.70993"
+       x2="324.22366"
+       y2="480.46399" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="5.6"
+     inkscape:cx="4.1677789"
+     inkscape:cy="-12.634861"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1281"
+     inkscape:window-height="871"
+     inkscape:window-x="83"
+     inkscape:window-y="88"
+     inkscape:window-maximized="0"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0" />
+  <metadata
+     id="metadata4196">
+    <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 />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Calque 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-289.44168,-388.10001)">
+    <g
+       style="display:inline"
+       id="CHECKED"
+       transform="matrix(0.24388164,0,0,0.24392489,117.90143,307.0331)">
+      <path
+         transform="matrix(-0.1087594,0,0,0.1087594,688.40496,317.87405)"
+         d="m -157.47986,322.08786 c 0,92.94252 -75.3448,168.28732 -168.28732,168.28732 -92.94252,0 -168.28732,-75.3448 -168.28732,-168.28732 0,-92.94252 75.3448,-168.28732 168.28732,-168.28732 92.94252,0 168.28732,75.3448 168.28732,168.28732 z"
+         sodipodi:ry="168.28732"
+         sodipodi:rx="168.28732"
+         sodipodi:cy="322.08786"
+         sodipodi:cx="-325.76718"
+         id="path17321"
+         style="fill:#2ca02c;fill-opacity:1;stroke-linecap:round"
+         sodipodi:type="arc" />
+      <path
+         id="path3728"
+         d="m 723.88395,332.84237 c -11.0398,0 -19.998,8.9582 -19.998,19.99803 0,11.03982 8.9582,20.00185 19.998,20.00185 11.0398,0 20.0018,-8.96203 20.0018,-20.00185 0,-11.03982 -8.962,-19.99803 -20.0018,-19.99803 z m 4.2982,8.26631 c 0.0585,-6.4e-4 0.1174,0.002 0.1765,0.004 0.0523,0.002 0.1048,0.007 0.1573,0.0115 0.007,4.8e-4 0.0128,-5.1e-4 0.0192,0 0.0976,0.008 0.197,0.0196 0.2955,0.0345 0.6222,0.0942 1.2567,0.33219 1.8459,0.72148 l 1.6579,1.09373 c 1.82,1.20248 2.495,3.37287 1.5159,4.86615 -0.7363,1.12292 -2.1907,1.53173 -3.642,1.15513 l -4.1868,13.37807 c -0.4804,1.53539 -2.0353,2.43321 -3.6074,2.15293 -0.008,-8.2e-4 -0.0153,-0.003 -0.0231,-0.004 -0.9587,-0.1091 -1.944,-0.61882 -2.7055,-1.48517 l -5.0887,-5.79103 c -1.4334,-1.63076 -1.5121,-3.89242 -0.1727,-5.07338 1.3393,-1.18095 3.5747,-0.81719 5.0081,0.81359 l 2.0455,2.32562 3.7494,-11.99652 c 0.0157,-0.0502 0.0357,-0.097 0.0537,-0.14583 0.002,-0.007 0.002,-0.0156 0.004,-0.023 0.0741,-0.26012 0.1886,-0.51118 0.3416,-
 0.74451 0.5419,-0.82645 1.4729,-1.26447 2.506,-1.28945 0.0168,-4.1e-4 0.0329,-0.004 0.0499,-0.004 z"
+         style="fill:#f8d600;fill-opacity:1;stroke:#000000;stroke-linecap:round"
+         inkscape:connector-curvature="0" />
+      <path
+         sodipodi:nodetypes="csss"
+         id="path3949"
+         d="m 742.51985,350.76986 c 0.041,4.32772 -37.1569,3.93017 -37.1618,0 -0.0117,-9.33966 8.3242,-16.91968 18.5809,-16.91968 10.2566,0 18.4923,7.58209 18.5809,16.91968 z"
+         style="fill:url(#linearGradient4389);fill-opacity:1;stroke-linecap:round"
+         inkscape:connector-curvature="0" />
+    </g>
+  </g>
+</svg>
diff --git a/src/play_piano-activity/play_piano.py b/src/play_piano-activity/play_piano.py
index d09513f..995e39b 100644
--- a/src/play_piano-activity/play_piano.py
+++ b/src/play_piano-activity/play_piano.py
@@ -46,7 +46,6 @@ class Gcompris_play_piano:
 
         self.metronomePlaying = False
 
-        self.timers = []
         self.afterBonus = None
 
     def start(self):
@@ -121,7 +120,7 @@ class Gcompris_play_piano:
         self.givenOption = []
         self.show_melody()
         self.kidsNoteList = []
-        self.piano = PianoKeyboard(250, 300, self.rootitem)
+        self.piano = PianoKeyboard(250, 305, self.rootitem)
         if level in [4, 5, 6, 12, 11, 10]:
             self.piano.blackKeys = True
 
@@ -142,16 +141,6 @@ class Gcompris_play_piano:
 
         gcompris.utils.item_focus_init(self.playButton, None)
 
-        # OK BUTTON
-        self.okButton = goocanvas.Svg(parent=self.rootitem,
-                                      svg_handle=gcompris.skin.svg_get(),
-                                      svg_id="#OK"
-                                      )
-        self.okButton.scale(1.4, 1.4)
-        self.okButton.translate(-170, -400)
-        self.okButton.connect("button_press_event", self.ok_event)
-        gcompris.utils.item_focus_init(self.okButton, None)
-
         # ERASE BUTTON
         self.eraseButton = goocanvas.Image(
                 parent=self.rootitem,
@@ -162,17 +151,32 @@ class Gcompris_play_piano:
                 )
         self.eraseButton.connect("button_press_event", self.erase_entry)
         gcompris.utils.item_focus_init(self.eraseButton, None)
-        self.show_ok_erase(False)
+        self.show_erase(False)
 
-    def show_ok_erase(self, status):
+    def show_erase(self, status):
         goostatus = goocanvas.ITEM_VISIBLE if status else goocanvas.ITEM_INVISIBLE
-        self.okButton.props.visibility = goostatus
         self.eraseButton.props.visibility = goostatus
 
+    def clear_pass_or_fail(self):
+        for note in self.staff.noteList:
+            note.statusNone()
+
+    def mark_pass_or_fail(self, note_played):
+        ''' Find the displayed note matching the current play '''
+        ''' and mark it as pass or fail '''
+        current_note_number = len(self.kidsNoteList) - 1
+        if len(self.staff.noteList) > current_note_number:
+            note_on_staff = self.staff.noteList[current_note_number]
+            if note_played.numID == note_on_staff.numID:
+                note_on_staff.statusPassed()
+            else:
+                note_on_staff.statusFailed()
+                self.show_erase(True)
 
-    def keyboard_click(self, widget=None, target=None, event=None, numID=None):
-        self.show_ok_erase(True)
+        if len(self.staff.noteList) - 1 == current_note_number:
+            gobject.timeout_add(500, self.display_bonus)
 
+    def keyboard_click(self, widget=None, target=None, event=None, numID=None):
         if not numID:
             numID = target.numID
         if self.gcomprisBoard.level <= 6:
@@ -181,6 +185,7 @@ class Gcompris_play_piano:
             n = QuarterNote(numID, 'bassClef', self.staff.rootitem)
         n.play()
         self.kidsNoteList.append(numID)
+        self.mark_pass_or_fail(n)
 
     def generateMelody(self):
         level = self.gcomprisBoard.level
@@ -216,10 +221,9 @@ class Gcompris_play_piano:
                 note = QuarterNote(item, 'bassClef', self.staff.rootitem)
             self.staff.drawNote(note)
 
-        self.timers.append(gobject.timeout_add(500, self.staff.playComposition))
+        gobject.timeout_add(500, self.staff.playComposition)
 
-    def ok_event(self, widget=None, target=None, event=None):
-        self.show_ok_erase(False)
+    def display_bonus(self):
         if self.kidsNoteList == self.givenOption:
             self.afterBonus = self.nextChallenge
             gcompris.bonus.display(gcompris.bonus.WIN, gcompris.bonus.NOTE)
@@ -230,7 +234,7 @@ class Gcompris_play_piano:
             self.score -= 1
 
     def tryagain(self):
-        self.kidsNoteList = []
+        self.erase_entry()
         self.staff.playComposition
 
     def nextChallenge(self):
@@ -242,9 +246,10 @@ class Gcompris_play_piano:
             return
         self.show_melody()
 
-    def erase_entry(self, widget, target, event):
+    def erase_entry(self, widget=None, target=None, event=None):
         self.kidsNoteList = []
-        self.show_ok_erase(False)
+        self.show_erase(False)
+        self.clear_pass_or_fail()
 
     def end(self):
 
@@ -276,8 +281,6 @@ class Gcompris_play_piano:
             self.erase_entry()
         elif keyval == gtk.keysyms.Delete:
             self.erase_entry()
-        elif keyval == gtk.keysyms.Return:
-            self.ok_event()
         elif keyval == gtk.keysyms.space:
             self.staff.playComposition()
         else:



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