diff --git a/resources/project/-JxYjHfIG2f0LqYcrgF19Tk99yE/KV1s2Ld-0W0hjFA0isrLMMo5kjUd.xml b/resources/project/-JxYjHfIG2f0LqYcrgF19Tk99yE/KV1s2Ld-0W0hjFA0isrLMMo5kjUd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/-JxYjHfIG2f0LqYcrgF19Tk99yE/KV1s2Ld-0W0hjFA0isrLMMo5kjUd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/-JxYjHfIG2f0LqYcrgF19Tk99yE/KV1s2Ld-0W0hjFA0isrLMMo5kjUp.xml b/resources/project/-JxYjHfIG2f0LqYcrgF19Tk99yE/KV1s2Ld-0W0hjFA0isrLMMo5kjUp.xml
new file mode 100644
index 0000000..842de6a
--- /dev/null
+++ b/resources/project/-JxYjHfIG2f0LqYcrgF19Tk99yE/KV1s2Ld-0W0hjFA0isrLMMo5kjUp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/f5U-zHPhyZJzGDBuc9waDwYOGcw/j7l-39BD8pCe4pVJ3Zpcd_p-AoAd.xml b/resources/project/-JxYjHfIG2f0LqYcrgF19Tk99yE/n9uG-vwYfcNNBDUlsES42alQ7osd.xml
similarity index 77%
rename from resources/project/f5U-zHPhyZJzGDBuc9waDwYOGcw/j7l-39BD8pCe4pVJ3Zpcd_p-AoAd.xml
rename to resources/project/-JxYjHfIG2f0LqYcrgF19Tk99yE/n9uG-vwYfcNNBDUlsES42alQ7osd.xml
index 7de97d4..7a6326b 100644
--- a/resources/project/f5U-zHPhyZJzGDBuc9waDwYOGcw/j7l-39BD8pCe4pVJ3Zpcd_p-AoAd.xml
+++ b/resources/project/-JxYjHfIG2f0LqYcrgF19Tk99yE/n9uG-vwYfcNNBDUlsES42alQ7osd.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/resources/project/-JxYjHfIG2f0LqYcrgF19Tk99yE/n9uG-vwYfcNNBDUlsES42alQ7osp.xml b/resources/project/-JxYjHfIG2f0LqYcrgF19Tk99yE/n9uG-vwYfcNNBDUlsES42alQ7osp.xml
new file mode 100644
index 0000000..a584d3e
--- /dev/null
+++ b/resources/project/-JxYjHfIG2f0LqYcrgF19Tk99yE/n9uG-vwYfcNNBDUlsES42alQ7osp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/-JxYjHfIG2f0LqYcrgF19Tk99yE/pOTCnqYsmD7OkuVKeieTVCdAGbMd.xml b/resources/project/-JxYjHfIG2f0LqYcrgF19Tk99yE/pOTCnqYsmD7OkuVKeieTVCdAGbMd.xml
new file mode 100644
index 0000000..7a6326b
--- /dev/null
+++ b/resources/project/-JxYjHfIG2f0LqYcrgF19Tk99yE/pOTCnqYsmD7OkuVKeieTVCdAGbMd.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/-JxYjHfIG2f0LqYcrgF19Tk99yE/pOTCnqYsmD7OkuVKeieTVCdAGbMp.xml b/resources/project/-JxYjHfIG2f0LqYcrgF19Tk99yE/pOTCnqYsmD7OkuVKeieTVCdAGbMp.xml
new file mode 100644
index 0000000..e088033
--- /dev/null
+++ b/resources/project/-JxYjHfIG2f0LqYcrgF19Tk99yE/pOTCnqYsmD7OkuVKeieTVCdAGbMp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/6HGJ4oAVUNiKqcphEyZLEi-vhA0/Nzh9vZQ1tYYzIxcLd9gRb2x6LDYd.xml b/resources/project/6HGJ4oAVUNiKqcphEyZLEi-vhA0/Nzh9vZQ1tYYzIxcLd9gRb2x6LDYd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/6HGJ4oAVUNiKqcphEyZLEi-vhA0/Nzh9vZQ1tYYzIxcLd9gRb2x6LDYd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/6HGJ4oAVUNiKqcphEyZLEi-vhA0/Nzh9vZQ1tYYzIxcLd9gRb2x6LDYp.xml b/resources/project/6HGJ4oAVUNiKqcphEyZLEi-vhA0/Nzh9vZQ1tYYzIxcLd9gRb2x6LDYp.xml
new file mode 100644
index 0000000..842de6a
--- /dev/null
+++ b/resources/project/6HGJ4oAVUNiKqcphEyZLEi-vhA0/Nzh9vZQ1tYYzIxcLd9gRb2x6LDYp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/6HGJ4oAVUNiKqcphEyZLEi-vhA0/lWv1ZHtgNlIm2Vk_PwH8_N2dVzMd.xml b/resources/project/6HGJ4oAVUNiKqcphEyZLEi-vhA0/lWv1ZHtgNlIm2Vk_PwH8_N2dVzMd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/6HGJ4oAVUNiKqcphEyZLEi-vhA0/lWv1ZHtgNlIm2Vk_PwH8_N2dVzMd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/6HGJ4oAVUNiKqcphEyZLEi-vhA0/lWv1ZHtgNlIm2Vk_PwH8_N2dVzMp.xml b/resources/project/6HGJ4oAVUNiKqcphEyZLEi-vhA0/lWv1ZHtgNlIm2Vk_PwH8_N2dVzMp.xml
new file mode 100644
index 0000000..1289cd3
--- /dev/null
+++ b/resources/project/6HGJ4oAVUNiKqcphEyZLEi-vhA0/lWv1ZHtgNlIm2Vk_PwH8_N2dVzMp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/dUNZ2ztS0NUboxJMwhCN7xU8RfQd.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/dUNZ2ztS0NUboxJMwhCN7xU8RfQd.xml
new file mode 100644
index 0000000..f5e8559
--- /dev/null
+++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/dUNZ2ztS0NUboxJMwhCN7xU8RfQd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/dUNZ2ztS0NUboxJMwhCN7xU8RfQp.xml b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/dUNZ2ztS0NUboxJMwhCN7xU8RfQp.xml
new file mode 100644
index 0000000..36a7893
--- /dev/null
+++ b/resources/project/EEtUlUb-dLAdf0KpMVivaUlztwA/dUNZ2ztS0NUboxJMwhCN7xU8RfQp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/4Mf16AqQMryVgg9Qb5BFcQZ8Ah8d.xml b/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/4Mf16AqQMryVgg9Qb5BFcQZ8Ah8d.xml
new file mode 100644
index 0000000..7a6326b
--- /dev/null
+++ b/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/4Mf16AqQMryVgg9Qb5BFcQZ8Ah8d.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/4Mf16AqQMryVgg9Qb5BFcQZ8Ah8p.xml b/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/4Mf16AqQMryVgg9Qb5BFcQZ8Ah8p.xml
new file mode 100644
index 0000000..69c9318
--- /dev/null
+++ b/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/4Mf16AqQMryVgg9Qb5BFcQZ8Ah8p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/4nnQ4IhT4MRhI0R7xrST8iJ4vSod.xml b/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/4nnQ4IhT4MRhI0R7xrST8iJ4vSod.xml
new file mode 100644
index 0000000..7a6326b
--- /dev/null
+++ b/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/4nnQ4IhT4MRhI0R7xrST8iJ4vSod.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/4nnQ4IhT4MRhI0R7xrST8iJ4vSop.xml b/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/4nnQ4IhT4MRhI0R7xrST8iJ4vSop.xml
new file mode 100644
index 0000000..3c31afd
--- /dev/null
+++ b/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/4nnQ4IhT4MRhI0R7xrST8iJ4vSop.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/nAHYfd_rS38BFe70duAyYGHwlTod.xml b/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/nAHYfd_rS38BFe70duAyYGHwlTod.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/nAHYfd_rS38BFe70duAyYGHwlTod.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/nAHYfd_rS38BFe70duAyYGHwlTop.xml b/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/nAHYfd_rS38BFe70duAyYGHwlTop.xml
new file mode 100644
index 0000000..842de6a
--- /dev/null
+++ b/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/nAHYfd_rS38BFe70duAyYGHwlTop.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/rS-SrVQGG9QJaPff44wrg-lIEqcd.xml b/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/rS-SrVQGG9QJaPff44wrg-lIEqcd.xml
new file mode 100644
index 0000000..7a6326b
--- /dev/null
+++ b/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/rS-SrVQGG9QJaPff44wrg-lIEqcd.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/rS-SrVQGG9QJaPff44wrg-lIEqcp.xml b/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/rS-SrVQGG9QJaPff44wrg-lIEqcp.xml
new file mode 100644
index 0000000..b35188c
--- /dev/null
+++ b/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/rS-SrVQGG9QJaPff44wrg-lIEqcp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/vRX84xNPpvmouhyyGH1JtM3RJ9Ud.xml b/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/vRX84xNPpvmouhyyGH1JtM3RJ9Ud.xml
new file mode 100644
index 0000000..7a6326b
--- /dev/null
+++ b/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/vRX84xNPpvmouhyyGH1JtM3RJ9Ud.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/vRX84xNPpvmouhyyGH1JtM3RJ9Up.xml b/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/vRX84xNPpvmouhyyGH1JtM3RJ9Up.xml
new file mode 100644
index 0000000..8e96cf7
--- /dev/null
+++ b/resources/project/GkZhPrjzHBlWmLhS_XdEzkI_pOg/vRX84xNPpvmouhyyGH1JtM3RJ9Up.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/-LnRzEgZw3B4JXNVolYe4KUTiL0d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/-LnRzEgZw3B4JXNVolYe4KUTiL0d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/-LnRzEgZw3B4JXNVolYe4KUTiL0d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/-LnRzEgZw3B4JXNVolYe4KUTiL0p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/-LnRzEgZw3B4JXNVolYe4KUTiL0p.xml
new file mode 100644
index 0000000..fb98125
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/-LnRzEgZw3B4JXNVolYe4KUTiL0p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/-ZPV7X_Lt6R9QuONLQN99Oj2_RYd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/-ZPV7X_Lt6R9QuONLQN99Oj2_RYd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/-ZPV7X_Lt6R9QuONLQN99Oj2_RYd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/-ZPV7X_Lt6R9QuONLQN99Oj2_RYp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/-ZPV7X_Lt6R9QuONLQN99Oj2_RYp.xml
new file mode 100644
index 0000000..c0cdb06
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/-ZPV7X_Lt6R9QuONLQN99Oj2_RYp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/0shsyGoE68Bo8bSojwutQWXocgId.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/0shsyGoE68Bo8bSojwutQWXocgId.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/0shsyGoE68Bo8bSojwutQWXocgId.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/0shsyGoE68Bo8bSojwutQWXocgIp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/0shsyGoE68Bo8bSojwutQWXocgIp.xml
new file mode 100644
index 0000000..1e205e8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/0shsyGoE68Bo8bSojwutQWXocgIp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/1eNuiaSkxaLdrcX-v3j3-GPWN4gd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/1eNuiaSkxaLdrcX-v3j3-GPWN4gd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/1eNuiaSkxaLdrcX-v3j3-GPWN4gd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/1eNuiaSkxaLdrcX-v3j3-GPWN4gp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/1eNuiaSkxaLdrcX-v3j3-GPWN4gp.xml
new file mode 100644
index 0000000..c3f01e6
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/1eNuiaSkxaLdrcX-v3j3-GPWN4gp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/1xDXObTIu2CLt1MVtuFggij9GL0d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/1xDXObTIu2CLt1MVtuFggij9GL0d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/1xDXObTIu2CLt1MVtuFggij9GL0d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/1xDXObTIu2CLt1MVtuFggij9GL0p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/1xDXObTIu2CLt1MVtuFggij9GL0p.xml
new file mode 100644
index 0000000..d90e985
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/1xDXObTIu2CLt1MVtuFggij9GL0p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/2PI5OkJxG9HjUYsxAgjvT3nmA00d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/2PI5OkJxG9HjUYsxAgjvT3nmA00d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/2PI5OkJxG9HjUYsxAgjvT3nmA00d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/2PI5OkJxG9HjUYsxAgjvT3nmA00p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/2PI5OkJxG9HjUYsxAgjvT3nmA00p.xml
new file mode 100644
index 0000000..1b9dbea
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/2PI5OkJxG9HjUYsxAgjvT3nmA00p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/3D3wsejxqYHjHLpaic1ItgbIVsAd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/3D3wsejxqYHjHLpaic1ItgbIVsAd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/3D3wsejxqYHjHLpaic1ItgbIVsAd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/3D3wsejxqYHjHLpaic1ItgbIVsAp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/3D3wsejxqYHjHLpaic1ItgbIVsAp.xml
new file mode 100644
index 0000000..21ef7a0
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/3D3wsejxqYHjHLpaic1ItgbIVsAp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/4NXRl71D8ugF2LP8xSuTkZtyNood.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/4NXRl71D8ugF2LP8xSuTkZtyNood.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/4NXRl71D8ugF2LP8xSuTkZtyNood.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/4NXRl71D8ugF2LP8xSuTkZtyNoop.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/4NXRl71D8ugF2LP8xSuTkZtyNoop.xml
new file mode 100644
index 0000000..edae1f9
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/4NXRl71D8ugF2LP8xSuTkZtyNoop.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/4XFPz491HcVI4Kr8IvUoV26wEz0d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/4XFPz491HcVI4Kr8IvUoV26wEz0d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/4XFPz491HcVI4Kr8IvUoV26wEz0d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/4XFPz491HcVI4Kr8IvUoV26wEz0p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/4XFPz491HcVI4Kr8IvUoV26wEz0p.xml
new file mode 100644
index 0000000..aa26902
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/4XFPz491HcVI4Kr8IvUoV26wEz0p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/5tIKUM03_-xHyswlx4zyohiVhW0d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/5tIKUM03_-xHyswlx4zyohiVhW0d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/5tIKUM03_-xHyswlx4zyohiVhW0d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/5tIKUM03_-xHyswlx4zyohiVhW0p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/5tIKUM03_-xHyswlx4zyohiVhW0p.xml
new file mode 100644
index 0000000..787ff4f
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/5tIKUM03_-xHyswlx4zyohiVhW0p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/6FTJ9XNlyrBACyE8gyZYYwXYr9Yd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/6FTJ9XNlyrBACyE8gyZYYwXYr9Yd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/6FTJ9XNlyrBACyE8gyZYYwXYr9Yd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/6FTJ9XNlyrBACyE8gyZYYwXYr9Yp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/6FTJ9XNlyrBACyE8gyZYYwXYr9Yp.xml
new file mode 100644
index 0000000..2a612de
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/6FTJ9XNlyrBACyE8gyZYYwXYr9Yp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/6_P8NovWN4YmAflXvfM0h26m5wod.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/6_P8NovWN4YmAflXvfM0h26m5wod.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/6_P8NovWN4YmAflXvfM0h26m5wod.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/6_P8NovWN4YmAflXvfM0h26m5wop.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/6_P8NovWN4YmAflXvfM0h26m5wop.xml
new file mode 100644
index 0000000..95363f2
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/6_P8NovWN4YmAflXvfM0h26m5wop.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/6rpQ2aC93RgG2XwTQ3ORxGonSTMd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/6rpQ2aC93RgG2XwTQ3ORxGonSTMd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/6rpQ2aC93RgG2XwTQ3ORxGonSTMd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/6rpQ2aC93RgG2XwTQ3ORxGonSTMp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/6rpQ2aC93RgG2XwTQ3ORxGonSTMp.xml
new file mode 100644
index 0000000..8a459eb
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/6rpQ2aC93RgG2XwTQ3ORxGonSTMp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/78YuO6zagmOinhPRwH2BpQTgGaMd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/78YuO6zagmOinhPRwH2BpQTgGaMd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/78YuO6zagmOinhPRwH2BpQTgGaMd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/78YuO6zagmOinhPRwH2BpQTgGaMp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/78YuO6zagmOinhPRwH2BpQTgGaMp.xml
new file mode 100644
index 0000000..a5d0727
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/78YuO6zagmOinhPRwH2BpQTgGaMp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/78huPeom40gRvk5uAP7g3m0cWzId.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/78huPeom40gRvk5uAP7g3m0cWzId.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/78huPeom40gRvk5uAP7g3m0cWzId.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/78huPeom40gRvk5uAP7g3m0cWzIp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/78huPeom40gRvk5uAP7g3m0cWzIp.xml
new file mode 100644
index 0000000..27cf1eb
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/78huPeom40gRvk5uAP7g3m0cWzIp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/8cKjCNQoMzHWkhnSecTD3kV-CL8d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/8cKjCNQoMzHWkhnSecTD3kV-CL8d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/8cKjCNQoMzHWkhnSecTD3kV-CL8d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/8cKjCNQoMzHWkhnSecTD3kV-CL8p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/8cKjCNQoMzHWkhnSecTD3kV-CL8p.xml
new file mode 100644
index 0000000..4b6ebce
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/8cKjCNQoMzHWkhnSecTD3kV-CL8p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/9Exi8u4R-Ab4__OikrLvnAGGMpQd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/9Exi8u4R-Ab4__OikrLvnAGGMpQd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/9Exi8u4R-Ab4__OikrLvnAGGMpQd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/9Exi8u4R-Ab4__OikrLvnAGGMpQp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/9Exi8u4R-Ab4__OikrLvnAGGMpQp.xml
new file mode 100644
index 0000000..7fb3cc2
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/9Exi8u4R-Ab4__OikrLvnAGGMpQp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/AYthqDS45-wCWmxeCv1PqVNKZKQd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/AYthqDS45-wCWmxeCv1PqVNKZKQd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/AYthqDS45-wCWmxeCv1PqVNKZKQd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/AYthqDS45-wCWmxeCv1PqVNKZKQp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/AYthqDS45-wCWmxeCv1PqVNKZKQp.xml
new file mode 100644
index 0000000..94714a4
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/AYthqDS45-wCWmxeCv1PqVNKZKQp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/A_mw4eLKCluI7Q0aXNAQ9ex9fDEd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/A_mw4eLKCluI7Q0aXNAQ9ex9fDEd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/A_mw4eLKCluI7Q0aXNAQ9ex9fDEd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/A_mw4eLKCluI7Q0aXNAQ9ex9fDEp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/A_mw4eLKCluI7Q0aXNAQ9ex9fDEp.xml
new file mode 100644
index 0000000..22a01b5
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/A_mw4eLKCluI7Q0aXNAQ9ex9fDEp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/BBEDV9cg1vJAgIViHAfmzh29wB8d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/BBEDV9cg1vJAgIViHAfmzh29wB8d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/BBEDV9cg1vJAgIViHAfmzh29wB8d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/BBEDV9cg1vJAgIViHAfmzh29wB8p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/BBEDV9cg1vJAgIViHAfmzh29wB8p.xml
new file mode 100644
index 0000000..3462d67
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/BBEDV9cg1vJAgIViHAfmzh29wB8p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/BjNBOXRl4_VbY2DOEa-t3E5Hqx0d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/BjNBOXRl4_VbY2DOEa-t3E5Hqx0d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/BjNBOXRl4_VbY2DOEa-t3E5Hqx0d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/BjNBOXRl4_VbY2DOEa-t3E5Hqx0p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/BjNBOXRl4_VbY2DOEa-t3E5Hqx0p.xml
new file mode 100644
index 0000000..73cd84d
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/BjNBOXRl4_VbY2DOEa-t3E5Hqx0p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/CDf_fWX7tbO59rt_VX8OlU1DoCcd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/CDf_fWX7tbO59rt_VX8OlU1DoCcd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/CDf_fWX7tbO59rt_VX8OlU1DoCcd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/CDf_fWX7tbO59rt_VX8OlU1DoCcp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/CDf_fWX7tbO59rt_VX8OlU1DoCcp.xml
new file mode 100644
index 0000000..ea55ba4
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/CDf_fWX7tbO59rt_VX8OlU1DoCcp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/CjCq8BE9QlohkHN8Yuou_4DGp1Ed.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/CjCq8BE9QlohkHN8Yuou_4DGp1Ed.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/CjCq8BE9QlohkHN8Yuou_4DGp1Ed.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/CjCq8BE9QlohkHN8Yuou_4DGp1Ep.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/CjCq8BE9QlohkHN8Yuou_4DGp1Ep.xml
new file mode 100644
index 0000000..e913f5c
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/CjCq8BE9QlohkHN8Yuou_4DGp1Ep.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/DD0IbcPWlmkRipINXMGuPdrwlUcd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/DD0IbcPWlmkRipINXMGuPdrwlUcd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/DD0IbcPWlmkRipINXMGuPdrwlUcd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/DD0IbcPWlmkRipINXMGuPdrwlUcp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/DD0IbcPWlmkRipINXMGuPdrwlUcp.xml
new file mode 100644
index 0000000..9af1a6c
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/DD0IbcPWlmkRipINXMGuPdrwlUcp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/DK_9YRSnKxtDcR5D7SNdZYk3Cwkd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/DK_9YRSnKxtDcR5D7SNdZYk3Cwkd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/DK_9YRSnKxtDcR5D7SNdZYk3Cwkd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/DK_9YRSnKxtDcR5D7SNdZYk3Cwkp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/DK_9YRSnKxtDcR5D7SNdZYk3Cwkp.xml
new file mode 100644
index 0000000..5ae6034
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/DK_9YRSnKxtDcR5D7SNdZYk3Cwkp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/E0zR8L7qTIRduBJqIjs51H-lOKgd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/E0zR8L7qTIRduBJqIjs51H-lOKgd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/E0zR8L7qTIRduBJqIjs51H-lOKgd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/E0zR8L7qTIRduBJqIjs51H-lOKgp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/E0zR8L7qTIRduBJqIjs51H-lOKgp.xml
new file mode 100644
index 0000000..36250c3
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/E0zR8L7qTIRduBJqIjs51H-lOKgp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Ehg5ssigxuKYX4nWHdFawaDBM9Qd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Ehg5ssigxuKYX4nWHdFawaDBM9Qd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Ehg5ssigxuKYX4nWHdFawaDBM9Qd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Ehg5ssigxuKYX4nWHdFawaDBM9Qp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Ehg5ssigxuKYX4nWHdFawaDBM9Qp.xml
new file mode 100644
index 0000000..20d5797
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Ehg5ssigxuKYX4nWHdFawaDBM9Qp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/EildXD-TnQKgBVerLQ_iLW6mqW4d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/EildXD-TnQKgBVerLQ_iLW6mqW4d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/EildXD-TnQKgBVerLQ_iLW6mqW4d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/EildXD-TnQKgBVerLQ_iLW6mqW4p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/EildXD-TnQKgBVerLQ_iLW6mqW4p.xml
new file mode 100644
index 0000000..3d9e580
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/EildXD-TnQKgBVerLQ_iLW6mqW4p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/EngcY-lnzU-enZDZocG9-dW2NHgd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/EngcY-lnzU-enZDZocG9-dW2NHgd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/EngcY-lnzU-enZDZocG9-dW2NHgd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/EngcY-lnzU-enZDZocG9-dW2NHgp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/EngcY-lnzU-enZDZocG9-dW2NHgp.xml
new file mode 100644
index 0000000..bcdfa8b
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/EngcY-lnzU-enZDZocG9-dW2NHgp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Eu1sbk12e_8ToscQX3YuiAjmJYkd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Eu1sbk12e_8ToscQX3YuiAjmJYkd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Eu1sbk12e_8ToscQX3YuiAjmJYkd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Eu1sbk12e_8ToscQX3YuiAjmJYkp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Eu1sbk12e_8ToscQX3YuiAjmJYkp.xml
new file mode 100644
index 0000000..0efa537
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Eu1sbk12e_8ToscQX3YuiAjmJYkp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/FfXf71tvJ32oWJsmHgNlC7GPgJkd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/FfXf71tvJ32oWJsmHgNlC7GPgJkd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/FfXf71tvJ32oWJsmHgNlC7GPgJkd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/FfXf71tvJ32oWJsmHgNlC7GPgJkp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/FfXf71tvJ32oWJsmHgNlC7GPgJkp.xml
new file mode 100644
index 0000000..23c4dc2
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/FfXf71tvJ32oWJsmHgNlC7GPgJkp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Fo4SM-xe91kNLH409aYhjjx8j78d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Fo4SM-xe91kNLH409aYhjjx8j78d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Fo4SM-xe91kNLH409aYhjjx8j78d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Fo4SM-xe91kNLH409aYhjjx8j78p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Fo4SM-xe91kNLH409aYhjjx8j78p.xml
new file mode 100644
index 0000000..f0c7519
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Fo4SM-xe91kNLH409aYhjjx8j78p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/FrdlvTUafDHBz9vt7RogD9H2ttEd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/FrdlvTUafDHBz9vt7RogD9H2ttEd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/FrdlvTUafDHBz9vt7RogD9H2ttEd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/FrdlvTUafDHBz9vt7RogD9H2ttEp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/FrdlvTUafDHBz9vt7RogD9H2ttEp.xml
new file mode 100644
index 0000000..606363b
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/FrdlvTUafDHBz9vt7RogD9H2ttEp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/FtYLilYu5C_-iCD-DTNKT7LY8wQd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/FtYLilYu5C_-iCD-DTNKT7LY8wQd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/FtYLilYu5C_-iCD-DTNKT7LY8wQd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/FtYLilYu5C_-iCD-DTNKT7LY8wQp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/FtYLilYu5C_-iCD-DTNKT7LY8wQp.xml
new file mode 100644
index 0000000..41a0c73
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/FtYLilYu5C_-iCD-DTNKT7LY8wQp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/GDb-RrdmK14KvebvJ-hHRTpN_Ikd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/GDb-RrdmK14KvebvJ-hHRTpN_Ikd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/GDb-RrdmK14KvebvJ-hHRTpN_Ikd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/GDb-RrdmK14KvebvJ-hHRTpN_Ikp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/GDb-RrdmK14KvebvJ-hHRTpN_Ikp.xml
new file mode 100644
index 0000000..aae9b40
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/GDb-RrdmK14KvebvJ-hHRTpN_Ikp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/HdoPdpxGmhVNBe0LSaNx0Fktm3kd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/HdoPdpxGmhVNBe0LSaNx0Fktm3kd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/HdoPdpxGmhVNBe0LSaNx0Fktm3kd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/HdoPdpxGmhVNBe0LSaNx0Fktm3kp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/HdoPdpxGmhVNBe0LSaNx0Fktm3kp.xml
new file mode 100644
index 0000000..1cd979b
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/HdoPdpxGmhVNBe0LSaNx0Fktm3kp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/I7KgxYhGQInX7Zya2CqKwODpgLEd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/I7KgxYhGQInX7Zya2CqKwODpgLEd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/I7KgxYhGQInX7Zya2CqKwODpgLEd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/I7KgxYhGQInX7Zya2CqKwODpgLEp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/I7KgxYhGQInX7Zya2CqKwODpgLEp.xml
new file mode 100644
index 0000000..4a2c48f
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/I7KgxYhGQInX7Zya2CqKwODpgLEp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/IFqBAfshXYyTN0QR2d1NLVJ4ypUd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/IFqBAfshXYyTN0QR2d1NLVJ4ypUd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/IFqBAfshXYyTN0QR2d1NLVJ4ypUd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/IFqBAfshXYyTN0QR2d1NLVJ4ypUp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/IFqBAfshXYyTN0QR2d1NLVJ4ypUp.xml
new file mode 100644
index 0000000..22ca9a4
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/IFqBAfshXYyTN0QR2d1NLVJ4ypUp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/IWvlU7eT2evXTsEglz5pcrBZvWEd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/IWvlU7eT2evXTsEglz5pcrBZvWEd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/IWvlU7eT2evXTsEglz5pcrBZvWEd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/IWvlU7eT2evXTsEglz5pcrBZvWEp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/IWvlU7eT2evXTsEglz5pcrBZvWEp.xml
new file mode 100644
index 0000000..04877af
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/IWvlU7eT2evXTsEglz5pcrBZvWEp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/IcMei0b4FmboSA72EOY1dPkfVvMd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/IcMei0b4FmboSA72EOY1dPkfVvMd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/IcMei0b4FmboSA72EOY1dPkfVvMd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/IcMei0b4FmboSA72EOY1dPkfVvMp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/IcMei0b4FmboSA72EOY1dPkfVvMp.xml
new file mode 100644
index 0000000..c913c8b
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/IcMei0b4FmboSA72EOY1dPkfVvMp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/IfiJNwpQKfX_iW3-Srb8ty_nLGod.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/IfiJNwpQKfX_iW3-Srb8ty_nLGod.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/IfiJNwpQKfX_iW3-Srb8ty_nLGod.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/IfiJNwpQKfX_iW3-Srb8ty_nLGop.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/IfiJNwpQKfX_iW3-Srb8ty_nLGop.xml
new file mode 100644
index 0000000..fd3571a
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/IfiJNwpQKfX_iW3-Srb8ty_nLGop.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JHQKzZs3BC3-UScXrVH0eWpjEl0d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JHQKzZs3BC3-UScXrVH0eWpjEl0d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JHQKzZs3BC3-UScXrVH0eWpjEl0d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JHQKzZs3BC3-UScXrVH0eWpjEl0p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JHQKzZs3BC3-UScXrVH0eWpjEl0p.xml
new file mode 100644
index 0000000..7d72a89
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JHQKzZs3BC3-UScXrVH0eWpjEl0p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JIHnkj8gJDj-AiiCGSV8kA568Akd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JIHnkj8gJDj-AiiCGSV8kA568Akd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JIHnkj8gJDj-AiiCGSV8kA568Akd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JIHnkj8gJDj-AiiCGSV8kA568Akp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JIHnkj8gJDj-AiiCGSV8kA568Akp.xml
new file mode 100644
index 0000000..d49550d
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JIHnkj8gJDj-AiiCGSV8kA568Akp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JPfCEMRxhg3MfKNuAHpntpVtIv8d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JPfCEMRxhg3MfKNuAHpntpVtIv8d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JPfCEMRxhg3MfKNuAHpntpVtIv8d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JPfCEMRxhg3MfKNuAHpntpVtIv8p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JPfCEMRxhg3MfKNuAHpntpVtIv8p.xml
new file mode 100644
index 0000000..a941c46
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JPfCEMRxhg3MfKNuAHpntpVtIv8p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JnTTTkpQ-UC7nE2JHCq-VrqmPqUd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JnTTTkpQ-UC7nE2JHCq-VrqmPqUd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JnTTTkpQ-UC7nE2JHCq-VrqmPqUd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JnTTTkpQ-UC7nE2JHCq-VrqmPqUp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JnTTTkpQ-UC7nE2JHCq-VrqmPqUp.xml
new file mode 100644
index 0000000..0ce4cc7
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JnTTTkpQ-UC7nE2JHCq-VrqmPqUp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JnV4WfEjjQjZjScWr_xuUOWHstcd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JnV4WfEjjQjZjScWr_xuUOWHstcd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JnV4WfEjjQjZjScWr_xuUOWHstcd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JnV4WfEjjQjZjScWr_xuUOWHstcp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JnV4WfEjjQjZjScWr_xuUOWHstcp.xml
new file mode 100644
index 0000000..bf0c7dc
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/JnV4WfEjjQjZjScWr_xuUOWHstcp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Jyt6TV5Cy1cZzs9H7IXLfVBDe-Id.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Jyt6TV5Cy1cZzs9H7IXLfVBDe-Id.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Jyt6TV5Cy1cZzs9H7IXLfVBDe-Id.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Jyt6TV5Cy1cZzs9H7IXLfVBDe-Ip.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Jyt6TV5Cy1cZzs9H7IXLfVBDe-Ip.xml
new file mode 100644
index 0000000..2d255a5
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Jyt6TV5Cy1cZzs9H7IXLfVBDe-Ip.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/K126bYYl-m9ywjL6EvIhUjhFqu8d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/K126bYYl-m9ywjL6EvIhUjhFqu8d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/K126bYYl-m9ywjL6EvIhUjhFqu8d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/K126bYYl-m9ywjL6EvIhUjhFqu8p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/K126bYYl-m9ywjL6EvIhUjhFqu8p.xml
new file mode 100644
index 0000000..7d60c2b
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/K126bYYl-m9ywjL6EvIhUjhFqu8p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/KH3A6-vDo63PdT55J-vi08I_kIgd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/KH3A6-vDo63PdT55J-vi08I_kIgd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/KH3A6-vDo63PdT55J-vi08I_kIgd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/KH3A6-vDo63PdT55J-vi08I_kIgp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/KH3A6-vDo63PdT55J-vi08I_kIgp.xml
new file mode 100644
index 0000000..d301b08
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/KH3A6-vDo63PdT55J-vi08I_kIgp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/KNxWYsum9GL_OnbVxgsCAgrB7_Qd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/KNxWYsum9GL_OnbVxgsCAgrB7_Qd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/KNxWYsum9GL_OnbVxgsCAgrB7_Qd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/KNxWYsum9GL_OnbVxgsCAgrB7_Qp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/KNxWYsum9GL_OnbVxgsCAgrB7_Qp.xml
new file mode 100644
index 0000000..3f2c8e3
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/KNxWYsum9GL_OnbVxgsCAgrB7_Qp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Kr4b9Qk0I3K1xly2a8XuERsK6zsd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Kr4b9Qk0I3K1xly2a8XuERsK6zsd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Kr4b9Qk0I3K1xly2a8XuERsK6zsd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Kr4b9Qk0I3K1xly2a8XuERsK6zsp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Kr4b9Qk0I3K1xly2a8XuERsK6zsp.xml
new file mode 100644
index 0000000..407d8c1
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Kr4b9Qk0I3K1xly2a8XuERsK6zsp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Kup8sOk-ZDjgFhlIgqmVd478GPEd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Kup8sOk-ZDjgFhlIgqmVd478GPEd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Kup8sOk-ZDjgFhlIgqmVd478GPEd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Kup8sOk-ZDjgFhlIgqmVd478GPEp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Kup8sOk-ZDjgFhlIgqmVd478GPEp.xml
new file mode 100644
index 0000000..0417980
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Kup8sOk-ZDjgFhlIgqmVd478GPEp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/LAoJMKbsG49-30hJn9YvsBBPuEUd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/LAoJMKbsG49-30hJn9YvsBBPuEUd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/LAoJMKbsG49-30hJn9YvsBBPuEUd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/LAoJMKbsG49-30hJn9YvsBBPuEUp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/LAoJMKbsG49-30hJn9YvsBBPuEUp.xml
new file mode 100644
index 0000000..2d201ef
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/LAoJMKbsG49-30hJn9YvsBBPuEUp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/LnSdN0twT038lDg3oOzlNT8_J4gd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/LnSdN0twT038lDg3oOzlNT8_J4gd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/LnSdN0twT038lDg3oOzlNT8_J4gd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/LnSdN0twT038lDg3oOzlNT8_J4gp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/LnSdN0twT038lDg3oOzlNT8_J4gp.xml
new file mode 100644
index 0000000..46fcb09
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/LnSdN0twT038lDg3oOzlNT8_J4gp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Lv6NG40AEOTx3jcC5tJ0NH_dr2wd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Lv6NG40AEOTx3jcC5tJ0NH_dr2wd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Lv6NG40AEOTx3jcC5tJ0NH_dr2wd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Lv6NG40AEOTx3jcC5tJ0NH_dr2wp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Lv6NG40AEOTx3jcC5tJ0NH_dr2wp.xml
new file mode 100644
index 0000000..70c09c9
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Lv6NG40AEOTx3jcC5tJ0NH_dr2wp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/MDaxLkfI7JUlk1vLy5DoHcuaan0d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/MDaxLkfI7JUlk1vLy5DoHcuaan0d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/MDaxLkfI7JUlk1vLy5DoHcuaan0d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/MDaxLkfI7JUlk1vLy5DoHcuaan0p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/MDaxLkfI7JUlk1vLy5DoHcuaan0p.xml
new file mode 100644
index 0000000..884d545
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/MDaxLkfI7JUlk1vLy5DoHcuaan0p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/MhEGG76ZnWvZzklMGdi5ffgnWlwd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/MhEGG76ZnWvZzklMGdi5ffgnWlwd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/MhEGG76ZnWvZzklMGdi5ffgnWlwd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/MhEGG76ZnWvZzklMGdi5ffgnWlwp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/MhEGG76ZnWvZzklMGdi5ffgnWlwp.xml
new file mode 100644
index 0000000..cef8b9e
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/MhEGG76ZnWvZzklMGdi5ffgnWlwp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Nfo4HzuAOjBvx3yDQc2eROzCvCAd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Nfo4HzuAOjBvx3yDQc2eROzCvCAd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Nfo4HzuAOjBvx3yDQc2eROzCvCAd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Nfo4HzuAOjBvx3yDQc2eROzCvCAp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Nfo4HzuAOjBvx3yDQc2eROzCvCAp.xml
new file mode 100644
index 0000000..b2cdcea
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Nfo4HzuAOjBvx3yDQc2eROzCvCAp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Nk13NV76jk7CDb_1zAC3u-v-mFwd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Nk13NV76jk7CDb_1zAC3u-v-mFwd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Nk13NV76jk7CDb_1zAC3u-v-mFwd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Nk13NV76jk7CDb_1zAC3u-v-mFwp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Nk13NV76jk7CDb_1zAC3u-v-mFwp.xml
new file mode 100644
index 0000000..8bb3ee7
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Nk13NV76jk7CDb_1zAC3u-v-mFwp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/OIGQ5QWbMwSdQgkCps783q-poBEd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/OIGQ5QWbMwSdQgkCps783q-poBEd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/OIGQ5QWbMwSdQgkCps783q-poBEd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/OIGQ5QWbMwSdQgkCps783q-poBEp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/OIGQ5QWbMwSdQgkCps783q-poBEp.xml
new file mode 100644
index 0000000..1dcb9ac
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/OIGQ5QWbMwSdQgkCps783q-poBEp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/OveCAgQEkDd-qB8IiIYWPCBiBmod.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/OveCAgQEkDd-qB8IiIYWPCBiBmod.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/OveCAgQEkDd-qB8IiIYWPCBiBmod.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/OveCAgQEkDd-qB8IiIYWPCBiBmop.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/OveCAgQEkDd-qB8IiIYWPCBiBmop.xml
new file mode 100644
index 0000000..d5a2d53
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/OveCAgQEkDd-qB8IiIYWPCBiBmop.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/OyG2IaQcOphjOSJDUvJMNgVQB38d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/OyG2IaQcOphjOSJDUvJMNgVQB38d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/OyG2IaQcOphjOSJDUvJMNgVQB38d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/OyG2IaQcOphjOSJDUvJMNgVQB38p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/OyG2IaQcOphjOSJDUvJMNgVQB38p.xml
new file mode 100644
index 0000000..e936148
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/OyG2IaQcOphjOSJDUvJMNgVQB38p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/P26KqFjr3ni3tMbIUwZ--cGN1EEd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/P26KqFjr3ni3tMbIUwZ--cGN1EEd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/P26KqFjr3ni3tMbIUwZ--cGN1EEd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/P26KqFjr3ni3tMbIUwZ--cGN1EEp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/P26KqFjr3ni3tMbIUwZ--cGN1EEp.xml
new file mode 100644
index 0000000..9d27898
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/P26KqFjr3ni3tMbIUwZ--cGN1EEp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/RROQndlobe8Nt1bz3S4gkGEcNtEd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/RROQndlobe8Nt1bz3S4gkGEcNtEd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/RROQndlobe8Nt1bz3S4gkGEcNtEd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/RROQndlobe8Nt1bz3S4gkGEcNtEp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/RROQndlobe8Nt1bz3S4gkGEcNtEp.xml
new file mode 100644
index 0000000..481b409
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/RROQndlobe8Nt1bz3S4gkGEcNtEp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/RgyPztRvTld5qteGIcTyZcjLVx0d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/RgyPztRvTld5qteGIcTyZcjLVx0d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/RgyPztRvTld5qteGIcTyZcjLVx0d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/RgyPztRvTld5qteGIcTyZcjLVx0p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/RgyPztRvTld5qteGIcTyZcjLVx0p.xml
new file mode 100644
index 0000000..618219f
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/RgyPztRvTld5qteGIcTyZcjLVx0p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/S4aOftBX1aER9ZCkU3NoZjupayAd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/S4aOftBX1aER9ZCkU3NoZjupayAd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/S4aOftBX1aER9ZCkU3NoZjupayAd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/S4aOftBX1aER9ZCkU3NoZjupayAp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/S4aOftBX1aER9ZCkU3NoZjupayAp.xml
new file mode 100644
index 0000000..78d9343
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/S4aOftBX1aER9ZCkU3NoZjupayAp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/SEzduMqOjDte-HGsUKkov5Wzg94d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/SEzduMqOjDte-HGsUKkov5Wzg94d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/SEzduMqOjDte-HGsUKkov5Wzg94d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/SEzduMqOjDte-HGsUKkov5Wzg94p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/SEzduMqOjDte-HGsUKkov5Wzg94p.xml
new file mode 100644
index 0000000..385ee81
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/SEzduMqOjDte-HGsUKkov5Wzg94p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/SInRnpxCZu17UI0oYRfsRBsa8dEd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/SInRnpxCZu17UI0oYRfsRBsa8dEd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/SInRnpxCZu17UI0oYRfsRBsa8dEd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/SInRnpxCZu17UI0oYRfsRBsa8dEp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/SInRnpxCZu17UI0oYRfsRBsa8dEp.xml
new file mode 100644
index 0000000..6eaee37
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/SInRnpxCZu17UI0oYRfsRBsa8dEp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/SM_m5wWQDiuVal1R787O0IstkQMd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/SM_m5wWQDiuVal1R787O0IstkQMd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/SM_m5wWQDiuVal1R787O0IstkQMd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/SM_m5wWQDiuVal1R787O0IstkQMp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/SM_m5wWQDiuVal1R787O0IstkQMp.xml
new file mode 100644
index 0000000..8a3091a
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/SM_m5wWQDiuVal1R787O0IstkQMp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/TM5PCqnzOnrpCgUxSNPSvaFxC_wd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/TM5PCqnzOnrpCgUxSNPSvaFxC_wd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/TM5PCqnzOnrpCgUxSNPSvaFxC_wd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/TM5PCqnzOnrpCgUxSNPSvaFxC_wp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/TM5PCqnzOnrpCgUxSNPSvaFxC_wp.xml
new file mode 100644
index 0000000..d4633a9
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/TM5PCqnzOnrpCgUxSNPSvaFxC_wp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/TQn4vLvTZg8c0ZpRqY_MO8YtoBUd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/TQn4vLvTZg8c0ZpRqY_MO8YtoBUd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/TQn4vLvTZg8c0ZpRqY_MO8YtoBUd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/TQn4vLvTZg8c0ZpRqY_MO8YtoBUp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/TQn4vLvTZg8c0ZpRqY_MO8YtoBUp.xml
new file mode 100644
index 0000000..b7eb815
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/TQn4vLvTZg8c0ZpRqY_MO8YtoBUp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/ThvjIAM900cyvB3sK8UhqzTNwEod.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/ThvjIAM900cyvB3sK8UhqzTNwEod.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/ThvjIAM900cyvB3sK8UhqzTNwEod.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/ThvjIAM900cyvB3sK8UhqzTNwEop.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/ThvjIAM900cyvB3sK8UhqzTNwEop.xml
new file mode 100644
index 0000000..a23c345
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/ThvjIAM900cyvB3sK8UhqzTNwEop.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/TpO43nC8CleC8ICIyIBEM9PmpnMd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/TpO43nC8CleC8ICIyIBEM9PmpnMd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/TpO43nC8CleC8ICIyIBEM9PmpnMd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/TpO43nC8CleC8ICIyIBEM9PmpnMp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/TpO43nC8CleC8ICIyIBEM9PmpnMp.xml
new file mode 100644
index 0000000..c6a361a
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/TpO43nC8CleC8ICIyIBEM9PmpnMp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/UVULaZTb-3XYLmGolCfk0lSzlL8d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/UVULaZTb-3XYLmGolCfk0lSzlL8d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/UVULaZTb-3XYLmGolCfk0lSzlL8d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/UVULaZTb-3XYLmGolCfk0lSzlL8p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/UVULaZTb-3XYLmGolCfk0lSzlL8p.xml
new file mode 100644
index 0000000..e70e0f3
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/UVULaZTb-3XYLmGolCfk0lSzlL8p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/V2m0w0LuJD0aWiOhsZj7UZkKe0gd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/V2m0w0LuJD0aWiOhsZj7UZkKe0gd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/V2m0w0LuJD0aWiOhsZj7UZkKe0gd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/V2m0w0LuJD0aWiOhsZj7UZkKe0gp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/V2m0w0LuJD0aWiOhsZj7UZkKe0gp.xml
new file mode 100644
index 0000000..955a710
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/V2m0w0LuJD0aWiOhsZj7UZkKe0gp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/WMKR2BPH9zErg6KqBPGZHojWnqYd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/WMKR2BPH9zErg6KqBPGZHojWnqYd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/WMKR2BPH9zErg6KqBPGZHojWnqYd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/WMKR2BPH9zErg6KqBPGZHojWnqYp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/WMKR2BPH9zErg6KqBPGZHojWnqYp.xml
new file mode 100644
index 0000000..41b7c69
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/WMKR2BPH9zErg6KqBPGZHojWnqYp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/WQMW1Kj9YgiLAU5TWphB5bmpnK0d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/WQMW1Kj9YgiLAU5TWphB5bmpnK0d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/WQMW1Kj9YgiLAU5TWphB5bmpnK0d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/WQMW1Kj9YgiLAU5TWphB5bmpnK0p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/WQMW1Kj9YgiLAU5TWphB5bmpnK0p.xml
new file mode 100644
index 0000000..535ad47
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/WQMW1Kj9YgiLAU5TWphB5bmpnK0p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/WmOcWFwF5CwLLBPB31rwjYpCYhId.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/WmOcWFwF5CwLLBPB31rwjYpCYhId.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/WmOcWFwF5CwLLBPB31rwjYpCYhId.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/WmOcWFwF5CwLLBPB31rwjYpCYhIp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/WmOcWFwF5CwLLBPB31rwjYpCYhIp.xml
new file mode 100644
index 0000000..b94415a
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/WmOcWFwF5CwLLBPB31rwjYpCYhIp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/XV5AqYe0wPAkQCa6oxYxw8vbAbkd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/XV5AqYe0wPAkQCa6oxYxw8vbAbkd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/XV5AqYe0wPAkQCa6oxYxw8vbAbkd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/XV5AqYe0wPAkQCa6oxYxw8vbAbkp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/XV5AqYe0wPAkQCa6oxYxw8vbAbkp.xml
new file mode 100644
index 0000000..0531b9b
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/XV5AqYe0wPAkQCa6oxYxw8vbAbkp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Y5zxLbQGpW0_ZG8UdMsIFrbsDLod.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Y5zxLbQGpW0_ZG8UdMsIFrbsDLod.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Y5zxLbQGpW0_ZG8UdMsIFrbsDLod.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Y5zxLbQGpW0_ZG8UdMsIFrbsDLop.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Y5zxLbQGpW0_ZG8UdMsIFrbsDLop.xml
new file mode 100644
index 0000000..93741c6
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/Y5zxLbQGpW0_ZG8UdMsIFrbsDLop.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/YmawGbMNhNN8o0dFSdxrkWDjQq8d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/YmawGbMNhNN8o0dFSdxrkWDjQq8d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/YmawGbMNhNN8o0dFSdxrkWDjQq8d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/YmawGbMNhNN8o0dFSdxrkWDjQq8p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/YmawGbMNhNN8o0dFSdxrkWDjQq8p.xml
new file mode 100644
index 0000000..0c36665
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/YmawGbMNhNN8o0dFSdxrkWDjQq8p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/_tNTJ6CzCJJ2QQsVTFNWIvRiZ0Id.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/_tNTJ6CzCJJ2QQsVTFNWIvRiZ0Id.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/_tNTJ6CzCJJ2QQsVTFNWIvRiZ0Id.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/_tNTJ6CzCJJ2QQsVTFNWIvRiZ0Ip.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/_tNTJ6CzCJJ2QQsVTFNWIvRiZ0Ip.xml
new file mode 100644
index 0000000..949321f
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/_tNTJ6CzCJJ2QQsVTFNWIvRiZ0Ip.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/avuM3n-wIEFUNiD3cGFkqefQrzwd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/avuM3n-wIEFUNiD3cGFkqefQrzwd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/avuM3n-wIEFUNiD3cGFkqefQrzwd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/avuM3n-wIEFUNiD3cGFkqefQrzwp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/avuM3n-wIEFUNiD3cGFkqefQrzwp.xml
new file mode 100644
index 0000000..0ba390b
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/avuM3n-wIEFUNiD3cGFkqefQrzwp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/d4-6FXjycg0qJUvyMRBOTqEm0SEd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/d4-6FXjycg0qJUvyMRBOTqEm0SEd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/d4-6FXjycg0qJUvyMRBOTqEm0SEd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/d4-6FXjycg0qJUvyMRBOTqEm0SEp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/d4-6FXjycg0qJUvyMRBOTqEm0SEp.xml
new file mode 100644
index 0000000..44a464c
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/d4-6FXjycg0qJUvyMRBOTqEm0SEp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/d4V63nzxnUq4Tg8oA0h_tjmUc-Ad.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/d4V63nzxnUq4Tg8oA0h_tjmUc-Ad.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/d4V63nzxnUq4Tg8oA0h_tjmUc-Ad.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/d4V63nzxnUq4Tg8oA0h_tjmUc-Ap.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/d4V63nzxnUq4Tg8oA0h_tjmUc-Ap.xml
new file mode 100644
index 0000000..8a457ed
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/d4V63nzxnUq4Tg8oA0h_tjmUc-Ap.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/e39mGRi0p_oi6XXNtWgbWQN-Ookd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/e39mGRi0p_oi6XXNtWgbWQN-Ookd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/e39mGRi0p_oi6XXNtWgbWQN-Ookd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/e39mGRi0p_oi6XXNtWgbWQN-Ookp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/e39mGRi0p_oi6XXNtWgbWQN-Ookp.xml
new file mode 100644
index 0000000..ca695ef
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/e39mGRi0p_oi6XXNtWgbWQN-Ookp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/gZmcfFSZa0N48NpNZO54H9_dfrId.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/gZmcfFSZa0N48NpNZO54H9_dfrId.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/gZmcfFSZa0N48NpNZO54H9_dfrId.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/gZmcfFSZa0N48NpNZO54H9_dfrIp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/gZmcfFSZa0N48NpNZO54H9_dfrIp.xml
new file mode 100644
index 0000000..e817990
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/gZmcfFSZa0N48NpNZO54H9_dfrIp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/gzoP_Nuv2MX746HxcIYnylPH81gd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/gzoP_Nuv2MX746HxcIYnylPH81gd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/gzoP_Nuv2MX746HxcIYnylPH81gd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/gzoP_Nuv2MX746HxcIYnylPH81gp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/gzoP_Nuv2MX746HxcIYnylPH81gp.xml
new file mode 100644
index 0000000..2e1c9ff
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/gzoP_Nuv2MX746HxcIYnylPH81gp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/hV3KClyFfr8d21rPlDsajnc2N5Qd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/hV3KClyFfr8d21rPlDsajnc2N5Qd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/hV3KClyFfr8d21rPlDsajnc2N5Qd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/hV3KClyFfr8d21rPlDsajnc2N5Qp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/hV3KClyFfr8d21rPlDsajnc2N5Qp.xml
new file mode 100644
index 0000000..b4ad982
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/hV3KClyFfr8d21rPlDsajnc2N5Qp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/hthC8sA4V56DA6quP21QaMGJ5Nkd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/hthC8sA4V56DA6quP21QaMGJ5Nkd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/hthC8sA4V56DA6quP21QaMGJ5Nkd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/hthC8sA4V56DA6quP21QaMGJ5Nkp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/hthC8sA4V56DA6quP21QaMGJ5Nkp.xml
new file mode 100644
index 0000000..4c6b939
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/hthC8sA4V56DA6quP21QaMGJ5Nkp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/hz57_J-BrWMaKrdTzfp44ArFJqYd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/hz57_J-BrWMaKrdTzfp44ArFJqYd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/hz57_J-BrWMaKrdTzfp44ArFJqYd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/hz57_J-BrWMaKrdTzfp44ArFJqYp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/hz57_J-BrWMaKrdTzfp44ArFJqYp.xml
new file mode 100644
index 0000000..9103ddf
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/hz57_J-BrWMaKrdTzfp44ArFJqYp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/j0LunrHNRGqhmkJjHDfV2Sev-7Ed.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/j0LunrHNRGqhmkJjHDfV2Sev-7Ed.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/j0LunrHNRGqhmkJjHDfV2Sev-7Ed.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/j0LunrHNRGqhmkJjHDfV2Sev-7Ep.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/j0LunrHNRGqhmkJjHDfV2Sev-7Ep.xml
new file mode 100644
index 0000000..7f5c4bc
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/j0LunrHNRGqhmkJjHDfV2Sev-7Ep.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/j9s61eqDqWM70kVkvBnVoO-HnDwd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/j9s61eqDqWM70kVkvBnVoO-HnDwd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/j9s61eqDqWM70kVkvBnVoO-HnDwd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/j9s61eqDqWM70kVkvBnVoO-HnDwp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/j9s61eqDqWM70kVkvBnVoO-HnDwp.xml
new file mode 100644
index 0000000..237442b
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/j9s61eqDqWM70kVkvBnVoO-HnDwp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/jd9M9mvXdG8MwShRMdZ6TNXArOQd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/jd9M9mvXdG8MwShRMdZ6TNXArOQd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/jd9M9mvXdG8MwShRMdZ6TNXArOQd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/jd9M9mvXdG8MwShRMdZ6TNXArOQp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/jd9M9mvXdG8MwShRMdZ6TNXArOQp.xml
new file mode 100644
index 0000000..4920ef2
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/jd9M9mvXdG8MwShRMdZ6TNXArOQp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/jvaTQuMAkn9USfgJdmqQqUwxN_kd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/jvaTQuMAkn9USfgJdmqQqUwxN_kd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/jvaTQuMAkn9USfgJdmqQqUwxN_kd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/jvaTQuMAkn9USfgJdmqQqUwxN_kp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/jvaTQuMAkn9USfgJdmqQqUwxN_kp.xml
new file mode 100644
index 0000000..c26c4fd
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/jvaTQuMAkn9USfgJdmqQqUwxN_kp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/kDeg2zoipVruEYdMy86dHEFL9T8d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/kDeg2zoipVruEYdMy86dHEFL9T8d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/kDeg2zoipVruEYdMy86dHEFL9T8d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/kDeg2zoipVruEYdMy86dHEFL9T8p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/kDeg2zoipVruEYdMy86dHEFL9T8p.xml
new file mode 100644
index 0000000..3e5d125
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/kDeg2zoipVruEYdMy86dHEFL9T8p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/kLvmxdMAJI4hKG08o1_zsyktUkMd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/kLvmxdMAJI4hKG08o1_zsyktUkMd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/kLvmxdMAJI4hKG08o1_zsyktUkMd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/kLvmxdMAJI4hKG08o1_zsyktUkMp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/kLvmxdMAJI4hKG08o1_zsyktUkMp.xml
new file mode 100644
index 0000000..cc479f5
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/kLvmxdMAJI4hKG08o1_zsyktUkMp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/kx7DemnB9kkjc72NiPggv2wUBJYd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/kx7DemnB9kkjc72NiPggv2wUBJYd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/kx7DemnB9kkjc72NiPggv2wUBJYd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/kx7DemnB9kkjc72NiPggv2wUBJYp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/kx7DemnB9kkjc72NiPggv2wUBJYp.xml
new file mode 100644
index 0000000..d1a8c1c
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/kx7DemnB9kkjc72NiPggv2wUBJYp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/l7r1LHQv1w0B82Xu-LcRl1usyyMd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/l7r1LHQv1w0B82Xu-LcRl1usyyMd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/l7r1LHQv1w0B82Xu-LcRl1usyyMd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/l7r1LHQv1w0B82Xu-LcRl1usyyMp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/l7r1LHQv1w0B82Xu-LcRl1usyyMp.xml
new file mode 100644
index 0000000..f443990
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/l7r1LHQv1w0B82Xu-LcRl1usyyMp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/la2mT-dAbNjlviaG-iwITaEsHAgd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/la2mT-dAbNjlviaG-iwITaEsHAgd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/la2mT-dAbNjlviaG-iwITaEsHAgd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/la2mT-dAbNjlviaG-iwITaEsHAgp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/la2mT-dAbNjlviaG-iwITaEsHAgp.xml
new file mode 100644
index 0000000..ec23465
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/la2mT-dAbNjlviaG-iwITaEsHAgp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/mVZHI7_rQt5phCaz3ALeCvgC8eAd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/mVZHI7_rQt5phCaz3ALeCvgC8eAd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/mVZHI7_rQt5phCaz3ALeCvgC8eAd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/mVZHI7_rQt5phCaz3ALeCvgC8eAp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/mVZHI7_rQt5phCaz3ALeCvgC8eAp.xml
new file mode 100644
index 0000000..832d1f6
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/mVZHI7_rQt5phCaz3ALeCvgC8eAp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/nF_p0cRlzyLEf6Zs7fPHmmsvTLod.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/nF_p0cRlzyLEf6Zs7fPHmmsvTLod.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/nF_p0cRlzyLEf6Zs7fPHmmsvTLod.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/nF_p0cRlzyLEf6Zs7fPHmmsvTLop.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/nF_p0cRlzyLEf6Zs7fPHmmsvTLop.xml
new file mode 100644
index 0000000..cb83a03
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/nF_p0cRlzyLEf6Zs7fPHmmsvTLop.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/nGC8nilmkdwYzkPZgBSxpyImVIMd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/nGC8nilmkdwYzkPZgBSxpyImVIMd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/nGC8nilmkdwYzkPZgBSxpyImVIMd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/nGC8nilmkdwYzkPZgBSxpyImVIMp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/nGC8nilmkdwYzkPZgBSxpyImVIMp.xml
new file mode 100644
index 0000000..4fc6627
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/nGC8nilmkdwYzkPZgBSxpyImVIMp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/o0XVdTDgCyyKcGMT6m_qWVxT5kcd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/o0XVdTDgCyyKcGMT6m_qWVxT5kcd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/o0XVdTDgCyyKcGMT6m_qWVxT5kcd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/o0XVdTDgCyyKcGMT6m_qWVxT5kcp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/o0XVdTDgCyyKcGMT6m_qWVxT5kcp.xml
new file mode 100644
index 0000000..e460f2a
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/o0XVdTDgCyyKcGMT6m_qWVxT5kcp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/of52PojRlvtFYvcuwH6LSS845DMd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/of52PojRlvtFYvcuwH6LSS845DMd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/of52PojRlvtFYvcuwH6LSS845DMd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/of52PojRlvtFYvcuwH6LSS845DMp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/of52PojRlvtFYvcuwH6LSS845DMp.xml
new file mode 100644
index 0000000..b95270c
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/of52PojRlvtFYvcuwH6LSS845DMp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/om07cewA8BMSHssjdix9KsKqqbsd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/om07cewA8BMSHssjdix9KsKqqbsd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/om07cewA8BMSHssjdix9KsKqqbsd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/om07cewA8BMSHssjdix9KsKqqbsp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/om07cewA8BMSHssjdix9KsKqqbsp.xml
new file mode 100644
index 0000000..8b239f8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/om07cewA8BMSHssjdix9KsKqqbsp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/p3L-1OpFTacwKfni1FuVcoHH6fYd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/p3L-1OpFTacwKfni1FuVcoHH6fYd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/p3L-1OpFTacwKfni1FuVcoHH6fYd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/p3L-1OpFTacwKfni1FuVcoHH6fYp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/p3L-1OpFTacwKfni1FuVcoHH6fYp.xml
new file mode 100644
index 0000000..a2f4455
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/p3L-1OpFTacwKfni1FuVcoHH6fYp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/p6DkNRiNB3cTVxykfSLZGBqk5TEd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/p6DkNRiNB3cTVxykfSLZGBqk5TEd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/p6DkNRiNB3cTVxykfSLZGBqk5TEd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/p6DkNRiNB3cTVxykfSLZGBqk5TEp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/p6DkNRiNB3cTVxykfSLZGBqk5TEp.xml
new file mode 100644
index 0000000..432f876
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/p6DkNRiNB3cTVxykfSLZGBqk5TEp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/pB68nYd4ltvd4VTs5ryMVGmnoBAd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/pB68nYd4ltvd4VTs5ryMVGmnoBAd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/pB68nYd4ltvd4VTs5ryMVGmnoBAd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/pB68nYd4ltvd4VTs5ryMVGmnoBAp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/pB68nYd4ltvd4VTs5ryMVGmnoBAp.xml
new file mode 100644
index 0000000..adf11d7
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/pB68nYd4ltvd4VTs5ryMVGmnoBAp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/plfEyEPtfun_ZrWg7c51jrVZlY4d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/plfEyEPtfun_ZrWg7c51jrVZlY4d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/plfEyEPtfun_ZrWg7c51jrVZlY4d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/plfEyEPtfun_ZrWg7c51jrVZlY4p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/plfEyEPtfun_ZrWg7c51jrVZlY4p.xml
new file mode 100644
index 0000000..2b834d2
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/plfEyEPtfun_ZrWg7c51jrVZlY4p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/prqUb8L1rmKQaSZDLv8LDT2tWyQd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/prqUb8L1rmKQaSZDLv8LDT2tWyQd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/prqUb8L1rmKQaSZDLv8LDT2tWyQd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/prqUb8L1rmKQaSZDLv8LDT2tWyQp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/prqUb8L1rmKQaSZDLv8LDT2tWyQp.xml
new file mode 100644
index 0000000..5cb7341
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/prqUb8L1rmKQaSZDLv8LDT2tWyQp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/rOfyIIF_rPLhy3jdH7-zqPuMhbkd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/rOfyIIF_rPLhy3jdH7-zqPuMhbkd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/rOfyIIF_rPLhy3jdH7-zqPuMhbkd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/rOfyIIF_rPLhy3jdH7-zqPuMhbkp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/rOfyIIF_rPLhy3jdH7-zqPuMhbkp.xml
new file mode 100644
index 0000000..e2f79d6
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/rOfyIIF_rPLhy3jdH7-zqPuMhbkp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/rRMQby8U52npG_sDJKZjdcoBlz8d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/rRMQby8U52npG_sDJKZjdcoBlz8d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/rRMQby8U52npG_sDJKZjdcoBlz8d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/rRMQby8U52npG_sDJKZjdcoBlz8p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/rRMQby8U52npG_sDJKZjdcoBlz8p.xml
new file mode 100644
index 0000000..f492463
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/rRMQby8U52npG_sDJKZjdcoBlz8p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/sbLpKCAlGY6igStniCoIvzZNZ0cd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/sbLpKCAlGY6igStniCoIvzZNZ0cd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/sbLpKCAlGY6igStniCoIvzZNZ0cd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/sbLpKCAlGY6igStniCoIvzZNZ0cp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/sbLpKCAlGY6igStniCoIvzZNZ0cp.xml
new file mode 100644
index 0000000..2c44923
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/sbLpKCAlGY6igStniCoIvzZNZ0cp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/secoX6VKFdXeTlZ20EO8GA-Chmsd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/secoX6VKFdXeTlZ20EO8GA-Chmsd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/secoX6VKFdXeTlZ20EO8GA-Chmsd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/secoX6VKFdXeTlZ20EO8GA-Chmsp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/secoX6VKFdXeTlZ20EO8GA-Chmsp.xml
new file mode 100644
index 0000000..04293ae
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/secoX6VKFdXeTlZ20EO8GA-Chmsp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/t8ow2ztLXo-URRX08-OdIkX67VQd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/t8ow2ztLXo-URRX08-OdIkX67VQd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/t8ow2ztLXo-URRX08-OdIkX67VQd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/t8ow2ztLXo-URRX08-OdIkX67VQp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/t8ow2ztLXo-URRX08-OdIkX67VQp.xml
new file mode 100644
index 0000000..1d4cf87
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/t8ow2ztLXo-URRX08-OdIkX67VQp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/tRRA2GFpDQa60fgbah90kaTLUdod.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/tRRA2GFpDQa60fgbah90kaTLUdod.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/tRRA2GFpDQa60fgbah90kaTLUdod.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/tRRA2GFpDQa60fgbah90kaTLUdop.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/tRRA2GFpDQa60fgbah90kaTLUdop.xml
new file mode 100644
index 0000000..af15dce
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/tRRA2GFpDQa60fgbah90kaTLUdop.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/u_mOz_MGJrkh2IEfHmnRpSmUNU0d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/u_mOz_MGJrkh2IEfHmnRpSmUNU0d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/u_mOz_MGJrkh2IEfHmnRpSmUNU0d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/u_mOz_MGJrkh2IEfHmnRpSmUNU0p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/u_mOz_MGJrkh2IEfHmnRpSmUNU0p.xml
new file mode 100644
index 0000000..97d9880
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/u_mOz_MGJrkh2IEfHmnRpSmUNU0p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/udrG90zlRnO1vIQ4F2XUQoE4NPsd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/udrG90zlRnO1vIQ4F2XUQoE4NPsd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/udrG90zlRnO1vIQ4F2XUQoE4NPsd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/udrG90zlRnO1vIQ4F2XUQoE4NPsp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/udrG90zlRnO1vIQ4F2XUQoE4NPsp.xml
new file mode 100644
index 0000000..dbab3ef
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/udrG90zlRnO1vIQ4F2XUQoE4NPsp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/vCELhankjnwH8RjXSz7H0wgz3_Qd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/vCELhankjnwH8RjXSz7H0wgz3_Qd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/vCELhankjnwH8RjXSz7H0wgz3_Qd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/vCELhankjnwH8RjXSz7H0wgz3_Qp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/vCELhankjnwH8RjXSz7H0wgz3_Qp.xml
new file mode 100644
index 0000000..36527ea
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/vCELhankjnwH8RjXSz7H0wgz3_Qp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/vFswKJ-mtxArgnkmCRY5Z03xgFId.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/vFswKJ-mtxArgnkmCRY5Z03xgFId.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/vFswKJ-mtxArgnkmCRY5Z03xgFId.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/vFswKJ-mtxArgnkmCRY5Z03xgFIp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/vFswKJ-mtxArgnkmCRY5Z03xgFIp.xml
new file mode 100644
index 0000000..960d50b
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/vFswKJ-mtxArgnkmCRY5Z03xgFIp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/va0v0WdxN1aYqRr7mP81i-mOMb0d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/va0v0WdxN1aYqRr7mP81i-mOMb0d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/va0v0WdxN1aYqRr7mP81i-mOMb0d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/va0v0WdxN1aYqRr7mP81i-mOMb0p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/va0v0WdxN1aYqRr7mP81i-mOMb0p.xml
new file mode 100644
index 0000000..2c34449
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/va0v0WdxN1aYqRr7mP81i-mOMb0p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/vqCvOwVWDyiKdQZVGOEXUO4jccYd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/vqCvOwVWDyiKdQZVGOEXUO4jccYd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/vqCvOwVWDyiKdQZVGOEXUO4jccYd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/vqCvOwVWDyiKdQZVGOEXUO4jccYp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/vqCvOwVWDyiKdQZVGOEXUO4jccYp.xml
new file mode 100644
index 0000000..f7d031c
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/vqCvOwVWDyiKdQZVGOEXUO4jccYp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/w-t6lMAOZhsTeBJOJZkFSUMelPcd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/w-t6lMAOZhsTeBJOJZkFSUMelPcd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/w-t6lMAOZhsTeBJOJZkFSUMelPcd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/w-t6lMAOZhsTeBJOJZkFSUMelPcp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/w-t6lMAOZhsTeBJOJZkFSUMelPcp.xml
new file mode 100644
index 0000000..91d4c09
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/w-t6lMAOZhsTeBJOJZkFSUMelPcp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/wIoFgfA318Hck6iYy2de-v-RcX8d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/wIoFgfA318Hck6iYy2de-v-RcX8d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/wIoFgfA318Hck6iYy2de-v-RcX8d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/wIoFgfA318Hck6iYy2de-v-RcX8p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/wIoFgfA318Hck6iYy2de-v-RcX8p.xml
new file mode 100644
index 0000000..6983c4b
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/wIoFgfA318Hck6iYy2de-v-RcX8p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/wUpnm4YgmzfKK3EBpkG_EVVRhMsd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/wUpnm4YgmzfKK3EBpkG_EVVRhMsd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/wUpnm4YgmzfKK3EBpkG_EVVRhMsd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/wUpnm4YgmzfKK3EBpkG_EVVRhMsp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/wUpnm4YgmzfKK3EBpkG_EVVRhMsp.xml
new file mode 100644
index 0000000..57af596
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/wUpnm4YgmzfKK3EBpkG_EVVRhMsp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/we0YXrZqVC0rUz_VawVDXuU1nC4d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/we0YXrZqVC0rUz_VawVDXuU1nC4d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/we0YXrZqVC0rUz_VawVDXuU1nC4d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/we0YXrZqVC0rUz_VawVDXuU1nC4p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/we0YXrZqVC0rUz_VawVDXuU1nC4p.xml
new file mode 100644
index 0000000..653f44e
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/we0YXrZqVC0rUz_VawVDXuU1nC4p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/x8myECENnywqDcJvB0swmLYPLDwd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/x8myECENnywqDcJvB0swmLYPLDwd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/x8myECENnywqDcJvB0swmLYPLDwd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/x8myECENnywqDcJvB0swmLYPLDwp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/x8myECENnywqDcJvB0swmLYPLDwp.xml
new file mode 100644
index 0000000..11946bb
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/x8myECENnywqDcJvB0swmLYPLDwp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/xNFz-HKBOzlCFrfPjATX_xGojR4d.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/xNFz-HKBOzlCFrfPjATX_xGojR4d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/xNFz-HKBOzlCFrfPjATX_xGojR4d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/xNFz-HKBOzlCFrfPjATX_xGojR4p.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/xNFz-HKBOzlCFrfPjATX_xGojR4p.xml
new file mode 100644
index 0000000..07667ad
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/xNFz-HKBOzlCFrfPjATX_xGojR4p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/xdYUSiR_eIWaWfhJgCOD62b-oeYd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/xdYUSiR_eIWaWfhJgCOD62b-oeYd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/xdYUSiR_eIWaWfhJgCOD62b-oeYd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/xdYUSiR_eIWaWfhJgCOD62b-oeYp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/xdYUSiR_eIWaWfhJgCOD62b-oeYp.xml
new file mode 100644
index 0000000..937d058
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/xdYUSiR_eIWaWfhJgCOD62b-oeYp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/yPGBAA34c6UAwnpNU0giF---7jwd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/yPGBAA34c6UAwnpNU0giF---7jwd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/yPGBAA34c6UAwnpNU0giF---7jwd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/yPGBAA34c6UAwnpNU0giF---7jwp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/yPGBAA34c6UAwnpNU0giF---7jwp.xml
new file mode 100644
index 0000000..29a4308
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/yPGBAA34c6UAwnpNU0giF---7jwp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/ya8LLCmIUyUwVfyHMXYulp6eKnId.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/ya8LLCmIUyUwVfyHMXYulp6eKnId.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/ya8LLCmIUyUwVfyHMXYulp6eKnId.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/ya8LLCmIUyUwVfyHMXYulp6eKnIp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/ya8LLCmIUyUwVfyHMXYulp6eKnIp.xml
new file mode 100644
index 0000000..2b5c35a
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/ya8LLCmIUyUwVfyHMXYulp6eKnIp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/zegYqUShx3X4UGRhX5BJ10pnmsEd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/zegYqUShx3X4UGRhX5BJ10pnmsEd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/zegYqUShx3X4UGRhX5BJ10pnmsEd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/zegYqUShx3X4UGRhX5BJ10pnmsEp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/zegYqUShx3X4UGRhX5BJ10pnmsEp.xml
new file mode 100644
index 0000000..617a025
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/zegYqUShx3X4UGRhX5BJ10pnmsEp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/zlD0syXMGImuHb7B-RSc5GhuAScd.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/zlD0syXMGImuHb7B-RSc5GhuAScd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/zlD0syXMGImuHb7B-RSc5GhuAScd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/zlD0syXMGImuHb7B-RSc5GhuAScp.xml b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/zlD0syXMGImuHb7B-RSc5GhuAScp.xml
new file mode 100644
index 0000000..d4f684f
--- /dev/null
+++ b/resources/project/NdsL0Nh4wtc5dxoI0TU1uVF-B00/zlD0syXMGImuHb7B-RSc5GhuAScp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/-t1vhuWhXfd2_bA01rA9UGacBr0d.xml b/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/-t1vhuWhXfd2_bA01rA9UGacBr0d.xml
new file mode 100644
index 0000000..fda6c04
--- /dev/null
+++ b/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/-t1vhuWhXfd2_bA01rA9UGacBr0d.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/-t1vhuWhXfd2_bA01rA9UGacBr0p.xml b/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/-t1vhuWhXfd2_bA01rA9UGacBr0p.xml
new file mode 100644
index 0000000..c897a45
--- /dev/null
+++ b/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/-t1vhuWhXfd2_bA01rA9UGacBr0p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/B-j2AtgQvcSYyFI6QRtcq2_ysF8d.xml b/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/B-j2AtgQvcSYyFI6QRtcq2_ysF8d.xml
new file mode 100644
index 0000000..fda6c04
--- /dev/null
+++ b/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/B-j2AtgQvcSYyFI6QRtcq2_ysF8d.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/B-j2AtgQvcSYyFI6QRtcq2_ysF8p.xml b/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/B-j2AtgQvcSYyFI6QRtcq2_ysF8p.xml
new file mode 100644
index 0000000..f615625
--- /dev/null
+++ b/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/B-j2AtgQvcSYyFI6QRtcq2_ysF8p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/Uka6WspLurK62WWVnPA5yb3a2h4d.xml b/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/Uka6WspLurK62WWVnPA5yb3a2h4d.xml
new file mode 100644
index 0000000..7a6326b
--- /dev/null
+++ b/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/Uka6WspLurK62WWVnPA5yb3a2h4d.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/Uka6WspLurK62WWVnPA5yb3a2h4p.xml b/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/Uka6WspLurK62WWVnPA5yb3a2h4p.xml
new file mode 100644
index 0000000..93ddaf0
--- /dev/null
+++ b/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/Uka6WspLurK62WWVnPA5yb3a2h4p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/ZFweRNDkNTjqNE1bFowTK-Lg4zkd.xml b/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/ZFweRNDkNTjqNE1bFowTK-Lg4zkd.xml
new file mode 100644
index 0000000..7a6326b
--- /dev/null
+++ b/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/ZFweRNDkNTjqNE1bFowTK-Lg4zkd.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/ZFweRNDkNTjqNE1bFowTK-Lg4zkp.xml b/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/ZFweRNDkNTjqNE1bFowTK-Lg4zkp.xml
new file mode 100644
index 0000000..b9f9c7d
--- /dev/null
+++ b/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/ZFweRNDkNTjqNE1bFowTK-Lg4zkp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/j8WKtWk7xdSV1Qd2-XfSC0ibMoId.xml b/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/j8WKtWk7xdSV1Qd2-XfSC0ibMoId.xml
new file mode 100644
index 0000000..7a6326b
--- /dev/null
+++ b/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/j8WKtWk7xdSV1Qd2-XfSC0ibMoId.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/j8WKtWk7xdSV1Qd2-XfSC0ibMoIp.xml b/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/j8WKtWk7xdSV1Qd2-XfSC0ibMoIp.xml
new file mode 100644
index 0000000..a6450ba
--- /dev/null
+++ b/resources/project/NlKaXP0P62kRJDmjZSzGu8Jf--k/j8WKtWk7xdSV1Qd2-XfSC0ibMoIp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/ObrY62phP-WFAZae6jPcfXGw95Id.xml b/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/ObrY62phP-WFAZae6jPcfXGw95Id.xml
new file mode 100644
index 0000000..fda6c04
--- /dev/null
+++ b/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/ObrY62phP-WFAZae6jPcfXGw95Id.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/ObrY62phP-WFAZae6jPcfXGw95Ip.xml b/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/ObrY62phP-WFAZae6jPcfXGw95Ip.xml
new file mode 100644
index 0000000..484b934
--- /dev/null
+++ b/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/ObrY62phP-WFAZae6jPcfXGw95Ip.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/tK-YwJbBDUqTl5ur7TuyAPLEvPcd.xml b/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/tK-YwJbBDUqTl5ur7TuyAPLEvPcd.xml
new file mode 100644
index 0000000..99772b4
--- /dev/null
+++ b/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/tK-YwJbBDUqTl5ur7TuyAPLEvPcd.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/tK-YwJbBDUqTl5ur7TuyAPLEvPcp.xml b/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/tK-YwJbBDUqTl5ur7TuyAPLEvPcp.xml
new file mode 100644
index 0000000..7f3d8ed
--- /dev/null
+++ b/resources/project/QfRan8VIS8FJWu9sq7_nfr0NcFw/tK-YwJbBDUqTl5ur7TuyAPLEvPcp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/Wqze2RguMm8RygQI0Uykdot17AI/14cfwHWYtO3bIZIAHJel6xwvZs0d.xml b/resources/project/Wqze2RguMm8RygQI0Uykdot17AI/14cfwHWYtO3bIZIAHJel6xwvZs0d.xml
new file mode 100644
index 0000000..7a6326b
--- /dev/null
+++ b/resources/project/Wqze2RguMm8RygQI0Uykdot17AI/14cfwHWYtO3bIZIAHJel6xwvZs0d.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/Wqze2RguMm8RygQI0Uykdot17AI/14cfwHWYtO3bIZIAHJel6xwvZs0p.xml b/resources/project/Wqze2RguMm8RygQI0Uykdot17AI/14cfwHWYtO3bIZIAHJel6xwvZs0p.xml
new file mode 100644
index 0000000..b5009ac
--- /dev/null
+++ b/resources/project/Wqze2RguMm8RygQI0Uykdot17AI/14cfwHWYtO3bIZIAHJel6xwvZs0p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/Wqze2RguMm8RygQI0Uykdot17AI/hMUzYmljSfZyrsNbSqvVBNLDdhMd.xml b/resources/project/Wqze2RguMm8RygQI0Uykdot17AI/hMUzYmljSfZyrsNbSqvVBNLDdhMd.xml
new file mode 100644
index 0000000..fda6c04
--- /dev/null
+++ b/resources/project/Wqze2RguMm8RygQI0Uykdot17AI/hMUzYmljSfZyrsNbSqvVBNLDdhMd.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/Wqze2RguMm8RygQI0Uykdot17AI/hMUzYmljSfZyrsNbSqvVBNLDdhMp.xml b/resources/project/Wqze2RguMm8RygQI0Uykdot17AI/hMUzYmljSfZyrsNbSqvVBNLDdhMp.xml
new file mode 100644
index 0000000..220b869
--- /dev/null
+++ b/resources/project/Wqze2RguMm8RygQI0Uykdot17AI/hMUzYmljSfZyrsNbSqvVBNLDdhMp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/a4NMXOJuMxY6ZPdF7LtPbcqgbXg/ZbkytE2NTET5HAXLROq1rMk8J54d.xml b/resources/project/a4NMXOJuMxY6ZPdF7LtPbcqgbXg/ZbkytE2NTET5HAXLROq1rMk8J54d.xml
new file mode 100644
index 0000000..7a6326b
--- /dev/null
+++ b/resources/project/a4NMXOJuMxY6ZPdF7LtPbcqgbXg/ZbkytE2NTET5HAXLROq1rMk8J54d.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/a4NMXOJuMxY6ZPdF7LtPbcqgbXg/ZbkytE2NTET5HAXLROq1rMk8J54p.xml b/resources/project/a4NMXOJuMxY6ZPdF7LtPbcqgbXg/ZbkytE2NTET5HAXLROq1rMk8J54p.xml
new file mode 100644
index 0000000..f56cf74
--- /dev/null
+++ b/resources/project/a4NMXOJuMxY6ZPdF7LtPbcqgbXg/ZbkytE2NTET5HAXLROq1rMk8J54p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/a4NMXOJuMxY6ZPdF7LtPbcqgbXg/nL7mB0EmM1_sBoQbu4NkUq5XMskd.xml b/resources/project/a4NMXOJuMxY6ZPdF7LtPbcqgbXg/nL7mB0EmM1_sBoQbu4NkUq5XMskd.xml
new file mode 100644
index 0000000..7a6326b
--- /dev/null
+++ b/resources/project/a4NMXOJuMxY6ZPdF7LtPbcqgbXg/nL7mB0EmM1_sBoQbu4NkUq5XMskd.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/a4NMXOJuMxY6ZPdF7LtPbcqgbXg/nL7mB0EmM1_sBoQbu4NkUq5XMskp.xml b/resources/project/a4NMXOJuMxY6ZPdF7LtPbcqgbXg/nL7mB0EmM1_sBoQbu4NkUq5XMskp.xml
new file mode 100644
index 0000000..c998200
--- /dev/null
+++ b/resources/project/a4NMXOJuMxY6ZPdF7LtPbcqgbXg/nL7mB0EmM1_sBoQbu4NkUq5XMskp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/YJG70BwMlTL8C7mo8z7rvPqvGD4d.xml b/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/YJG70BwMlTL8C7mo8z7rvPqvGD4d.xml
new file mode 100644
index 0000000..7a6326b
--- /dev/null
+++ b/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/YJG70BwMlTL8C7mo8z7rvPqvGD4d.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/YJG70BwMlTL8C7mo8z7rvPqvGD4p.xml b/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/YJG70BwMlTL8C7mo8z7rvPqvGD4p.xml
new file mode 100644
index 0000000..8e96cf7
--- /dev/null
+++ b/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/YJG70BwMlTL8C7mo8z7rvPqvGD4p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/_CMbxM7vtC88Y5Ls4F-uYYiBRJgd.xml b/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/_CMbxM7vtC88Y5Ls4F-uYYiBRJgd.xml
new file mode 100644
index 0000000..7a6326b
--- /dev/null
+++ b/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/_CMbxM7vtC88Y5Ls4F-uYYiBRJgd.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/_CMbxM7vtC88Y5Ls4F-uYYiBRJgp.xml b/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/_CMbxM7vtC88Y5Ls4F-uYYiBRJgp.xml
new file mode 100644
index 0000000..b35188c
--- /dev/null
+++ b/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/_CMbxM7vtC88Y5Ls4F-uYYiBRJgp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/ik1NGaL_boPxdnGHUZzao7SQI_Qd.xml b/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/ik1NGaL_boPxdnGHUZzao7SQI_Qd.xml
new file mode 100644
index 0000000..7a6326b
--- /dev/null
+++ b/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/ik1NGaL_boPxdnGHUZzao7SQI_Qd.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/ik1NGaL_boPxdnGHUZzao7SQI_Qp.xml b/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/ik1NGaL_boPxdnGHUZzao7SQI_Qp.xml
new file mode 100644
index 0000000..3c31afd
--- /dev/null
+++ b/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/ik1NGaL_boPxdnGHUZzao7SQI_Qp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/nlpXlc0mTyZKSdK7BLQRqEHlo5sd.xml b/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/nlpXlc0mTyZKSdK7BLQRqEHlo5sd.xml
new file mode 100644
index 0000000..7a6326b
--- /dev/null
+++ b/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/nlpXlc0mTyZKSdK7BLQRqEHlo5sd.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/nlpXlc0mTyZKSdK7BLQRqEHlo5sp.xml b/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/nlpXlc0mTyZKSdK7BLQRqEHlo5sp.xml
new file mode 100644
index 0000000..69c9318
--- /dev/null
+++ b/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/nlpXlc0mTyZKSdK7BLQRqEHlo5sp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/xoiWFrkA2etXA6qNdFk8c9nfanod.xml b/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/xoiWFrkA2etXA6qNdFk8c9nfanod.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/xoiWFrkA2etXA6qNdFk8c9nfanod.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/xoiWFrkA2etXA6qNdFk8c9nfanop.xml b/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/xoiWFrkA2etXA6qNdFk8c9nfanop.xml
new file mode 100644
index 0000000..842de6a
--- /dev/null
+++ b/resources/project/ceaVDhqtkvV5Y52oIQ7MOhken3g/xoiWFrkA2etXA6qNdFk8c9nfanop.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/GkZhPrjzHBlWmLhS_XdEzkI_pOgd.xml b/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/GkZhPrjzHBlWmLhS_XdEzkI_pOgd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/GkZhPrjzHBlWmLhS_XdEzkI_pOgd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/GkZhPrjzHBlWmLhS_XdEzkI_pOgp.xml b/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/GkZhPrjzHBlWmLhS_XdEzkI_pOgp.xml
new file mode 100644
index 0000000..294f1fe
--- /dev/null
+++ b/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/GkZhPrjzHBlWmLhS_XdEzkI_pOgp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/NAPgIqrfihsMqVIaUxFYn_Xa9AMd.xml b/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/NAPgIqrfihsMqVIaUxFYn_Xa9AMd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/NAPgIqrfihsMqVIaUxFYn_Xa9AMd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/NAPgIqrfihsMqVIaUxFYn_Xa9AMp.xml b/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/NAPgIqrfihsMqVIaUxFYn_Xa9AMp.xml
new file mode 100644
index 0000000..842de6a
--- /dev/null
+++ b/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/NAPgIqrfihsMqVIaUxFYn_Xa9AMp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/ceaVDhqtkvV5Y52oIQ7MOhken3gd.xml b/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/ceaVDhqtkvV5Y52oIQ7MOhken3gd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/ceaVDhqtkvV5Y52oIQ7MOhken3gd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/ceaVDhqtkvV5Y52oIQ7MOhken3gp.xml b/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/ceaVDhqtkvV5Y52oIQ7MOhken3gp.xml
new file mode 100644
index 0000000..d3b8d29
--- /dev/null
+++ b/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/ceaVDhqtkvV5Y52oIQ7MOhken3gp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/f5W54yNk0Zwx1sRFz0ew04MdvIAd.xml b/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/f5W54yNk0Zwx1sRFz0ew04MdvIAd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/f5W54yNk0Zwx1sRFz0ew04MdvIAd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/f5W54yNk0Zwx1sRFz0ew04MdvIAp.xml b/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/f5W54yNk0Zwx1sRFz0ew04MdvIAp.xml
new file mode 100644
index 0000000..3c5a2d4
--- /dev/null
+++ b/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/f5W54yNk0Zwx1sRFz0ew04MdvIAp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/qu_tzR5t8eCCr9qrIOIGQzeETHUd.xml b/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/qu_tzR5t8eCCr9qrIOIGQzeETHUd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/qu_tzR5t8eCCr9qrIOIGQzeETHUd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/qu_tzR5t8eCCr9qrIOIGQzeETHUp.xml b/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/qu_tzR5t8eCCr9qrIOIGQzeETHUp.xml
new file mode 100644
index 0000000..e063699
--- /dev/null
+++ b/resources/project/eZzARIDvGAmYNGp0Fs2mKSIexRI/qu_tzR5t8eCCr9qrIOIGQzeETHUp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/4x-ApT8Yg6aSjIQrWyFYNMrfxTEd.xml b/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/4x-ApT8Yg6aSjIQrWyFYNMrfxTEd.xml
new file mode 100644
index 0000000..7a6326b
--- /dev/null
+++ b/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/4x-ApT8Yg6aSjIQrWyFYNMrfxTEd.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/4x-ApT8Yg6aSjIQrWyFYNMrfxTEp.xml b/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/4x-ApT8Yg6aSjIQrWyFYNMrfxTEp.xml
new file mode 100644
index 0000000..ecb9eb0
--- /dev/null
+++ b/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/4x-ApT8Yg6aSjIQrWyFYNMrfxTEp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/6PB0bNcC4OKMWt_Jcxs-0G2F97Ed.xml b/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/6PB0bNcC4OKMWt_Jcxs-0G2F97Ed.xml
new file mode 100644
index 0000000..7a6326b
--- /dev/null
+++ b/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/6PB0bNcC4OKMWt_Jcxs-0G2F97Ed.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/6PB0bNcC4OKMWt_Jcxs-0G2F97Ep.xml b/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/6PB0bNcC4OKMWt_Jcxs-0G2F97Ep.xml
new file mode 100644
index 0000000..bc347d9
--- /dev/null
+++ b/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/6PB0bNcC4OKMWt_Jcxs-0G2F97Ep.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/OkKnC53-0i7wo8Q4f4beGgufowMd.xml b/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/OkKnC53-0i7wo8Q4f4beGgufowMd.xml
new file mode 100644
index 0000000..7a6326b
--- /dev/null
+++ b/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/OkKnC53-0i7wo8Q4f4beGgufowMd.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/OkKnC53-0i7wo8Q4f4beGgufowMp.xml b/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/OkKnC53-0i7wo8Q4f4beGgufowMp.xml
new file mode 100644
index 0000000..c2122be
--- /dev/null
+++ b/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/OkKnC53-0i7wo8Q4f4beGgufowMp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/akT1M2pPrNZecPRn_5INVNLnJ6Qd.xml b/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/akT1M2pPrNZecPRn_5INVNLnJ6Qd.xml
new file mode 100644
index 0000000..7a6326b
--- /dev/null
+++ b/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/akT1M2pPrNZecPRn_5INVNLnJ6Qd.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/akT1M2pPrNZecPRn_5INVNLnJ6Qp.xml b/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/akT1M2pPrNZecPRn_5INVNLnJ6Qp.xml
new file mode 100644
index 0000000..3b75ea5
--- /dev/null
+++ b/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/akT1M2pPrNZecPRn_5INVNLnJ6Qp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/uEFXjvRtQDk4x1ZiSQvTrGr2Rxgd.xml b/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/uEFXjvRtQDk4x1ZiSQvTrGr2Rxgd.xml
new file mode 100644
index 0000000..7a6326b
--- /dev/null
+++ b/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/uEFXjvRtQDk4x1ZiSQvTrGr2Rxgd.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/uEFXjvRtQDk4x1ZiSQvTrGr2Rxgp.xml b/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/uEFXjvRtQDk4x1ZiSQvTrGr2Rxgp.xml
new file mode 100644
index 0000000..3fb94a6
--- /dev/null
+++ b/resources/project/elO4D6tOV7Lp-Jfo7ptgr2NlB30/uEFXjvRtQDk4x1ZiSQvTrGr2Rxgp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/f1CasPtPWEKkULO6lQ_WTjau2Nw/6HGJ4oAVUNiKqcphEyZLEi-vhA0d.xml b/resources/project/f1CasPtPWEKkULO6lQ_WTjau2Nw/6HGJ4oAVUNiKqcphEyZLEi-vhA0d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/f1CasPtPWEKkULO6lQ_WTjau2Nw/6HGJ4oAVUNiKqcphEyZLEi-vhA0d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/f1CasPtPWEKkULO6lQ_WTjau2Nw/6HGJ4oAVUNiKqcphEyZLEi-vhA0p.xml b/resources/project/f1CasPtPWEKkULO6lQ_WTjau2Nw/6HGJ4oAVUNiKqcphEyZLEi-vhA0p.xml
new file mode 100644
index 0000000..c35bbac
--- /dev/null
+++ b/resources/project/f1CasPtPWEKkULO6lQ_WTjau2Nw/6HGJ4oAVUNiKqcphEyZLEi-vhA0p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/f1CasPtPWEKkULO6lQ_WTjau2Nw/fn6KZr3pcEU2sl9hG31V-_Ooyd4d.xml b/resources/project/f1CasPtPWEKkULO6lQ_WTjau2Nw/fn6KZr3pcEU2sl9hG31V-_Ooyd4d.xml
new file mode 100644
index 0000000..99772b4
--- /dev/null
+++ b/resources/project/f1CasPtPWEKkULO6lQ_WTjau2Nw/fn6KZr3pcEU2sl9hG31V-_Ooyd4d.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/f1CasPtPWEKkULO6lQ_WTjau2Nw/fn6KZr3pcEU2sl9hG31V-_Ooyd4p.xml b/resources/project/f1CasPtPWEKkULO6lQ_WTjau2Nw/fn6KZr3pcEU2sl9hG31V-_Ooyd4p.xml
new file mode 100644
index 0000000..34481fc
--- /dev/null
+++ b/resources/project/f1CasPtPWEKkULO6lQ_WTjau2Nw/fn6KZr3pcEU2sl9hG31V-_Ooyd4p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/f5U-zHPhyZJzGDBuc9waDwYOGcw/j7l-39BD8pCe4pVJ3Zpcd_p-AoAp.xml b/resources/project/f5U-zHPhyZJzGDBuc9waDwYOGcw/j7l-39BD8pCe4pVJ3Zpcd_p-AoAp.xml
deleted file mode 100644
index b9746d6..0000000
--- a/resources/project/f5U-zHPhyZJzGDBuc9waDwYOGcw/j7l-39BD8pCe4pVJ3Zpcd_p-AoAp.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/resources/project/f5W54yNk0Zwx1sRFz0ew04MdvIA/jAqnBtSIGPSIhf1xA5V9AYXWT8Md.xml b/resources/project/f5W54yNk0Zwx1sRFz0ew04MdvIA/jAqnBtSIGPSIhf1xA5V9AYXWT8Md.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/f5W54yNk0Zwx1sRFz0ew04MdvIA/jAqnBtSIGPSIhf1xA5V9AYXWT8Md.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/f5W54yNk0Zwx1sRFz0ew04MdvIA/jAqnBtSIGPSIhf1xA5V9AYXWT8Mp.xml b/resources/project/f5W54yNk0Zwx1sRFz0ew04MdvIA/jAqnBtSIGPSIhf1xA5V9AYXWT8Mp.xml
new file mode 100644
index 0000000..842de6a
--- /dev/null
+++ b/resources/project/f5W54yNk0Zwx1sRFz0ew04MdvIA/jAqnBtSIGPSIhf1xA5V9AYXWT8Mp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/f5W54yNk0Zwx1sRFz0ew04MdvIA/o-Tn-s1SXrdT10aw7MmQ3hd2SHMd.xml b/resources/project/f5W54yNk0Zwx1sRFz0ew04MdvIA/o-Tn-s1SXrdT10aw7MmQ3hd2SHMd.xml
new file mode 100644
index 0000000..7a6326b
--- /dev/null
+++ b/resources/project/f5W54yNk0Zwx1sRFz0ew04MdvIA/o-Tn-s1SXrdT10aw7MmQ3hd2SHMd.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/f5W54yNk0Zwx1sRFz0ew04MdvIA/o-Tn-s1SXrdT10aw7MmQ3hd2SHMp.xml b/resources/project/f5W54yNk0Zwx1sRFz0ew04MdvIA/o-Tn-s1SXrdT10aw7MmQ3hd2SHMp.xml
new file mode 100644
index 0000000..75c5c21
--- /dev/null
+++ b/resources/project/f5W54yNk0Zwx1sRFz0ew04MdvIA/o-Tn-s1SXrdT10aw7MmQ3hd2SHMp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/gdDuiZdBOBh_KnGn-recV21_YgU/NpKBafCNzwt46UIiL2e9Xk0Mk20d.xml b/resources/project/gdDuiZdBOBh_KnGn-recV21_YgU/NpKBafCNzwt46UIiL2e9Xk0Mk20d.xml
new file mode 100644
index 0000000..7a6326b
--- /dev/null
+++ b/resources/project/gdDuiZdBOBh_KnGn-recV21_YgU/NpKBafCNzwt46UIiL2e9Xk0Mk20d.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/gdDuiZdBOBh_KnGn-recV21_YgU/NpKBafCNzwt46UIiL2e9Xk0Mk20p.xml b/resources/project/gdDuiZdBOBh_KnGn-recV21_YgU/NpKBafCNzwt46UIiL2e9Xk0Mk20p.xml
new file mode 100644
index 0000000..488e3fa
--- /dev/null
+++ b/resources/project/gdDuiZdBOBh_KnGn-recV21_YgU/NpKBafCNzwt46UIiL2e9Xk0Mk20p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/5ieMYPaQnclp-AlvUjRy9zy7M1wd.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/5ieMYPaQnclp-AlvUjRy9zy7M1wd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/5ieMYPaQnclp-AlvUjRy9zy7M1wd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/5ieMYPaQnclp-AlvUjRy9zy7M1wp.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/5ieMYPaQnclp-AlvUjRy9zy7M1wp.xml
new file mode 100644
index 0000000..984d014
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/5ieMYPaQnclp-AlvUjRy9zy7M1wp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/9uFFpKlODWvDmfdiNnQW-nfkIZkd.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/9uFFpKlODWvDmfdiNnQW-nfkIZkd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/9uFFpKlODWvDmfdiNnQW-nfkIZkd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/9uFFpKlODWvDmfdiNnQW-nfkIZkp.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/9uFFpKlODWvDmfdiNnQW-nfkIZkp.xml
new file mode 100644
index 0000000..842de6a
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/9uFFpKlODWvDmfdiNnQW-nfkIZkp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/DFOXQaY1rRKQpyipTH_8fEHeQR4d.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/DFOXQaY1rRKQpyipTH_8fEHeQR4d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/DFOXQaY1rRKQpyipTH_8fEHeQR4d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/DFOXQaY1rRKQpyipTH_8fEHeQR4p.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/DFOXQaY1rRKQpyipTH_8fEHeQR4p.xml
new file mode 100644
index 0000000..28f8a34
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/DFOXQaY1rRKQpyipTH_8fEHeQR4p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/G94UrlDDT_1aS6UphEDyzkzRBzkd.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/G94UrlDDT_1aS6UphEDyzkzRBzkd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/G94UrlDDT_1aS6UphEDyzkzRBzkd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/G94UrlDDT_1aS6UphEDyzkzRBzkp.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/G94UrlDDT_1aS6UphEDyzkzRBzkp.xml
new file mode 100644
index 0000000..5f29378
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/G94UrlDDT_1aS6UphEDyzkzRBzkp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/HJYQYiX9gQ8ZqJsNXQvKTneIb-Ud.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/HJYQYiX9gQ8ZqJsNXQvKTneIb-Ud.xml
new file mode 100644
index 0000000..7a6326b
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/HJYQYiX9gQ8ZqJsNXQvKTneIb-Ud.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/HJYQYiX9gQ8ZqJsNXQvKTneIb-Up.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/HJYQYiX9gQ8ZqJsNXQvKTneIb-Up.xml
new file mode 100644
index 0000000..4b85246
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/HJYQYiX9gQ8ZqJsNXQvKTneIb-Up.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/VV30ODzqIaFizzJ8PKhtDADLI1wd.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/VV30ODzqIaFizzJ8PKhtDADLI1wd.xml
new file mode 100644
index 0000000..7a6326b
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/VV30ODzqIaFizzJ8PKhtDADLI1wd.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/VV30ODzqIaFizzJ8PKhtDADLI1wp.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/VV30ODzqIaFizzJ8PKhtDADLI1wp.xml
new file mode 100644
index 0000000..1f37a43
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/VV30ODzqIaFizzJ8PKhtDADLI1wp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/ZZmsNADUtYutDnr-aIgn-OnfSI0d.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/ZZmsNADUtYutDnr-aIgn-OnfSI0d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/ZZmsNADUtYutDnr-aIgn-OnfSI0d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/ZZmsNADUtYutDnr-aIgn-OnfSI0p.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/ZZmsNADUtYutDnr-aIgn-OnfSI0p.xml
new file mode 100644
index 0000000..bf5bbd1
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/ZZmsNADUtYutDnr-aIgn-OnfSI0p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/eZzARIDvGAmYNGp0Fs2mKSIexRId.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/eZzARIDvGAmYNGp0Fs2mKSIexRId.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/eZzARIDvGAmYNGp0Fs2mKSIexRId.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/eZzARIDvGAmYNGp0Fs2mKSIexRIp.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/eZzARIDvGAmYNGp0Fs2mKSIexRIp.xml
new file mode 100644
index 0000000..c6dbd8f
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/eZzARIDvGAmYNGp0Fs2mKSIexRIp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/hgg1-jCGTS4UMZSE4YfJsf2oaMsd.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/hgg1-jCGTS4UMZSE4YfJsf2oaMsd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/hgg1-jCGTS4UMZSE4YfJsf2oaMsd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/hgg1-jCGTS4UMZSE4YfJsf2oaMsp.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/hgg1-jCGTS4UMZSE4YfJsf2oaMsp.xml
new file mode 100644
index 0000000..1b0b774
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/hgg1-jCGTS4UMZSE4YfJsf2oaMsp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/jaceHlD4clVEUhekbOMrwJ0qvtwd.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/jaceHlD4clVEUhekbOMrwJ0qvtwd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/jaceHlD4clVEUhekbOMrwJ0qvtwd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/jaceHlD4clVEUhekbOMrwJ0qvtwp.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/jaceHlD4clVEUhekbOMrwJ0qvtwp.xml
new file mode 100644
index 0000000..78401bd
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/jaceHlD4clVEUhekbOMrwJ0qvtwp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/mCtrcvUKYUQmJExj9-qq672xa7od.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/mCtrcvUKYUQmJExj9-qq672xa7od.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/mCtrcvUKYUQmJExj9-qq672xa7od.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/mCtrcvUKYUQmJExj9-qq672xa7op.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/mCtrcvUKYUQmJExj9-qq672xa7op.xml
new file mode 100644
index 0000000..81bca21
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/mCtrcvUKYUQmJExj9-qq672xa7op.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/mPOr8vFJdzcvPj0dogVlQGxmkvwd.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/mPOr8vFJdzcvPj0dogVlQGxmkvwd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/mPOr8vFJdzcvPj0dogVlQGxmkvwd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/mPOr8vFJdzcvPj0dogVlQGxmkvwp.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/mPOr8vFJdzcvPj0dogVlQGxmkvwp.xml
new file mode 100644
index 0000000..f19c21d
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/mPOr8vFJdzcvPj0dogVlQGxmkvwp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/sIfkSL1IZsp3jKR-0Arx8WdFz8kd.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/sIfkSL1IZsp3jKR-0Arx8WdFz8kd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/sIfkSL1IZsp3jKR-0Arx8WdFz8kd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/sIfkSL1IZsp3jKR-0Arx8WdFz8kp.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/sIfkSL1IZsp3jKR-0Arx8WdFz8kp.xml
new file mode 100644
index 0000000..e8ae89f
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/sIfkSL1IZsp3jKR-0Arx8WdFz8kp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/uTxIUYmJ5r_akB8kb_pv9GQm8S4d.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/uTxIUYmJ5r_akB8kb_pv9GQm8S4d.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/uTxIUYmJ5r_akB8kb_pv9GQm8S4d.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/uTxIUYmJ5r_akB8kb_pv9GQm8S4p.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/uTxIUYmJ5r_akB8kb_pv9GQm8S4p.xml
new file mode 100644
index 0000000..4ff9c3d
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/uTxIUYmJ5r_akB8kb_pv9GQm8S4p.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/xsU64DkOPl8llhmBp3e8It6w4Sod.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/xsU64DkOPl8llhmBp3e8It6w4Sod.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/xsU64DkOPl8llhmBp3e8It6w4Sod.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/xsU64DkOPl8llhmBp3e8It6w4Sop.xml b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/xsU64DkOPl8llhmBp3e8It6w4Sop.xml
new file mode 100644
index 0000000..5532f65
--- /dev/null
+++ b/resources/project/lWv1ZHtgNlIm2Vk_PwH8_N2dVzM/xsU64DkOPl8llhmBp3e8It6w4Sop.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/qu_tzR5t8eCCr9qrIOIGQzeETHU/-JxYjHfIG2f0LqYcrgF19Tk99yEd.xml b/resources/project/qu_tzR5t8eCCr9qrIOIGQzeETHU/-JxYjHfIG2f0LqYcrgF19Tk99yEd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/qu_tzR5t8eCCr9qrIOIGQzeETHU/-JxYjHfIG2f0LqYcrgF19Tk99yEd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/qu_tzR5t8eCCr9qrIOIGQzeETHU/-JxYjHfIG2f0LqYcrgF19Tk99yEp.xml b/resources/project/qu_tzR5t8eCCr9qrIOIGQzeETHU/-JxYjHfIG2f0LqYcrgF19Tk99yEp.xml
new file mode 100644
index 0000000..da29615
--- /dev/null
+++ b/resources/project/qu_tzR5t8eCCr9qrIOIGQzeETHU/-JxYjHfIG2f0LqYcrgF19Tk99yEp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/qu_tzR5t8eCCr9qrIOIGQzeETHU/nXflqEnMMNVwCQ0tHjs5QFskfmcd.xml b/resources/project/qu_tzR5t8eCCr9qrIOIGQzeETHU/nXflqEnMMNVwCQ0tHjs5QFskfmcd.xml
new file mode 100644
index 0000000..a75f7a8
--- /dev/null
+++ b/resources/project/qu_tzR5t8eCCr9qrIOIGQzeETHU/nXflqEnMMNVwCQ0tHjs5QFskfmcd.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/project/qu_tzR5t8eCCr9qrIOIGQzeETHU/nXflqEnMMNVwCQ0tHjs5QFskfmcp.xml b/resources/project/qu_tzR5t8eCCr9qrIOIGQzeETHU/nXflqEnMMNVwCQ0tHjs5QFskfmcp.xml
new file mode 100644
index 0000000..842de6a
--- /dev/null
+++ b/resources/project/qu_tzR5t8eCCr9qrIOIGQzeETHU/nXflqEnMMNVwCQ0tHjs5QFskfmcp.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/test/+wt/+test/ColorSelector.m b/test/+wt/+test/ColorSelector.m
index fa21a7a..336d286 100644
--- a/test/+wt/+test/ColorSelector.m
+++ b/test/+wt/+test/ColorSelector.m
@@ -34,7 +34,7 @@ function testValueProperty(testCase)
% Test an invalid color
newColor = [-1 0 1];
- errorID = 'wt:validators:mustBeBetween';
+ errorID = 'MATLAB:validators:mustBeInRange';
testCase.verifySetPropertyError("Value", newColor, errorID);
% Test an invalid color
diff --git a/widgets/+wt/+abstract/BaseViewChart.m b/widgets/+wt/+abstract/BaseViewChart.m
new file mode 100644
index 0000000..7192d51
--- /dev/null
+++ b/widgets/+wt/+abstract/BaseViewChart.m
@@ -0,0 +1,142 @@
+classdef BaseViewChart < matlab.graphics.chartcontainer.ChartContainer & ...
+ wt.mixin.ModelObserver
+ % Base class for view charts referencing a BaseModel class
+
+ % Copyright 2024 The MathWorks Inc.
+
+
+ %% Public Properties
+ properties (AbortSet)
+
+ % The default title prefix to display
+ TitlePrefix (1,1) string
+
+ end %properties
+
+
+ %% Internal Properties
+ properties (Transient, NonCopyable, Hidden, SetAccess = protected)
+
+ % TiledLayout for axes
+ TiledLayout matlab.graphics.layout.TiledChartLayout
+
+ % Axes to display the signal
+ Axes (1,:) matlab.graphics.axis.Axes
+
+ % Legend of each axes
+ Legend (1,:) matlab.graphics.illustration.Legend
+
+ end %properties
+
+
+ properties (Transient, NonCopyable, UsedInUpdate = true, ...
+ GetAccess = private, SetAccess = protected)
+
+ % Internal flag to trigger an update call
+ TriggerUpdate_BVC (1,1) logical = false
+
+ end %properties
+
+
+ %% Constructor
+ methods
+ function obj = BaseViewChart(parent, varargin)
+ % Constructor
+
+ if nargin < 1
+ fig = uifigure();
+ parent = uigridlayout(fig,[1,1]);
+ end
+
+ % Call superclass constructors
+ obj = obj@matlab.graphics.chartcontainer.ChartContainer(parent, varargin{:});
+ obj@wt.mixin.ModelObserver();
+
+ end %function
+ end %methods
+
+
+ %% Debugging Methods
+ methods
+
+ function forceUpdateChart(obj)
+ % Forces update to run (For debugging only!)
+
+ obj.update();
+
+ end %function
+
+ end %methods
+
+
+ %% Protected Methods
+ methods (Access = protected)
+
+ function setup(obj)
+ % Create the underlying components
+
+ % Configure Layout
+ obj.TiledLayout = getLayout(obj);
+ obj.TiledLayout.Padding = "compact";
+ obj.TiledLayout.TileSpacing = "compact";
+
+ % Name the panel with the view's class name by default
+ obj.TitlePrefix = extract(string(class(obj)), ...
+ alphanumericsPattern + textBoundary);
+ obj.TiledLayout.Title.String = obj.TitlePrefix;
+
+ end %function
+
+
+ function update(obj)
+
+ % Get the model to display
+ [model, validToDisplay] = obj.getScalarModelToDisplay();
+
+ % Prepare default name of the panel
+ if validToDisplay && strlength(model.Name)
+ groupTitle = obj.TitlePrefix + ": " + model.Name;
+ else
+ groupTitle = obj.TitlePrefix;
+ end
+
+ % Update the panel name
+ obj.TiledLayout.Title.String = groupTitle;
+
+ end %function
+
+
+ function requestUpdate(obj)
+ % Request update to occur at next drawnow cycle
+
+ obj.TriggerUpdate_BVC = ~obj.TriggerUpdate_BVC;
+
+ end %function
+
+
+ function onModelSet(obj)
+ % Triggered when Model has been set to a new value
+
+ % Request an update
+ obj.requestUpdate();
+
+ % Call superclass method
+ obj.onModelSet@wt.mixin.ModelObserver()
+
+ end %function
+
+
+ function onModelChanged(obj,evt)
+ % Triggered when a property within the model has changed
+
+ % Request an update
+ obj.requestUpdate();
+
+ % Call superclass method
+ obj.onModelChanged@wt.mixin.ModelObserver(evt)
+
+ end %function
+
+ end %methods
+
+end %classdef
\ No newline at end of file
diff --git a/widgets/+wt/+abstract/BaseViewController.m b/widgets/+wt/+abstract/BaseViewController.m
new file mode 100644
index 0000000..928babe
--- /dev/null
+++ b/widgets/+wt/+abstract/BaseViewController.m
@@ -0,0 +1,202 @@
+classdef BaseViewController < wt.mixin.ModelObserver & ...
+ matlab.ui.componentcontainer.ComponentContainer
+ % Base class for views/controllers referencing a BaseModel class
+
+ % Copyright 2024 The MathWorks Inc.
+
+
+ %% Public Properties
+ properties (AbortSet)
+
+ % The default panel name prefix to display
+ PanelNamePrefix (1,1) string
+
+ end %properties
+
+
+ %% Internal Properties
+ properties (Hidden, SetAccess = protected)
+
+ % Internal grid to place outer panel contents
+ OuterGrid matlab.ui.container.GridLayout
+
+ % Outer panel with optional title
+ OuterPanel matlab.ui.container.Panel
+
+ % The internal grid to manage contents
+ Grid matlab.ui.container.GridLayout
+
+ end %properties
+
+
+ properties (Transient, NonCopyable, UsedInUpdate = true, ...
+ GetAccess = private, SetAccess = protected)
+
+ % Internal flag to trigger an update call
+ TriggerUpdate_BVC (1,1) logical = false
+
+ end %properties
+
+
+ %% Constructor
+ methods
+ function obj = BaseViewController(varargin)
+ % Constructor
+
+ % Call superclass constructors
+ obj = obj@matlab.ui.componentcontainer.ComponentContainer(varargin{:});
+ obj@wt.mixin.ModelObserver();
+
+ end %function
+ end %methods
+
+
+ %% Debugging Methods
+ methods
+
+ function forceUpdate(obj)
+ % Forces update to run (For debugging only!)
+
+ obj.update();
+
+ end %function
+
+ end %methods
+
+
+ %% Protected Methods
+ methods (Access = protected)
+
+ function setup(obj)
+ % Configure the widget
+
+ % Set default positioning
+ warnState = warning('off','MATLAB:ui:components:noPositionSetWhenInLayoutContainer');
+ obj.Units = "normalized";
+ obj.Position = [0 0 1 1];
+ warning(warnState);
+
+ % Outer grid to place the outer panel
+ obj.OuterGrid = uigridlayout(obj, [1 1]);
+ obj.OuterGrid.Padding = [0 0 0 0];
+
+ % Make an outer panel
+ obj.OuterPanel = uipanel(obj.OuterGrid);
+
+ % Name the panel with the view's class name by default
+ obj.PanelNamePrefix = extract(string(class(obj)), ...
+ alphanumericsPattern + textBoundary);
+ obj.OuterPanel.Title = obj.PanelNamePrefix;
+
+ % Grid Layout to manage contents
+ obj.Grid = uigridlayout(obj.OuterPanel,[5 2]);
+ obj.Grid.Padding = 10;
+ obj.Grid.ColumnWidth = {'fit','1x'};
+ obj.Grid.RowHeight = {'fit','fit','fit','fit','fit'};
+ obj.Grid.ColumnSpacing = 5;
+ obj.Grid.RowSpacing = 10;
+ obj.Grid.Scrollable = true;
+
+ end %function
+
+
+ function update(obj)
+
+ % Get the model to display
+ [model, validToDisplay] = obj.getScalarModelToDisplay();
+
+ % Prepare default name of the panel
+ if validToDisplay && strlength(model.Name)
+ panelTitle = obj.PanelNamePrefix + ": " + model.Name;
+ else
+ panelTitle = obj.PanelNamePrefix;
+ end
+
+ % Update the panel name
+ obj.OuterPanel.Title = panelTitle;
+
+ end %function
+
+
+ function requestUpdate(obj)
+ % Request update to occur at next drawnow cycle
+
+ obj.TriggerUpdate_BVC = ~obj.TriggerUpdate_BVC;
+
+ end %function
+
+
+ function onModelSet(obj)
+ % Triggered when Model has been changed
+
+ % Request an update
+ obj.requestUpdate();
+
+ % Call superclass method
+ obj.onModelSet@wt.mixin.ModelObserver()
+
+ end %function
+
+ function onModelChanged(obj,evt)
+ % Triggered when a property within the model has changed
+
+ % Request an update
+ obj.requestUpdate();
+
+ % Call superclass method
+ obj.onModelChanged@wt.mixin.ModelObserver(evt)
+
+ end %function
+
+
+ function onFieldEdited(obj,evt,fieldName,index)
+ % This is a generic callback that simple controls may use. For
+ % example, the ValueChangedFcn for an edit field may call this
+ % directly with the Model's property name that should be
+ % updated. Use this for callbacks of simple controls that can
+ % set a field simply by name.
+
+ arguments
+ obj (1,1) wt.abstract.BaseViewController
+ evt
+ fieldName (1,1) string
+ index (1,:) double {mustBeInteger,mustBePositive} = zeros(1,0)
+ end
+
+ if ~isscalar(obj.Model)
+ warning("wt:BaseViewController:onFieldEdited:NonScalarModel",...
+ "The onFieldEdited method is unable to set new " + ...
+ "value to an empty or nonscalar model.")
+ return
+ end
+
+ % Get the new value
+ newValue = evt.Value;
+
+ % Treat char as string
+ if ischar(newValue)
+ newValue = string(newValue);
+ end
+
+ % Handle array values with the index input indicating what
+ % index of the newValue was changed
+ if ~isscalar(newValue) && ~isempty(index) && ...
+ isscalar(obj.Model.(fieldName)(index))
+
+ % We have one index of an array being set
+ obj.Model.(fieldName)(index) = newValue(index);
+
+ else
+
+ % We are setting the entire array
+ obj.Model.(fieldName) = newValue;
+
+ end
+
+ % Set the new value
+
+ end %function
+
+ end %methods
+
+end %classdef
\ No newline at end of file
diff --git a/widgets/+wt/+abstract/BaseWidget.m b/widgets/+wt/+abstract/BaseWidget.m
index 55cc044..f140057 100644
--- a/widgets/+wt/+abstract/BaseWidget.m
+++ b/widgets/+wt/+abstract/BaseWidget.m
@@ -38,7 +38,7 @@
properties (Transient, Access = private)
% Flag to indicate earlier release (to be updated during setup)
- IsPre21a (1,1) logical = verLessThan('matlab','9.10')
+ IsPre21a (1,1) logical = isMATLABReleaseOlderThan("R2021a")
end %properties
diff --git a/widgets/+wt/+apps/AbstractSessionApp.m b/widgets/+wt/+apps/AbstractSessionApp.m
new file mode 100644
index 0000000..708225e
--- /dev/null
+++ b/widgets/+wt/+apps/AbstractSessionApp.m
@@ -0,0 +1,431 @@
+classdef (Abstract, AllowedSubclasses = {?wt.apps.BaseSingleSessionApp, ...
+ ?wt.apps.BaseMultiSessionApp}) AbstractSessionApp < wt.apps.BaseApp
+ % Abstract base class for Widgets Toolbox app with 1+ sessions
+
+ % Copyright 2024 The MathWorks Inc.
+
+
+ %% Abstract Public Properties
+ properties (Abstract, AbortSet, SetObservable)
+
+ % Session data for the app (must be subclass of wt.model.BaseSession)
+ Session (1,:) wt.model.BaseSession
+
+ end %properties
+
+
+
+ %% Abstract Methods (subclass must implement these)
+ methods (Abstract, Access = protected)
+
+ % Creates a new session object for the app. It must return a
+ % subclass of wt.model.BaseSession
+ sessionObj = createNewSession(app)
+
+ end %methods
+
+
+
+ %% Internal properties
+ properties (Transient, NonCopyable, Access = private)
+
+ % Listener to Session property being set
+ SessionSetListener event.listener
+
+ % Listener to changes within Session object
+ SessionChangedListener event.listener
+
+ % Listeners to session marked clean/dirty
+ SessionDirtyListener event.listener
+
+ end %properties
+
+
+ properties (Dependent, SetAccess = immutable)
+
+ % Indicates a valid session is present
+ HasValidSession (1,1) logical
+
+ % Indicates if session is dirty
+ Dirty (1,1) logical
+
+ end %properties
+
+
+ % Accessors
+ methods
+
+ function value = get.HasValidSession(app)
+ value = ~isempty(app.Session) && any(isvalid(app.Session));
+ end
+
+ function value = get.Dirty(app)
+ value = ~isempty(app.Session) && ...
+ any( app.Session(isvalid(app.Session)).Dirty );
+ end
+
+ end %methods
+
+
+
+ %% Constructor
+ methods (Access = public)
+
+ function app = AbstractSessionApp(varargin)
+ % Constructor
+
+ % Call superclass constructor
+ app@wt.apps.BaseApp(varargin{:});
+
+ % Attach listeners to Session being set
+ app.SessionSetListener = listener(app,"Session","PostSet",...
+ @(~,~)app.onSessionSet_Private());
+
+ end %function
+
+ end %methods
+
+
+ %% Internal Protected Methods
+ methods (Access = {?wt.apps.BaseSingleSessionApp, ?wt.apps.BaseMultiSessionApp})
+
+ function close_Internal(app)
+ % Close the app
+
+ % Define arguments
+ arguments
+ app (1,1) wt.apps.BaseApp
+ end
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
+ % Freeze the figure with a progress dialog
+ dlg = app.showIndeterminateProgress("Closing");
+ cleanupObj = onCleanup(@()delete(dlg));
+
+ % Delete the app
+ app.delete();
+
+ end %function
+
+
+ function isCancelled = closeSession_Internal(app, session)
+ % Close a session
+
+ % Define arguments
+ arguments
+ app (1,1) wt.apps.BaseApp
+ session wt.model.BaseSession
+ end
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
+ % Prompt to save existing session if dirty
+ isCancelled = promptToSaveSession(app, session);
+
+ % Close the session
+ if ~isCancelled
+
+ % Remove session from the app
+ isMatch = app.Session == session;
+ app.Session(isMatch) = [];
+
+ end %if
+
+ end %function
+
+
+ function saveSession_Internal(app, useSaveAs, session)
+ % Save the session to a file
+
+ % Define arguments
+ arguments
+ app (1,1) wt.apps.BaseApp
+ useSaveAs (1,1) logical
+ session wt.model.BaseSession
+ end
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
+ % Check/prep the session path
+ sessionPath = app.checkSessionPath_Internal(session);
+
+ % Prompt for "save as" if needed
+ if useSaveAs || ~isfile(sessionPath)
+ sessionPath = app.promptToSaveAs(sessionPath);
+ end
+
+ % Save the file (unless path is empty indicating cancel)
+ if strlength(sessionPath)
+
+ % Freeze the figure with a progress dialog
+ dlg = app.showIndeterminateProgress("Saving Session");
+ cleanupObj = onCleanup(@()delete(dlg));
+
+ % Save the session
+ session.save(sessionPath);
+
+ % Update the app in case filepath changed
+ app.updateTitle()
+ app.update()
+
+ end %if strlength(sessionPath)
+
+ end %function
+
+
+ function sessionPath = checkSessionPath_Internal(app, session)
+ % Check / prepare the session path
+
+ % Define arguments
+ arguments
+ app (1,1) wt.apps.BaseApp
+ session wt.model.BaseSession
+ end
+
+ % Get the session info
+ sessionPath = session.FilePath;
+ sessionFolder = fileparts(sessionPath);
+ sessionName = session.FileName;
+ lastFolder = app.LastFolder;
+
+ % If file does not already exist, make default path
+ if isfile(sessionPath)
+ % Do nothing special
+ elseif isfolder(sessionFolder)
+ % File not found but folder was
+ sessionPath = fullfile(sessionFolder,sessionName);
+ else
+ % Neither file nor folder give, so use defaults
+ sessionPath = fullfile(lastFolder,sessionName);
+ end
+
+ end %function
+
+
+ function session = loadSession_Internal(app, sessionPath)
+ % Load a session from a file
+
+ % Define arguments
+ arguments
+ app (1,1) wt.apps.BaseApp
+ sessionPath (1,1) string = ""
+ end
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
+ % Unless a file was already specified, prompt to load
+ if ~isfile(sessionPath)
+ sessionPath = app.promptToLoad();
+ end
+
+ % Check the file name of this session
+ [~,sessionFileName,sessionFileExt] = fileparts(sessionPath);
+ sessionFileName = sessionFileName + sessionFileExt;
+ if isempty(app.Session)
+ isAlreadyOpen = false;
+ else
+ existingSessionFiles = vertcat( app.Session.FileName );
+ isAlreadyOpen = any( matches(sessionFileName, ...
+ existingSessionFiles, "IgnoreCase", true) );
+ end
+
+ % How should we proceed?
+ if isAlreadyOpen
+
+ % Return an empty session of correct type
+ session = app.getEmptySession();
+
+ % Throw an error
+ title = "Load Session";
+ message = sprintf("""%s"" is already open.", ...
+ sessionFileName);
+ app.throwError(message, title);
+
+ elseif isfile(sessionPath)
+ % Load the file
+
+ % Freeze the figure with a progress dialog
+ dlg = app.showIndeterminateProgress("Loading Session");
+ cleanupObj = onCleanup(@()delete(dlg));
+
+ % Load the session
+ try
+ session = wt.model.BaseSession.open(sessionPath);
+ catch err
+ session = app.getEmptySession();
+ app.throwError(err)
+ end
+
+ else
+
+ % Return an empty session of correct type
+ session = app.getEmptySession();
+
+ end %if
+
+ end %function
+
+
+ function isCancelled = promptToSaveSession(app, session)
+ % Prompt the user to save their session before losing it
+
+ % Define arguments
+ arguments
+ app (1,1) wt.apps.BaseApp
+ session (1,1) wt.model.BaseSession = app.Session
+ end
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
+ % Default output
+ isCancelled = false;
+
+ % Don't save and return now if session is invalid or clean
+ if ~isvalid(session) || ~session.Dirty
+ return
+ end
+
+ % Prompt whether to save
+ message = sprintf("Save changes to '%s'?",session.FileName);
+ title = "Load Session";
+ selection = app.promptYesNoCancel(message, title);
+
+ % If Yes, prompt to save the existing session first
+ if selection == "Yes"
+ sessionSavePath = app.saveSession();
+ if ~strlength(sessionSavePath)
+ isCancelled = true;
+ end
+ elseif selection == "Cancel"
+ isCancelled = true;
+ end
+
+ end %function
+
+ end %methods
+
+
+ %% Sealed Protected Methods
+ methods (Sealed, Access = protected)
+
+ function session = getEmptySession(app)
+ % Returns an empty session of the same type as Session property
+
+ session = app.Session(1,[]);
+
+ end %function
+
+ end %methods
+
+
+ %% Protected Methods
+ methods (Access = protected)
+
+ function onSessionSet(app)
+ % Triggered when a SetObservable property in the session has
+ % changed. May be overridden for custom behavior using incoming
+ % event data.
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
+ % Trigger updates
+ if app.SetupComplete
+ app.update();
+ app.updateTitle();
+ end
+
+ end %function
+
+ function onSessionChanged(app,~)
+ % Triggered when a SetObservable property in the session has
+ % changed. May be overridden for custom behavior using incoming
+ % event data.
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
+ % Update the app
+ if app.SetupComplete
+ app.update();
+ app.updateTitle();
+ end
+
+ end %function
+
+
+ function onSessionDirty(app,~)
+ % Triggered when the session's MarkedDirty event fires
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
+ % Update the title only
+ % (prop change should have triggered update already)
+ if app.SetupComplete
+ app.updateTitle();
+ end
+
+ end %function
+
+
+ function onSessionClean(app,~)
+ % Triggered when the session's MarkedClean event fires
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
+ % Update the app
+ if app.SetupComplete
+ app.update();
+ app.updateTitle();
+ end
+
+ end %function
+
+ end %methods
+
+
+ %% Private Methods
+ methods (Access = private)
+
+ function onSessionChanged_Private(app,evt)
+ % Triggered when attached Session has a ModelChanged event
+
+ % Show output if Debug is on
+ app.displayDebugText(evt);
+
+ % Call the app's session changed method
+ app.onSessionChanged(evt);
+
+ end %function
+
+
+ function onSessionSet_Private(app)
+ % Triggered after Session property is set
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
+ app.SessionChangedListener = listener(app.Session,...
+ 'ModelChanged',@(~,evt)app.onSessionChanged_Private(evt));
+
+ app.SessionDirtyListener = [
+ listener(app.Session,'MarkedDirty',@(~,evt)app.onSessionDirty(evt))
+ listener(app.Session,'MarkedClean',@(~,evt)app.onSessionClean(evt))
+ ];
+
+ % Call the app's session set method
+ app.onSessionSet();
+
+ end %function
+
+ end %methods
+
+end %classdef
\ No newline at end of file
diff --git a/widgets/+wt/+apps/BaseApp.m b/widgets/+wt/+apps/BaseApp.m
index 696fca1..fcfee02 100644
--- a/widgets/+wt/+apps/BaseApp.m
+++ b/widgets/+wt/+apps/BaseApp.m
@@ -1,101 +1,165 @@
-classdef BaseApp < matlab.apps.AppBase & matlab.mixin.SetGetExactNames & ...
+classdef BaseApp < matlab.apps.AppBase & ...
+ matlab.mixin.SetGetExactNames & ...
+ matlab.mixin.CustomDisplay & ...
wt.mixin.ErrorHandling
% Base class for Widgets Toolbox apps
-
- % Copyright 2020-2023 The MathWorks, Inc.
-
-
+
+ % Copyright 2020-2024 The MathWorks, Inc.
+
+
%% Properties
properties (AbortSet)
-
+
% Name of the app
Name (1,1) string = "My App"
-
+
end %properties
-
-
+
+
properties (SetAccess = protected)
-
+
% Model class for App preferences
%(may subclass wt.model.Preferences to add more prefs)
Preferences (1,1) wt.model.Preferences
-
+
% Name of group to store preferences (defaults to class name)
PreferenceGroup (1,1) string
-
+
end %properties
-
-
+
+
properties (AbortSet, Dependent)
-
+
% Position of the app window
Position
-
+
% Visibility of the app window
Visible
-
+
% State of the app window
WindowState
-
+
end %properties
-
-
-
+
+
+ % Accessors
+ methods
+
+ function set.Name(app,value)
+ app.Name = value;
+ app.updateTitle();
+ end
+
+
+ function value = get.PreferenceGroup(app)
+ value = app.PreferenceGroup;
+ if ~strlength(value)
+ value = class(app);
+ end
+ value = matlab.lang.makeValidName(value);
+ end
+
+
+ function value = get.Position(app)
+ value = app.Figure.Position;
+ end
+
+ function set.Position(app,value)
+ app.Figure.Position = value;
+ end
+
+
+ function value = get.Visible(app)
+ value = app.Figure.Visible;
+ end
+
+ function set.Visible(app,value)
+ app.Figure.Visible = value;
+ end
+
+
+ function value = get.WindowState(app)
+ value = app.Figure.WindowState;
+ end
+
+ function set.WindowState(app,value)
+ app.Figure.WindowState = value;
+ end
+
+ end %methods
+
+
+
%% Internal properties
- properties (Hidden, Transient, NonCopyable, SetAccess = immutable)
-
+ properties (Transient, NonCopyable, SetAccess = immutable)
+
% Figure window of the app
Figure matlab.ui.Figure
-
+
% Primary grid to place contents
Grid matlab.ui.container.GridLayout
-
+
end %properties
-
-
- properties (Transient, NonCopyable, Hidden, SetAccess = protected)
-
+
+
+ properties (Transient, NonCopyable, SetAccess = protected)
+
% Last used folder (for file operations)
LastFolder (1,1) string = pwd
-
+
% Is setup complete?
SetupComplete (1,1) logical = false;
-
+
end %properties
-
-
-
+
+
+
%% Abstract methods (subclass must implement these)
methods (Abstract, Access = protected)
-
+
setup(app)
-
update(app)
-
+
+ end %methods
+
+ methods (Abstract, Access = public)
+
+ close(app)
+
+ end %methods
+
+
+
+ %% Debugging
+ properties (Transient)
+
+ % Toggle true to enable debugging display
+ Debug (1,1) logical = false
+
end %methods
-
-
-
- %% Debugging Methods
+
+
methods
-
+
function forceUpdate(app)
% Forces update to run (For debugging only!)
-
+
app.update();
-
+
+ drawnow
+
end %function
-
+
end %methods
-
-
-
+
+
+
%% Constructor / destructor
methods (Access = public)
-
+
function app = BaseApp(varargin)
% Constructor
-
+
% Create the figure and hide until components are created
app.Figure = uifigure( ...
'AutoResizeChildren','off',...
@@ -103,21 +167,21 @@ function forceUpdate(app)
'DeleteFcn',@(h,e)delete(app), ...
'CloseRequestFcn',@(h,e)close(app), ...
'Visible','off');
-
+
% Create MainGridLayout
app.Grid = uigridlayout(app.Figure,[1 1]);
app.Grid.Padding = [0 0 0 0];
-
+
% Check for preference input and assign it first, in case
% Preferences was subclassed
% [splitArgs,varargin] = app.splitArgs('Preferences', varargin{:});
% if ~isempty(splitArgs)
% app.Preferences = splitArgs{2};
% end
-
+
% Retrieve preferences
app.loadPreferences();
-
+
% Load last figure position
% Note app.Figure.Position is inner position, where
% app.Position is outer position. Inner position is the same
@@ -126,74 +190,72 @@ function forceUpdate(app)
% purpose so it does not depend on if/when any menubar or
% toolbar is added to the figure.
app.Figure.Position = app.getPreference('Position',[100 100 1000 700]);
-
+
% Set up components
app.setup_internal();
app.setup();
-
+
% Set any P-V pairs
if ~isempty(varargin)
set(app, varargin{:});
end
-
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
% Register the app with App Designer
registerApp(app, app.Figure)
-
+
% Ensure it's on screen
app.moveOnScreen();
-
+
% Mark the setup complete
app.SetupComplete = true;
-
+
% Update the app
app.update();
-
+
% Update the title
app.updateTitle();
-
+
% Force drawing to finish
- drawnow
-
+ drawnow('limitrate')
+
% Now, make it visible
app.Figure.Visible = 'on';
-
+
end %function
-
-
+
+
function delete(app)
% Destructor
-
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
% Store last position in preferences
if isscalar(app.Figure) && isvalid(app.Figure)
app.setPreference('Position',app.Figure.Position)
end
-
+
% Save preferences
app.savePreferences();
-
+
% Now, delete the figure
delete(app.Figure)
-
+
end %function
-
+
end %methods
-
-
-
+
+
+
%% Public Methods
methods
-
- function close(app)
- % Triggered on figure closed
-
- app.delete();
-
- end %function
-
-
+
function selection = promptYesNoCancel(app, message, title, default, icon)
% Prompt the user with a yes/no/cancel selection
-
+
% Define arguments
arguments
app (1,1) wt.apps.BaseApp
@@ -202,20 +264,23 @@ function close(app)
default (1,1) string = "Cancel"
icon (1,1) string = "question"
end
-
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
% Launch the prompt
selection = uiconfirm(app.Figure, message, title,...
"Options",["Yes","No","Cancel"],...
"DefaultOption",default,...
"CancelOption","Cancel",...
"Icon",icon);
-
+
end %function
-
-
+
+
function filePath = promptToSaveAs(app, filePath, filter, title)
% Prompt the user to save a file
-
+
% Define arguments
arguments
app (1,1) wt.apps.BaseApp
@@ -223,10 +288,13 @@ function close(app)
filter = ["*.mat","MATLAB MAT File"];
title (1,1) string = "Save as"
end
-
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
% Prompt for the file
[fileName,pathName] = uiputfile(filter, title, filePath);
-
+
% Did the user cancel?
if isequal(fileName,0)
filePath = string.empty(0);
@@ -234,43 +302,46 @@ function close(app)
filePath = fullfile(pathName,fileName);
app.LastFolder = pathName;
end %if isequal(fileName,0)
-
+
end %function
-
-
+
+
function filePath = promptToLoad(app, filter, title)
% Prompt the user to load a file
-
+
% Define arguments
arguments
app (1,1) wt.apps.BaseApp
filter = ["*.mat","MATLAB MAT File"];
title (1,1) string = "Open"
end
-
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
% Prompt for the file
[fileName,pathName] = uigetfile(filter, title, app.LastFolder);
-
+
% Did the user cancel?
if isequal(fileName,0)
filePath = string.empty(0);
else
- filePath = fullfile(pathName,fileName);
+ filePath = string( fullfile(pathName,fileName) );
app.LastFolder = pathName;
end %if isequal(fileName,0)
-
+
end %function
-
+
end %methods
-
-
-
+
+
+
%% Sealed Public methods
methods (Sealed)
-
+
function value = getPreference(app,propName,defaultValue)
% Get an app preference from the Preferences object
-
+
if isprop(app.Preferences, propName)
value = app.Preferences.(propName);
elseif nargin>2
@@ -278,188 +349,234 @@ function close(app)
else
value = [];
end
-
+
end %function
-
-
+
+
function setPreference(app,propName,value)
% Set an app preference in the Preferences object
-
+
if ~isprop(app.Preferences,propName)
addprop(app.Preferences,propName);
end
app.Preferences.(propName) = value;
-
+
end %function
-
-
+
+
function moveOnScreen(app)
% Ensure the figure is placed on screen
-
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
if strcmp(app.Figure.Units,'pixels')
-
+
% Get the corners of each screen
g = groot;
screenPos = g.MonitorPositions;
screenCornerA = screenPos(:,1:2);
screenCornerB = screenPos(:,1:2) + screenPos(:,3:4) - 1;
-
+
% Buffer for title bar
titleBarHeight = 30;
-
+
% Get the corners of the figure (bottom left and top right)
figPos = app.Figure.OuterPosition;
figCornerA = figPos(1:2);
figCornerB = figPos(1:2) + figPos(:,3:4) - 1;
-
+
% Are the corners on any screen?
aIsOnScreen = all( figCornerA >= screenCornerA & ...
figCornerA <= screenCornerB, 2 );
bIsOnScreen = all( figCornerB >= screenCornerA & ...
figCornerB <= screenCornerB, 2);
-
+
% Are corners on a screen?
-
+
% Are both corners fully on any screen?
if any(aIsOnScreen) && any(bIsOnScreen)
% Yes - do nothing
-
+
elseif any(bIsOnScreen)
% No - only upper right corner is on a screen
-
+
% Calculate the adjustment needed, and make it
figAdjust = max(figCornerA, screenCornerA(bIsOnScreen,:)) ...
- figCornerA;
figPos(1:2) = figPos(1:2) + figAdjust;
-
+
% Ensure the upper right corner still fits
figPos(3:4) = min(figPos(3:4), ...
screenCornerB(bIsOnScreen,:) - figPos(1:2) - [0 titleBarHeight] + 1);
-
+
% Move the figure
app.Figure.Position = figPos;
-
+
elseif any(aIsOnScreen)
% No - only lower left corner is on a screen
-
+
% Calculate the adjustment needed, and make it
figAdjust = min(figCornerB, screenCornerB(aIsOnScreen,:)) ...
- figCornerB;
figPos(1:2) = max( screenCornerA(aIsOnScreen,:),...
figPos(1:2) + figAdjust );
-
+
% Ensure the upper right corner still fits
figPos(3:4) = min(figPos(3:4), ...
screenCornerB(aIsOnScreen,:) - figPos(1:2) - [0 titleBarHeight] + 1);
-
+
% Move the figure
app.Figure.Position = figPos;
-
+
else
% No - Not on any screen
-
+
% This is slower, but uncommon anyway
movegui(app.Figure,'onscreen');
-
+
end %if any( all(aIsOnScreen,2) & all(bIsOnScreen,2) )
-
+
else
-
+
% This is slower, but uncommon anyway
movegui(app.Figure,'onscreen');
-
+
end %if strcmp(app.Figure.Units,'pixels')
-
+
end %function
-
+
end %methods
-
-
-
+
+
+
%% Protected Methods
methods (Access = protected)
- function setup_internal(~)
+ function displayDebugText(app, evt)
+ % Display the path to the caller function in the command window
+
+ if app.Debug
+
+ stackInfo = dbstack(1,'-completenames');
+ fcnName = string(stackInfo(1).name);
+ filePath = string( stackInfo(1).file );
+ namespaces = extractBetween(filePath, "+", "\");
+ classname = extractBetween(filePath, "@", "\");
+ pathParts = vertcat(namespaces, classname, fcnName);
+ dispPath = join(pathParts, ".");
+ appClass = class(app);
+
+ formatStr = ' [%s] %s\n';
+ fprintf(formatStr, appClass, filePath, dispPath);
+
+ if nargin >= 2
+ fprintf(' --------------------\n Event Data:\n\n');
+ disp(evt);
+ fprintf(' --------------------\n')
+ end
+
+ end %if
+
+ end %function
+
+
+ function setup_internal(app)
% Preform internal pre-setup necessary
-
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
% This is used for session managed apps
-
+
end %function
-
-
+
+
function loadPreferences(app)
% Load stored preferences
-
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
app.Preferences.load(app.PreferenceGroup);
-
+
end %function
-
-
+
+
function savePreferences(app)
% Save preferences
-
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
app.Preferences.save(app.PreferenceGroup);
-
+
end %function
-
-
+
+
function updateTitle(app)
% Update the figure title
-
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
app.Figure.Name = app.Name;
-
+
end %function
-
+
end %methods
-
-
-
- %% Accessors
- methods
-
- function set.Name(app,value)
- app.Name = value;
- app.updateTitle();
- end
-
-
- function value = get.PreferenceGroup(app)
- value = app.PreferenceGroup;
- if isempty(value)
- value = class(app);
- end
- value = matlab.lang.makeValidName(value);
- end
-
-
- function value = get.Position(app)
- value = app.Figure.Position;
- end
-
- function set.Position(app,value)
- app.Figure.Position = value;
- end
-
-
- function value = get.Visible(app)
- value = app.Figure.Visible;
- end
-
- function set.Visible(app,value)
- app.Figure.Visible = value;
- end
-
-
- function value = get.WindowState(app)
- value = app.Figure.WindowState;
- end
-
- function set.WindowState(app,value)
- app.Figure.WindowState = value;
- end
-
+
+
+
+ %% Display Customization
+ methods (Access = protected)
+
+ function propGroups = getPropertyGroups(app)
+ % Customize how the properties are displayed
+
+ import matlab.mixin.util.PropertyGroup
+
+ persistent pGroups
+ if isempty(pGroups)
+
+ % BaseApp properties
+ baseAppTitle = " ------ BaseApp Properties ------";
+ baseAppProperties = properties("wt.apps.BaseApp");
+ usedProps = baseAppProperties;
+
+ % Get properties for concrete class
+ mc = metaclass(app);
+ propInfo = mc.PropertyList;
+
+ % Filter out used properties
+ [~,idxA] = setdiff({propInfo.Name}, usedProps, "stable");
+ propInfo = propInfo(idxA);
+
+ % Split out read-only properties
+ getInfo = {propInfo.GetAccess};
+ setInfo = {propInfo.SetAccess};
+ isPublicGet = cellfun(@(x)isequal(x,'public'), getInfo);
+ isPublicSet = cellfun(@(x)isequal(x,'public'), setInfo);
+ concPublicSetProps = {propInfo(isPublicGet & isPublicSet).Name};
+ concProtectedSetProps = {propInfo(isPublicGet & ~isPublicSet).Name};
+
+ % Set titles
+ concPublicTitle = " ------ " + app.Name + " Public Properties ------";
+ concProtectedTitle = " ------ " + app.Name + " Read-Only Properties ------";
+
+ pGroups = [
+ PropertyGroup(concProtectedSetProps, concProtectedTitle)
+ PropertyGroup(concPublicSetProps, concPublicTitle)
+ PropertyGroup(baseAppProperties, baseAppTitle)
+ ];
+
+ end %if
+
+ propGroups = pGroups;
+
+ end %function
+
end %methods
-
-
-end % classdef
+
+end % classdef
\ No newline at end of file
diff --git a/widgets/+wt/+apps/BaseMultiSessionApp.m b/widgets/+wt/+apps/BaseMultiSessionApp.m
new file mode 100644
index 0000000..81ccf41
--- /dev/null
+++ b/widgets/+wt/+apps/BaseMultiSessionApp.m
@@ -0,0 +1,363 @@
+classdef BaseMultiSessionApp < wt.apps.AbstractSessionApp
+ % Base class for Widgets Toolbox app with multiple managed sessions
+
+ % Copyright 2024 The MathWorks Inc.
+
+
+ %% Properties
+ properties (AbortSet, SetObservable)
+
+ % Session data for the app (must be subclass of wt.model.BaseSession)
+ Session
+
+ end %properties
+
+
+ %% Read-Only Properties
+ properties (AbortSet, SetAccess = private)
+
+ % Index of currently selected session
+ SelectedSessionIndex double ...
+ {mustBeInteger, mustBePositive, mustBeScalarOrEmpty} = []
+
+ end %properties
+
+
+ properties (AbortSet, Dependent, SetAccess = private)
+
+ % Number of sessions loaded
+ NumSessions (1,1) double
+
+ % Currently selected session
+ SelectedSession wt.model.BaseSession
+
+ % Name of the currently selected session
+ SelectedSessionName
+
+ % File path of the currently selected session
+ SelectedSessionPath
+
+ end %properties
+
+
+ % Accessors
+ methods
+
+ function value = get.SelectedSessionIndex(app)
+ value = app.SelectedSessionIndex;
+ numSessions = app.NumSessions;
+ if numSessions == 0
+ value = [];
+ elseif value > numSessions
+ value = app.NumSessions;
+ end
+ end
+
+ function value = get.NumSessions(app)
+ value = numel(app.Session);
+ end
+
+ function value = get.SelectedSession(app)
+ value = app.Session( app.SelectedSessionIndex );
+ end
+
+ function value = get.SelectedSessionName(app)
+ session = app.SelectedSession;
+ if ~isempty(session) && isvalid(session)
+ value = session.FileName;
+ else
+ value = "";
+ end
+ end
+
+ function value = get.SelectedSessionPath(app)
+ session = app.SelectedSession;
+ if ~isempty(session) && isvalid(session)
+ value = session.FilePath;
+ else
+ value = "";
+ end
+ end
+
+ end %methods
+
+
+ %% Sealed Public methods
+ methods (Sealed)
+
+ function close(app)
+ % Close the app
+
+ % Define arguments
+ arguments
+ app (1,1) wt.apps.BaseApp
+ end
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
+ % Prompt to save existing sessions
+ for session = app.Session
+ isCancelled = promptToSaveSession(app, session);
+ if isCancelled
+ return
+ end %if
+ end %for
+
+ % Close the app
+ app.close_Internal();
+
+ end %function
+
+
+ function session = newSession(app)
+ % Start a new session
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
+ % Freeze the figure with a progress dialog
+ dlg = app.showIndeterminateProgress();
+ cleanupObj = onCleanup(@()delete(dlg));
+
+ % Instantiate the new session
+ session = app.createNewSession();
+
+ % Store the session
+ % This also triggers app.update(), app.updateTitle()
+ if isempty(app.Session)
+ app.Session = session;
+ else
+ app.Session(end+1) = session;
+ % Don't select by default, but concrete app may do this
+ end
+
+ end %function
+
+
+ function sessionPath = saveSession(app, useSaveAs, session)
+ % Save the session to a file
+
+ % Define arguments
+ arguments
+ app (1,1) wt.apps.BaseApp
+ useSaveAs (1,1) logical = false
+ session wt.model.BaseSession = app.SelectedSession
+ end
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
+ % Confirm scalar session
+ if isscalar(session)
+
+ % Call superclass method
+ app.saveSession_Internal(useSaveAs, session);
+
+ % Populate output
+ sessionPath = session.FilePath;
+
+ else
+
+ % Throw an error
+ title = "Save Session";
+ message = "No session was selected.";
+ app.throwError(message, title);
+
+ sessionPath = "";
+
+ end %if
+
+ end %function
+
+
+ function session = loadSession(app, sessionPath)
+ % Load a session from a file
+
+ % Define arguments
+ arguments
+ app (1,1) wt.apps.BaseApp
+ sessionPath (1,1) string = ""
+ end
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
+ % Call superclass internal load method
+ session = app.loadSession_Internal(sessionPath);
+
+ % Store the session
+ % This also triggers app.update(), app.updateTitle()
+ if ~isscalar(session)
+ % User cancelled
+ return
+ elseif isempty(app.Session)
+ app.Session = session;
+ else
+ app.Session(end+1) = session;
+ % Don't select by default, but concrete app may do this
+ end
+
+ end %function
+
+
+ function closeSession(app, session)
+ % Close the specified session
+
+ % Define arguments
+ arguments
+ app (1,1) wt.apps.BaseApp
+ session wt.model.BaseSession = app.SelectedSession
+ end
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
+ % Exit if session is empty
+ if isempty(session) || ~isvalid(session)
+ return
+ end
+
+ % Call superclass internal close method
+ isCancelled = app.closeSession_Internal(session);
+
+ % Deselect the closed session
+ if ~isCancelled
+ if isequal(app.SelectedSession, session)
+ emptySession = app.getEmptySession();
+ app.selectSession(emptySession)
+ end
+ end
+
+ end %function
+
+
+ function selectSession(app, session)
+ % Select the specified session
+
+ % Define arguments
+ arguments
+ app (1,1) wt.apps.BaseApp
+ session wt.model.BaseSession
+ end
+
+ % Was a scalar session given?
+ if isscalar(session)
+
+ % Which index is the session?
+ selIdx = find(session == app.Session, 1);
+
+ % Select it
+ app.SelectedSessionIndex = selIdx;
+
+ else
+
+ % Empty selection
+ app.SelectedSessionIndex = [];
+
+ end
+
+ % Update the title
+ app.updateTitle()
+
+ end %function
+
+ end %methods
+
+
+ %% Protected Methods
+ methods (Access = protected)
+
+ function setup_internal(app)
+ % Preform internal pre-setup necessary
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
+ % Instantiate empty session to start
+ session = app.createNewSession();
+ app.Session = session(1,[]);
+
+ end %function
+
+
+ function updateTitle(app)
+ % Update the app title, showing the session name and dirty flag
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
+ % Decide on the figure title
+ session = app.SelectedSession;
+ if isempty(session) || ~isvalid(session)
+ app.Figure.Name = app.Name;
+ elseif session.Dirty
+ app.Figure.Name = app.Name + " - " + session.FileName + " *";
+ else
+ app.Figure.Name = app.Name + " - " + session.FileName;
+ end
+
+ end %function
+
+ end %methods
+
+
+ %% Display Customization
+ methods (Access = protected)
+
+ function propGroups = getPropertyGroups(app)
+ % Customize how the properties are displayed
+
+ import matlab.mixin.util.PropertyGroup
+
+ persistent pGroups
+ if isempty(pGroups)
+
+ % BaseApp properties
+ baseAppTitle = " ------ BaseApp Properties ------";
+ baseAppProperties = properties("wt.apps.BaseApp");
+ usedProps = baseAppProperties;
+
+ % BaseMultiSessionApp Properties
+ sessionTitle = " ------ BaseMultiSessionApp Properties ------";
+ sessionProperties = setdiff(...
+ properties("wt.apps.BaseMultiSessionApp"), usedProps);
+ usedProps = [baseAppProperties; sessionProperties];
+
+ % Get properties for concrete class
+ mc = metaclass(app);
+ propInfo = mc.PropertyList;
+
+ % Filter out used properties
+ [~,idxA] = setdiff({propInfo.Name}, usedProps, "stable");
+ propInfo = propInfo(idxA);
+
+ % Split out read-only properties
+ getInfo = {propInfo.GetAccess};
+ setInfo = {propInfo.SetAccess};
+ isPublicGet = cellfun(@(x)isequal(x,'public'), getInfo);
+ isPublicSet = cellfun(@(x)isequal(x,'public'), setInfo);
+ concPublicSetProps = {propInfo(isPublicGet & isPublicSet).Name};
+ concProtectedSetProps = {propInfo(isPublicGet & ~isPublicSet).Name};
+
+ % Set titles
+ concPublicTitle = " ------ " + app.Name + " Public Properties ------";
+ concProtectedTitle = " ------ " + app.Name + " Read-Only Properties ------";
+
+ pGroups = [
+ PropertyGroup(concProtectedSetProps, concProtectedTitle)
+ PropertyGroup(concPublicSetProps, concPublicTitle)
+ PropertyGroup(baseAppProperties, baseAppTitle)
+ PropertyGroup(sessionProperties, sessionTitle)
+ ];
+
+ end %if
+
+ propGroups = pGroups;
+
+ end %function
+
+ end %methods
+
+
+end %classdef
\ No newline at end of file
diff --git a/widgets/+wt/+apps/BaseSingleSessionApp.m b/widgets/+wt/+apps/BaseSingleSessionApp.m
index 9168cdd..955841c 100644
--- a/widgets/+wt/+apps/BaseSingleSessionApp.m
+++ b/widgets/+wt/+apps/BaseSingleSessionApp.m
@@ -1,240 +1,130 @@
-classdef (Abstract) BaseSingleSessionApp < wt.apps.BaseApp
+classdef (Abstract) BaseSingleSessionApp < wt.apps.AbstractSessionApp
% Base class for Widgets Toolbox app with a managed single session
- % Copyright 2020-2021 The MathWorks Inc.
+ % Copyright 2020-2024 The MathWorks Inc.
%% Properties
-
- properties (AbortSet)
+ properties (AbortSet, SetObservable)
% Session data for the app (must be subclass of wt.model.BaseSession)
- Session (1,1) wt.model.BaseSession {mustBeScalarOrEmpty} ...
- = wt.model.BaseSession;
-
- end %properties
-
-
-
- %% Internal properties
- properties (Dependent, SetAccess = immutable)
-
- % Indicates if any session is dirty
- Dirty (1,1) logical
-
- % Last used folder (for file operations)
- HasValidSession (1,1) logical
-
- end %properties
-
-
- properties (Transient, NonCopyable, Access = private)
-
- % Listener to changes within Session object
- SessionChangedListener event.listener
+ Session
end %properties
-
-
-
- %% Abstract Methods (subclass must implement these)
- methods (Abstract, Access = protected)
-
- % Creates a new session object for the app. It must return a
- % subclass of wt.model.BaseSession
- sessionObj = createNewSession(app)
+
+
+ % Accessors
+ methods
+ function set.Session(app,value)
+ mustBeScalarOrEmpty(value); % Single Session only
+ app.Session = value;
+ end
+
end %methods
-
- %% Public methods
+ %% Sealed Public methods
methods (Sealed)
- function newSession(app)
+ function close(app)
+ % Close the app
+
+ % Define arguments
+ arguments
+ app (1,1) wt.apps.BaseApp
+ end
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
+ % Prompt to save existing session
+ isCancelled = promptToSaveSession(app, app.Session);
+ if isCancelled
+ return
+ end %if
+
+ % Close the app
+ app.close_Internal();
+
+ end %function
+
+
+ function session = newSession(app)
% Start a new session
+
+ % Show output if Debug is on
+ app.displayDebugText();
- % If an existing session is dirty, give the user a chance to
- % save before loading another session
- if app.Dirty
- isCancelled = promptToSaveFirst(app);
- if isCancelled
- return;
- end
- end %if app.Dirty
+ % Prompt to save existing session
+ isCancelled = promptToSaveSession(app, app.Session);
+ if isCancelled
+ session = app.getEmptySession();
+ return
+ end %if
% Freeze the figure with a progress dialog
- dlg = uiprogressdlg(app.Figure);
- dlg.Title = "New Session";
- dlg.Indeterminate = true;
+ dlg = app.showIndeterminateProgress();
cleanupObj = onCleanup(@()delete(dlg));
% Instantiate the new session
- sessionObj = app.createNewSession();
- app.Session = sessionObj;
- app.updateTitle();
-
- % Force an update prior to the progress dialog closing
- drawnow
+ session = app.createNewSession();
+
+ % Store the session
+ % This also triggers app.update(), app.updateTitle()
+ app.Session = session;
end %function
- function sessionPath = saveSession(app, useSaveAs)
+ function sessionPath = saveSession(app, useSaveAs, session)
% Save the session to a file
% Define arguments
arguments
app (1,1) wt.apps.BaseApp
useSaveAs (1,1) logical = false
+ session wt.model.BaseSession = app.Session
end
-
- % We must have a session to save!
- if ~app.HasValidSession
- error("Session does not exist.");
- end
-
- % Get the session info
- sessionPath = app.Session.FilePath;
- sessionName = app.Session.FileName;
- lastFolder = app.LastFolder;
-
- % Does the session file already exist?
- fileExists = exist(sessionPath,"file");
- if ~fileExists
- % It doesn't exist - prompt with a default path
- useSaveAs = true;
- sessionPath = fullfile(lastFolder,sessionName);
- end
-
- % Prompt for "save as" if needed
- if useSaveAs
- sessionPath = app.promptToSaveAs(sessionPath);
- if ~isempty( sessionPath )
- app.Session.FilePath = sessionPath;
- end
- end
-
- % Save the file
- if strlength(sessionPath)
-
- % Freeze the figure with a progress dialog
- dlg = uiprogressdlg(app.Figure);
- dlg.Title = "Save Session";
- dlg.Message = sessionPath;
- dlg.Indeterminate = true;
- cleanupObj = onCleanup(@()delete(dlg));
-
- % Save the session
- app.Session.save();
- app.Session.FilePath = sessionPath;
- app.Session.Dirty = false;
- app.updateTitle();
-
- % Force an update prior to the progress dialog closing
- drawnow
-
- end %if strlength(sessionPath)
-
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
+ % Call superclass internal save method
+ app.saveSession_Internal(useSaveAs, session);
+
+ % Populate output
+ sessionPath = session.FilePath;
+
end %function
-
-
- function loadSession(app, sessionPath)
+
+
+ function session = loadSession(app, sessionPath)
% Load a session from a file
-
+
% Define arguments
arguments
app (1,1) wt.apps.BaseApp
sessionPath (1,1) string = ""
end
-
- % If an existing session is dirty, give the user a chance to
- % save before loading another session
- if app.Dirty
- isCancelled = promptToSaveFirst(app);
- if isCancelled
- return;
- end
- end %if app.Dirty
-
- % Unless a file was already specified, prompt to load
- if ~exist(sessionPath,"file")
- sessionPath = app.promptToLoad();
- end
-
- % Load the file
- if strlength(sessionPath)
-
- % Freeze the figure with a progress dialog
- dlg = uiprogressdlg(app.Figure);
- dlg.Title = "Load Session";
- dlg.Message = sessionPath;
- dlg.Indeterminate = true;
- cleanupObj = onCleanup(@()delete(dlg));
-
- % Load the session
- sessionObj = wt.model.BaseSession.open(sessionPath);
- sessionObj.FilePath = sessionPath;
-
- % Store the session - triggers app.update()
- app.Session = sessionObj;
-
- % Update the title
- app.updateTitle();
-
- % Force an update prior to the progress dialog closing
- drawnow
-
- end %if strlength(sessionPath)
-
- end %function
-
-
- function isCancelled = promptToSaveFirst(app)
- % Prompt the user to save a file
-
- % Default output
- isCancelled = false;
-
- % Prompt whether to save
- message = sprintf("Save changes to '%s'?",app.Session.FileName);
- title = "Load Session";
- selection = app.promptYesNoCancel(message, title);
-
- % If Yes, prompt to save the existing session first
- if selection == "Yes"
- sessionSavePath = app.saveSession();
- if ~strlength(sessionSavePath)
- isCancelled = true;
- end
- elseif selection == "Cancel"
- isCancelled = true;
- end
-
- end %function
-
-
- function close(app)
- % Triggered on figure closed
-
- % If an existing session is dirty, give the user a chance to
- % save before loading another session
- if app.Dirty
- isCancelled = promptToSaveFirst(app);
- if isCancelled
- return;
- end
- end %if app.Dirty
-
-
- % Freeze the figure with a progress dialog
- dlg = uiprogressdlg(app.Figure);
- dlg.Message = "Closing";
- dlg.Indeterminate = true;
-
- % Delete the app
- app.delete();
-
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
+ % Prompt to save existing session
+ isCancelled = promptToSaveSession(app, app.Session);
+ if isCancelled
+ session = app.getEmptySession();
+ return
+ end %if
+
+ % Call superclass internal load method
+ session = app.loadSession_Internal(sessionPath);
+
+ % Store the session
+ % This also triggers app.update(), app.updateTitle()
+ app.Session = session;
+
end %function
end %methods
@@ -246,6 +136,9 @@ function close(app)
function setup_internal(app)
% Preform internal pre-setup necessary
+
+ % Show output if Debug is on
+ app.displayDebugText();
% Instantiate initial session
app.Session = app.createNewSession();
@@ -255,7 +148,11 @@ function setup_internal(app)
function updateTitle(app)
% Update the app title, showing the session name and dirty flag
+
+ % Show output if Debug is on
+ app.displayDebugText();
+ % Decide on the figure title
if ~app.HasValidSession
app.Figure.Name = app.Name;
elseif app.Session.Dirty
@@ -267,65 +164,65 @@ function updateTitle(app)
end %function
end %methods
-
-
-
- %% Private Methods
- methods (Access = private)
-
- function onSessionChanged(app,~)
- % Triggered when a SetObservable property in the session has
- % changed. May be overridden for custom behavior using incoming
- % event data.
-
- % Trigger an update
- app.update();
-
- end %function
-
-
- function onSessionChanged_private(app,e)
-
- % Update the app title
- app.updateTitle();
-
- % Call the app's session changed method
- app.onSessionChanged(e);
-
- end %function
-
-
- function attachSessionListeners(app)
-
- app.SessionChangedListener = event.listener(app.Session,...
- 'PropertyChanged',@(h,e)onSessionChanged_private(app,e));
-
+
+
+
+ %% Display Customization
+ methods (Access = protected)
+
+ function propGroups = getPropertyGroups(app)
+ % Customize how the properties are displayed
+
+ import matlab.mixin.util.PropertyGroup
+
+ persistent pGroups
+ if isempty(pGroups)
+
+ % BaseApp properties
+ baseAppTitle = " ------ BaseApp Properties ------";
+ baseAppProperties = properties("wt.apps.BaseApp");
+ usedProps = baseAppProperties;
+
+ % BaseSingleSessionApp Properties
+ sessionTitle = " ------ BaseSingleSessionApp Properties ------";
+ sessionProperties = setdiff(...
+ properties("wt.apps.BaseSingleSessionApp"), usedProps);
+ usedProps = [baseAppProperties; sessionProperties];
+
+ % Get properties for concrete class
+ mc = metaclass(app);
+ propInfo = mc.PropertyList;
+
+ % Filter out used properties
+ [~,idxA] = setdiff({propInfo.Name}, usedProps, "stable");
+ propInfo = propInfo(idxA);
+
+ % Split out read-only properties
+ getInfo = {propInfo.GetAccess};
+ setInfo = {propInfo.SetAccess};
+ isPublicGet = cellfun(@(x)isequal(x,'public'), getInfo);
+ isPublicSet = cellfun(@(x)isequal(x,'public'), setInfo);
+ concPublicSetProps = {propInfo(isPublicGet & isPublicSet).Name};
+ concProtectedSetProps = {propInfo(isPublicGet & ~isPublicSet).Name};
+
+ % Set titles
+ concPublicTitle = " ------ " + app.Name + " Public Properties ------";
+ concProtectedTitle = " ------ " + app.Name + " Read-Only Properties ------";
+
+ pGroups = [
+ PropertyGroup(concProtectedSetProps, concProtectedTitle)
+ PropertyGroup(concPublicSetProps, concPublicTitle)
+ PropertyGroup(baseAppProperties, baseAppTitle)
+ PropertyGroup(sessionProperties, sessionTitle)
+ ];
+
+ end %if
+
+ propGroups = pGroups;
+
end %function
-
+
end %methods
-
-
-
- %% Accessors
- methods
-
- function set.Session(app,value)
- app.Session = value;
- if app.SetupComplete
- app.update();
- end
- app.attachSessionListeners();
- end
-
- function value = get.HasValidSession(app)
- value = ~isempty(app.Session) && isvalid(app.Session);
- end
-
- function value = get.Dirty(app)
- value = app.HasValidSession && app.Session.Dirty;
- end
-
- end %methods
-
-
-end %classdef
+
+
+end %classdef
\ No newline at end of file
diff --git a/widgets/+wt/+enum/ArrayRestriction.m b/widgets/+wt/+enum/ArrayRestriction.m
new file mode 100644
index 0000000..62091b6
--- /dev/null
+++ b/widgets/+wt/+enum/ArrayRestriction.m
@@ -0,0 +1,15 @@
+classdef ArrayRestriction
+ %ARRAYRESTRICTION Represent array restriction selections
+ % Enumerates a list of choices
+
+ % Copyright 2024 The MathWorks Inc.
+
+
+ %% Enumerations
+ enumeration
+ none
+ increasing
+ decreasing
+ end %enumeration
+
+end %classdef
\ No newline at end of file
diff --git a/widgets/+wt/+eventdata/ListManagerEventData.m b/widgets/+wt/+eventdata/ListManagerEventData.m
new file mode 100644
index 0000000..511ed2c
--- /dev/null
+++ b/widgets/+wt/+eventdata/ListManagerEventData.m
@@ -0,0 +1,14 @@
+classdef ListManagerEventData < event.EventData
+ % Event data for list manager change events
+
+ % Copyright 2024 The MathWorks, Inc.
+
+ %% Properties
+ properties
+ Action (1,1) string
+ Item (1,1) string
+ ItemData
+ Index double {mustBeInteger, mustBeNonnegative, mustBeScalarOrEmpty}
+ end %properties
+
+end % classdef
\ No newline at end of file
diff --git a/widgets/+wt/+eventdata/ModelChangedData.m b/widgets/+wt/+eventdata/ModelChangedData.m
new file mode 100644
index 0000000..08df238
--- /dev/null
+++ b/widgets/+wt/+eventdata/ModelChangedData.m
@@ -0,0 +1,15 @@
+classdef ModelChangedData < event.EventData
+ % Event data for model changes
+
+ % Copyright 2024 The MathWorks, Inc.
+
+ %% Properties
+ properties %(SetAccess = ?wt.model.BaseModel)
+ Model
+ Property string {mustBeScalarOrEmpty}
+ Value
+ Stack (1,:) cell
+ ClassStack (1,:) string
+ end %properties
+
+end % classdef
\ No newline at end of file
diff --git a/widgets/+wt/+eventdata/ModelSetData.m b/widgets/+wt/+eventdata/ModelSetData.m
new file mode 100644
index 0000000..38edbe0
--- /dev/null
+++ b/widgets/+wt/+eventdata/ModelSetData.m
@@ -0,0 +1,12 @@
+classdef ModelSetData < event.EventData
+ % Event data for model set
+
+ % Copyright 2024 The MathWorks, Inc.
+
+ %% Properties
+ properties
+ Model
+ Controller
+ end %properties
+
+end % classdef
\ No newline at end of file
diff --git a/widgets/+wt/+eventdata/PropertyChangedData.m b/widgets/+wt/+eventdata/PropertyChangedData.m
index b83d1ef..1a3e0a6 100644
--- a/widgets/+wt/+eventdata/PropertyChangedData.m
+++ b/widgets/+wt/+eventdata/PropertyChangedData.m
@@ -7,7 +7,7 @@
% obj = wt.eventdata.PropertyChangedData(...,'p1',v1,...)
%
- % Copyright 2020-2021 The MathWorks, Inc.
+ % Copyright 2020-2024 The MathWorks, Inc.
%% Properties
properties (SetAccess = protected)
@@ -23,33 +23,40 @@
% Set the changed property name
obj.Property = propName;
-
+
% Is input a MATLAB eventdata?
if isa(newValue,'matlab.ui.eventdata.ValueChangedData')
+
obj.Value = newValue.Value;
obj.PreviousValue = newValue.PreviousValue;
+
elseif isa(newValue,'matlab.ui.eventdata.ValueChangingData')
+
obj.Value = newValue.Value;
+
else
+
% No - use the value directly
obj.Value = newValue;
- end
-
- % Was a previous value provided?
- if mod(numel(varargin),2)
- obj.PreviousValue = varargin{1};
- varargin(1) = [];
- end
-
+
+ % Was a previous value provided?
+ if mod(numel(varargin),2)
+ obj.PreviousValue = varargin{1};
+ varargin(1) = [];
+ end
+
+ end %if
+
% Any remaining varargin are dynamic property-value pairs
- for idx=1:numel(varargin)
+ for idx=1:2:numel(varargin)
thisProp = varargin{idx};
- thisValue = remArgs.(thisProp);
+ thisValue = varargin{idx+1};
obj.addprop(thisProp);
obj.(thisProp) = thisValue;
end
end %constructor
+
end %methods
end % classdef
\ No newline at end of file
diff --git a/widgets/+wt/+eventdata/RowEntriesTableChangedData.m b/widgets/+wt/+eventdata/RowEntriesTableChangedData.m
new file mode 100644
index 0000000..91157b4
--- /dev/null
+++ b/widgets/+wt/+eventdata/RowEntriesTableChangedData.m
@@ -0,0 +1,19 @@
+classdef RowEntriesTableChangedData < event.EventData
+ % Event data for table changed events
+
+ % Copyright 2024 The MathWorks, Inc.
+
+ %% Properties
+ properties
+ Action (1,1) string
+ Row double
+ Column double
+ EditValue string {mustBeScalarOrEmpty}
+ Value
+ PreviousValue
+ TableData
+ PreviousTableData
+ Error
+ end %properties
+
+end % classdef
\ No newline at end of file
diff --git a/widgets/+wt/+eventdata/SliderCheckboxChangedData.m b/widgets/+wt/+eventdata/SliderCheckboxChangedData.m
index f2427e3..02b66de 100644
--- a/widgets/+wt/+eventdata/SliderCheckboxChangedData.m
+++ b/widgets/+wt/+eventdata/SliderCheckboxChangedData.m
@@ -5,7 +5,7 @@
% obj = wt.eventdata.SliderCheckboxChangedData(name,index,prop,state,value)
%
- % Copyright 2020-2021 The MathWorks, Inc.
+ % Copyright 2020-2024 The MathWorks, Inc.
%% Properties
properties (SetAccess = protected)
diff --git a/widgets/+wt/+eventdata/TreeModelSingleSelectionData.m b/widgets/+wt/+eventdata/TreeModelSingleSelectionData.m
new file mode 100644
index 0000000..8604e06
--- /dev/null
+++ b/widgets/+wt/+eventdata/TreeModelSingleSelectionData.m
@@ -0,0 +1,90 @@
+classdef TreeModelSingleSelectionData < event.EventData
+ % Event data for tree model selection for a single-node selection tree
+
+ % Copyright 2024 The MathWorks, Inc.
+
+
+ %% Properties
+ properties
+ Node matlab.ui.container.TreeNode {mustBeScalarOrEmpty}
+ end %properties
+
+ properties (SetAccess = protected)
+ NodeTag (1,1) string = ""
+ SelectionPath (1,:) matlab.ui.container.TreeNode
+ SelectionPathTag (1,:) string
+ end %properties
+
+ properties
+ Model wt.model.BaseModel {mustBeScalarOrEmpty}
+ Session wt.model.BaseSession {mustBeScalarOrEmpty}
+ end %properties
+
+
+ % Accessors
+ methods
+ function set.Node(obj, value)
+ obj.Node = value;
+ obj.findNodeTag(value);
+ obj.findSelectionPath(value);
+ end
+ end
+
+
+ %% Dependent Properties
+ properties (Dependent, SetAccess = protected)
+ ModelClass (1,1) string
+ ModelType (1,1) string
+ end %properties
+
+ % Accessors
+ methods
+ function value = get.ModelClass(obj)
+ value = string(class(obj.Model));
+ end
+ function value = get.ModelType(obj)
+ value = extract(obj.ModelClass, alphanumericsPattern + textBoundary);
+ end
+ end
+
+
+ %% Methods
+ methods (Access = private)
+
+ function findNodeTag(obj, node)
+ % Populates the tag of selected node(s)
+
+ if isempty(node)
+ obj.NodeTag = "";
+ else
+ obj.NodeTag = string(node.Tag);
+ end
+
+ end %function
+
+
+ function pathOut = findSelectionPath(obj, pathIn)
+ % Find the full selection path from a given node
+
+ if isempty(pathIn) || ~isa(pathIn, "matlab.ui.container.TreeNode")
+ pathOut = matlab.ui.container.TreeNode.empty(1,0);
+ elseif isa(pathIn.Parent, "matlab.ui.container.TreeNode")
+ pathOut = horzcat(obj.findSelectionPath(pathIn.Parent), pathIn);
+ else
+ pathOut = pathIn;
+ end
+
+ if ~nargout
+ obj.SelectionPath = pathOut;
+ if isempty(pathOut)
+ obj.SelectionPathTag = string.empty(1,0);
+ else
+ obj.SelectionPathTag = string({pathOut.Tag}');
+ end
+ end
+
+ end %function
+
+ end %methods
+
+end % classdef
\ No newline at end of file
diff --git a/widgets/+wt/+eventdata/ValueChangedData.m b/widgets/+wt/+eventdata/ValueChangedData.m
index ed8d763..341fd2a 100644
--- a/widgets/+wt/+eventdata/ValueChangedData.m
+++ b/widgets/+wt/+eventdata/ValueChangedData.m
@@ -7,7 +7,7 @@
% obj = wt.eventdata.ValueChangedData(...,'p1',v1,...)
%
- % Copyright 2020-2021 The MathWorks, Inc.
+ % Copyright 2020-2024 The MathWorks, Inc.
%% Properties
properties (SetAccess = protected)
@@ -18,26 +18,41 @@
%% Constructor / destructor
methods
- function obj = ValueChangedData(newValue, previousValue)
-
- arguments
- newValue
- previousValue = []
- end
-
+ function obj = ValueChangedData(newValue,varargin)
+
% Is input a MATLAB eventdata?
if isa(newValue,'matlab.ui.eventdata.ValueChangedData')
+
obj.Value = newValue.Value;
obj.PreviousValue = newValue.PreviousValue;
+
elseif isa(newValue,'matlab.ui.eventdata.ValueChangingData')
+
obj.Value = newValue.Value;
+
else
+
% No - use the value directly
obj.Value = newValue;
- obj.PreviousValue = previousValue;
+
+ % Was a previous value provided?
+ if mod(numel(varargin),2)
+ obj.PreviousValue = varargin{1};
+ varargin(1) = [];
+ end
+
+ end %if
+
+ % Any remaining varargin are dynamic property-value pairs
+ for idx=1:2:numel(varargin)
+ thisProp = varargin{idx};
+ thisValue = varargin{idx+1};
+ obj.addprop(thisProp);
+ obj.(thisProp) = thisValue;
end
-
+
end %constructor
+
end %methods
end % classdef
\ No newline at end of file
diff --git a/widgets/+wt/+mixin/ButtonColorable.m b/widgets/+wt/+mixin/ButtonColorable.m
index 21e1463..0a31b64 100644
--- a/widgets/+wt/+mixin/ButtonColorable.m
+++ b/widgets/+wt/+mixin/ButtonColorable.m
@@ -8,7 +8,7 @@
properties (AbortSet)
% Button Color
- ButtonColor (1,3) double {wt.validators.mustBeBetweenZeroAndOne} = [1 1 1] * 0.96
+ ButtonColor (1,3) double {mustBeInRange(ButtonColor,0,1)} = [1 1 1] * 0.96
end %properties
diff --git a/widgets/+wt/+mixin/DisplayNonScalarObjectAsTable.m b/widgets/+wt/+mixin/DisplayNonScalarObjectAsTable.m
index 5c15c4e..e156bc6 100644
--- a/widgets/+wt/+mixin/DisplayNonScalarObjectAsTable.m
+++ b/widgets/+wt/+mixin/DisplayNonScalarObjectAsTable.m
@@ -5,13 +5,7 @@
% display an array of objects as a table.
%
- % Copyright 2018-2019 The MathWorks, Inc.
- %
- % Auth/Revision:
- % MathWorks Consulting
- % $Author: rjackey $
- % $Revision: 348 $ $Date: 2018-03-02 15:51:54 -0500 (Fri, 02 Mar 2018) $
- % ---------------------------------------------------------------------
+ % Copyright 2018-2024 The MathWorks, Inc.
%% Public Methods
@@ -80,7 +74,7 @@ function displayNonScalarObject(obj)
% Show the group list in a table
disp( obj.toDisplayTable() );
- if isa(obj,'handle') && any(~isvalid(obj))
+ if isa(obj,'handle') && any(~isvalid(obj(:)))
fprintf(' Object array contains deleted handles.\n');
end
diff --git a/widgets/+wt/+mixin/ErrorHandling.m b/widgets/+wt/+mixin/ErrorHandling.m
index 93e023f..5e8600d 100644
--- a/widgets/+wt/+mixin/ErrorHandling.m
+++ b/widgets/+wt/+mixin/ErrorHandling.m
@@ -12,7 +12,7 @@ function throwError(obj,err,title)
arguments
obj (1,1) wt.mixin.ErrorHandling
err % string or MException
- title (1,1) string = "Error"
+ title (1,1) string = "Internal Error in " + class(obj)
end
% Prepare the message
@@ -25,7 +25,7 @@ function throwError(obj,err,title)
% Locate ancestor figure
if isprop(obj,"Figure")
- fig = obj.Figure;
+ fig = obj.Figure; %#ok
else
fig = ancestor(obj,'figure');
end
@@ -55,7 +55,7 @@ function throwError(obj,err,title)
% Locate ancestor figure
if isprop(obj,"Figure")
- fig = obj.Figure;
+ fig = obj.Figure; %#ok
else
fig = ancestor(obj,'figure');
end
@@ -76,12 +76,61 @@ function throwError(obj,err,title)
function dlg = showIndeterminateProgress(obj,title,message,cancelOn)
% Places an indeterminate progress dialog in the widget's figure
+ % Validate arguments
+ arguments
+ obj (1,1) wt.mixin.ErrorHandling
+ title (1,1) string = "Please Wait"
+ message (1,1) string = ""
+ cancelOn (1,1) logical = false
+ end
+
dlg = showProgress(obj,title,message,cancelOn);
dlg.Indeterminate = true;
end %function
- end%methods
+
+ function result = promptForConfirmation(obj,message,title,buttonNames)
+ % Places an indeterminate progress dialog in the widget's figure
+
+ % Validate arguments
+ arguments (Input)
+ obj (1,1) wt.mixin.ErrorHandling
+ message (1,1) string = "Are you sure?"
+ title (1,1) string = ""
+ buttonNames (1,2) string = ["Yes","Cancel"]
+ end
+
+ arguments (Output)
+ result (1,1) logical
+ end
+
+ % Locate ancestor figure
+ if isprop(obj,"Figure")
+ fig = obj.Figure; %#ok
+ else
+ fig = ancestor(obj,'figure');
+ end
+
+ % Place in a dialog if possible
+ if isempty(fig)
+
+ id = "wt:mixin:ErrorHandling:NoFigure";
+ msg = "No figure is present to place the dialog.";
+ warning(id,msg);
+ result = false;
+
+ else
+
+ selection = uiconfirm(fig, message, title,...
+ "Options", buttonNames, "DefaultOption", 2);
+ result = matches(buttonNames(1), selection);
+
+ end
+
+ end %function
+
+ end %methods
end %classdef
diff --git a/widgets/+wt/+mixin/FieldColorable.m b/widgets/+wt/+mixin/FieldColorable.m
index e94ce97..5bc67ed 100644
--- a/widgets/+wt/+mixin/FieldColorable.m
+++ b/widgets/+wt/+mixin/FieldColorable.m
@@ -8,7 +8,7 @@
properties (AbortSet)
% Field Color
- FieldColor (1,3) double {wt.validators.mustBeBetweenZeroAndOne} = [1 1 1]
+ FieldColor (1,3) double {mustBeInRange(FieldColor,0,1)} = [1 1 1]
end %properties
diff --git a/widgets/+wt/+mixin/FontColorable.m b/widgets/+wt/+mixin/FontColorable.m
index df51f0e..ac95146 100644
--- a/widgets/+wt/+mixin/FontColorable.m
+++ b/widgets/+wt/+mixin/FontColorable.m
@@ -8,7 +8,7 @@
properties (AbortSet)
% Font color
- FontColor (1,3) double {wt.validators.mustBeBetweenZeroAndOne} = [0 0 0]
+ FontColor (1,3) double {mustBeInRange(FontColor,0,1)} = [0 0 0]
end %properties
diff --git a/widgets/+wt/+mixin/FontStyled.m b/widgets/+wt/+mixin/FontStyled.m
index abe3235..4606209 100644
--- a/widgets/+wt/+mixin/FontStyled.m
+++ b/widgets/+wt/+mixin/FontStyled.m
@@ -20,7 +20,7 @@
FontAngle {mustBeMember(FontAngle,{'normal','italic'})} = 'normal'
% Font color
- FontColor (1,3) double {wt.validators.mustBeBetweenZeroAndOne} = [0 0 0]
+ FontColor (1,3) double {mustBeInRange(FontColor,0,1)} = [0 0 0]
end %properties
diff --git a/widgets/+wt/+mixin/ModelObserver.m b/widgets/+wt/+mixin/ModelObserver.m
new file mode 100644
index 0000000..9521624
--- /dev/null
+++ b/widgets/+wt/+mixin/ModelObserver.m
@@ -0,0 +1,162 @@
+classdef (Abstract, AllowedSubclasses = ...
+ {?wt.abstract.BaseViewController, ?wt.abstract.BaseViewChart})...
+ ModelObserver < handle
+ % Mixin for components using a model that observe changes
+
+
+ %% Abstract Properties
+ % Subclass must define these
+ properties (Abstract, AbortSet, SetObservable)
+
+ % Model class containing data to display in the pane
+ Model wt.model.BaseModel
+
+ end %properties
+
+
+ %% Events
+ events (NotifyAccess = protected)
+
+ % Triggered when the Model has changed
+ ModelSet
+
+ % Triggered when a property within the model has changed
+ ModelChanged
+
+ end %events
+
+
+ %% Internal Properties
+ properties (SetAccess = protected)
+
+ % Listener for a new model being attached
+ ModelSetListener event.listener
+
+ % Listener for property changes within the model
+ ModelChangedListener event.listener
+
+ end %properties
+
+
+ %% Constructor
+ methods
+ function obj = ModelObserver()
+ % Constructor
+
+ % Listen to Model property being set
+ obj.ModelSetListener = listener(obj,"Model","PostSet",@(~,~)onModelSet(obj));
+
+ % Listen to model changes
+ obj.attachModelListeners();
+
+ end %function
+ end %methods
+
+
+
+ %% Protected Methods
+ methods (Access = protected)
+
+ function onModelSet(obj)
+ % Triggered when Model has been changed
+
+ % Listen to model property changes
+ obj.attachModelListeners();
+
+ % Prepare event data
+ evtOut = wt.eventdata.ModelSetData();
+ evtOut.Model = obj.Model;
+ evtOut.Controller = obj;
+
+ % Notify listeners
+ notify(obj,"ModelSet",evtOut)
+
+ end %function
+
+
+ function attachModelListeners(obj)
+ % Triggered when Model has been changed
+
+ % Listen to model property changes
+ obj.ModelChangedListener = listener(obj.Model,...
+ "ModelChanged", @(~,evt)onModelChanged(obj,evt));
+
+ end %function
+
+
+ function onModelChanged(obj,evt)
+ % Triggered when a property within the model has changed
+
+ arguments
+ obj (1,1) wt.mixin.ModelObserver
+ evt (1,1) wt.eventdata.ModelChangedData
+ end
+
+ % Prepare eventdata
+ evtOut = wt.eventdata.ModelChangedData;
+ evtOut.Model = evt.Model;
+ evtOut.Property = evt.Property;
+ evtOut.Value = evt.Value;
+ evtOut.Stack = [{obj}, evt.Stack];
+ evtOut.ClassStack = [class(obj), evt.ClassStack];
+
+ % Notify listeners
+ notify(obj,"ModelChanged",evtOut)
+
+ end %function
+
+
+ function className = getModelClassName(obj)
+ % Returns the class name of the Model property contents
+
+ arguments (Input)
+ obj (1,1) wt.mixin.ModelObserver
+ end
+
+ arguments (Output)
+ className (1,1) string
+ end
+
+ % Get the class of the Model array
+ className = string( class( obj.Model ) );
+
+ end %function
+
+
+ function newModel = constructDefaultModel(obj)
+ % Generates a scalar object instance of the class name used in
+ % the Model property. The model must be instantiated with no
+ % input arguments
+
+ arguments
+ obj (1,1) wt.mixin.ModelObserver
+ end
+
+ % Construct a new object
+ className = getModelClassName(obj);
+ fcnConstruct = str2func(className);
+ newModel = fcnConstruct();
+
+ end %function
+
+
+ function [model, validToDisplay] = getScalarModelToDisplay(obj)
+
+ % Get a single instance of the correct model type and indicate
+ % if it's found and valid. This is useful in case obj.Model is
+ % empty, it will still return a scalar instance to show default
+ % values and the modelValid flag will be false, indicating to
+ % disable the fields.
+ model = obj.Model;
+ validToDisplay = isscalar(model) && isvalid(model);
+ if ~validToDisplay
+ model = obj.constructDefaultModel();
+ end
+ validToDisplay = matlab.lang.OnOffSwitchState(validToDisplay);
+
+ end %function
+
+ end %methods
+
+
+end %classdef
\ No newline at end of file
diff --git a/widgets/+wt/+mixin/Orderable.m b/widgets/+wt/+mixin/Orderable.m
new file mode 100644
index 0000000..188f980
--- /dev/null
+++ b/widgets/+wt/+mixin/Orderable.m
@@ -0,0 +1,138 @@
+classdef (HandleCompatible) Orderable
+ % Implements functionality for orderable lists
+
+
+ %% Internal Static methods
+ methods (Static, Access = protected)
+
+ function [idxNew, idxSelAfter] = shiftListIndices(shift, numItems, idxSel)
+ % Shift the selected indices up/down within a list
+
+ % Define arguments
+ arguments (Input)
+ % Shift amount and direction (typically 1 or -1)
+ shift (1,1) double {mustBeInteger}
+
+ % Total number of items in the list
+ numItems (1,1) double {mustBeInteger, mustBeNonnegative}
+
+ % Selected indices to move
+ idxSel (1,:) double {mustBeInteger, mustBePositive, mustBeLessThanOrEqual(idxSel,numItems)}
+ end
+
+ arguments (Output)
+ % Indices of the complete list after re-ordering
+ idxNew (1,:) double {mustBeInteger, mustBePositive}
+
+ % Indices where the selected data end up after the move
+ idxSelAfter (1,:) double {mustBeInteger, mustBePositive}
+ end
+
+ % Make indices to all items as they are now
+ idxNew = 1:numItems;
+
+ % Allocate the final indices
+ idxSelAfter = idxSel;
+
+ % Find the last stable item that doesn't move
+ [~,idxStable] = setdiff(idxNew, idxSel, 'stable');
+ if ~isempty(idxStable)
+ idxFirstStable = idxStable(1);
+ idxLastStable = idxStable(end);
+ else
+ idxFirstStable = inf;
+ idxLastStable = 0;
+ end
+
+ % Which way do we loop?
+ if shift > 0 %Shift to end
+
+ for idxToMove = numel(idxSel):-1:1
+
+ % Calculate if there's room to move this item
+ idxThisBefore = idxSel(idxToMove);
+ thisShift = max( min(idxLastStable-idxThisBefore, shift), 0 );
+
+ % Where does this item move from/to
+ idxThisAfter = idxThisBefore + thisShift;
+ idxSelAfter(idxToMove) = idxThisAfter;
+
+ % Where do other items move from/to
+ idxOthersBefore = idxSel(idxToMove)+1:1:idxThisAfter;
+ idxOthersAfter = idxOthersBefore - thisShift;
+
+ % Move the items
+ idxNew([idxThisAfter idxOthersAfter]) = idxNew([idxThisBefore idxOthersBefore]);
+
+ end
+
+ elseif shift < 0 %Shift to start
+
+ for idxToMove = 1:numel(idxSel)
+
+ % Calculate if there's room to move this item
+ idxThisBefore = idxSel(idxToMove);
+ thisShift = min( max(idxFirstStable-idxThisBefore, shift), 0 );
+
+ % Where does this item move from/to
+ idxThisAfter = idxThisBefore + thisShift;
+ idxSelAfter(idxToMove) = idxThisAfter;
+
+ % Where do other items move from/to
+ idxOthersBefore = idxThisAfter:1:idxSel(idxToMove)-1;
+ idxOthersAfter = idxOthersBefore - thisShift;
+
+ % Move the items
+ idxNew([idxThisAfter idxOthersAfter]) = idxNew([idxThisBefore idxOthersBefore]);
+
+ end
+
+ end %if shift > 0
+
+ end %function
+
+
+ function [backEnabled, fwdEnabled] = areOrderButtonsEnabled(numItems, idxSel, allowSortItem)
+ % Determine whether back/forward (down/up) buttons should be
+ % enabled or not given the selection index
+
+ % Define arguments
+ arguments (Input)
+ % Total number of items in the list
+ numItems (1,1) double {mustBeInteger, mustBeNonnegative}
+
+ % Selected indices
+ idxSel (1,:) double {mustBeInteger, mustBePositive, mustBeLessThanOrEqual(idxSel,numItems)}
+
+ % Indicates whether each individual item may be sorted
+ allowSortItem (1,:) logical = true(1, numItems);
+ end
+
+ arguments (Output)
+ % Should back (up) button be enabled?
+ backEnabled (1,1) logical
+
+ % Should forward (down) button be enabled?
+ fwdEnabled (1,1) logical
+ end
+
+ % How many items selected?
+ numSel = numel(idxSel);
+
+ % Only sortable items selected
+ selIsSortable = (numSel > 0) && all( allowSortItem(idxSel) );
+
+ % Enable back (up)?
+ idxMinSortable = find(allowSortItem,1,"first");
+ backEnabled = selIsSortable && (max(idxSel) > (numSel + idxMinSortable - 1) );
+
+ % Enable forward (down)?
+ idxMaxSortable = find(allowSortItem,1,"last");
+ fwdEnabled = selIsSortable && (min(idxSel) <= (idxMaxSortable - numSel));
+
+ end %function
+
+ end %methods
+
+
+end %classdef
\ No newline at end of file
diff --git a/widgets/+wt/+mixin/TitleColorable.m b/widgets/+wt/+mixin/TitleColorable.m
index 76d7001..9fa8e68 100644
--- a/widgets/+wt/+mixin/TitleColorable.m
+++ b/widgets/+wt/+mixin/TitleColorable.m
@@ -8,7 +8,7 @@
properties (AbortSet)
% Title color
- TitleColor (1,3) double {wt.validators.mustBeBetweenZeroAndOne} = [0 0 0]
+ TitleColor (1,3) double {mustBeInRange(TitleColor,0,1)} = [0 0 0]
end %properties
diff --git a/widgets/+wt/+model/BaseModel.m b/widgets/+wt/+model/BaseModel.m
index 9efc058..39af8b2 100644
--- a/widgets/+wt/+model/BaseModel.m
+++ b/widgets/+wt/+model/BaseModel.m
@@ -1,98 +1,269 @@
-classdef (Abstract) BaseModel < handle & ...
+classdef BaseModel < handle & ...
matlab.mixin.SetGetExactNames & ...
+ matlab.mixin.Copyable & ...
wt.mixin.DisplayNonScalarObjectAsTable
% Base model class for apps that provides:
% PV pairs assignment on construction
+ %
% Display nonscalar arrays in table format
+ %
% Public PropertyChanged event for properties tagged as SetObservable
% to enable apps/widgets to listen to model changes
- %
+ %
+ % Public ModelChanged event for recursive property change
+ % notifications in a hierarchy of BaseWidget classes
+ %
+
+ % Copyright 2020-2024 The MathWorks, Inc.
+
- % Copyright 2020-2023 The MathWorks, Inc.
-
-
%% Events
events
-
+
% Triggered when SetObservable properties are changed
- PropertyChanged
-
+ PropertyChanged
+
+ % Triggered when an aggregated / nested model has changed
+ ModelChanged
+
end %events
-
-
-
+
+
+ %% Inputs - Public Properties
+ properties (AbortSet, SetObservable)
+
+ % Name of this item
+ Name (1,1) string = ""
+
+ end %properties
+
+
+ % Accessors
+ methods
+
+ function value = get.Name(obj)
+ % If a name has been specified, use it. Else, the concrete
+ % class may customize what the default name should be.
+ if strlength(obj.Name) > 0
+ value = obj.Name;
+ else
+ value = obj.getDefaultName();
+ end
+ end %function
+
+ end %methods
+
+
+
%% Internal Properties
+ properties (Transient, Hidden)
+
+ % Toggle true in each instance to enable debugging display
+ Debug (1,1) logical = false
+
+ end %properties
+
+
properties (Transient, NonCopyable, Hidden, SetAccess = private)
-
+
% Listeners to public properties
PropListeners
-
+
+ end %properties
+
+
+ properties (Transient, NonCopyable, Access = private)
+
+ % Listeners to any aggregated / nested handle class models
+ AggregatedModelListeners
+
end %properties
-
-
-
+
+
+ properties (Hidden, AbortSet)
+ % These are used internally and functionality may change in the
+ % future
+
+ % Enables change listeners
+ EnableChangeListeners (1,1) logical = true
+
+ % Enables nested model listeners to trigger onModelChanged event
+ EnableAggregatedModelListeners (1,1) logical = true
+
+ end %properties
+
+
+ % Accessors
+ methods
+ function set.EnableAggregatedModelListeners(obj,value)
+ obj.EnableAggregatedModelListeners = value;
+ if value
+ obj.attachModelListeners()
+ else
+ obj.clearModelListeners()
+ end
+ end
+ end %methods
+
+
%% Constructor
methods
function obj = BaseModel(varargin)
% Constructor
-
+
% Populate public properties from P-V input pairs
if nargin
obj.assignPVPairs(varargin{:});
end
-
+
% Create listeners to public properties
obj.createPropListeners();
-
- end %function obj = ObjectModel(varargin)
+
+ % Create listeners to aggregated model classes that may have
+ % property change events
+ for idx = 1:numel(obj)
+ if obj(idx).EnableAggregatedModelListeners
+ obj(idx).attachModelListeners();
+ end
+ end
+
+ end %function
end %methods
-
-
-
+
+
+
%% Static methods
methods (Static)
-
+
function obj = loadobj(obj)
% Customize loading from file
-
+
if isstruct(obj)
error('Unable to load object.');
end
-
+
% Need to recreate listeners
obj.createPropListeners();
-
+
+ for idx = 1:numel(obj)
+ if obj(idx).EnableAggregatedModelListeners
+ obj(idx).attachModelListeners();
+ end
+ end
+
end %function
-
+
end %methods
-
-
-
+
+
+
+ %% Public methods
+ methods
+
+ function debugAggregatedModels(obj, value)
+ % Recursively set debug on aggregated models in the hierarchy
+
+ arguments
+ obj (1,1) wt.model.BaseModel
+ value (1,1) logical = true;
+ end
+
+ % Debug this model
+ obj.Debug = value;
+
+ % Loop on aggregated models and set debug
+ aggProps = obj.getAggregatedModelProperties();
+ for thisProp = aggProps
+ thisModel = obj.(thisProp);
+ if ~isempty(thisModel) && all(isa(thisModel,"handle"))
+ thisModel(~isvalid(thisModel)) = [];
+ for idx = 1:numel(thisModel)
+ thisModel(idx).debugAggregatedModels(value);
+ end
+ end
+ end
+
+ end %function
+
+ end %methods
+
+
+
%% Protected Methods
-
- methods (Access=protected)
+ methods (Access = protected)
+
+ function name = getDefaultName(~)
+ % Defines what the default name should be. A subclass may
+ % override this to customize the default name.
+
+ name = "";
+
+ end %function
- % This method is similar to
- % matlab.io.internal.mixin.HasPropertiesAsNVPairs, but this one
- % generally performs faster.
+
+ function props = getAggregatedModelProperties(~)
+ % Returns a list of aggregated model property names
+ %
+ % This overridable method lists properties containing
+ % aggregated BaseModel classes to listen for hierarchical
+ % ModelChanged events. Use this cautiously if model class
+ % references are used repeatedly. The intended purpose is to
+ % pass notifications up the hierarchy to the top level, so the
+ % session can be marked dirty.
+
+ arguments(Output)
+ props (1,:) string
+ end
+
+ props = string.empty(1,0);
+ end %function
+
+
+ function cpObj = copyElement(obj)
+ % Override copyElement method
+
+ if obj.Debug
+ disp("wt.model.BaseModel.copyElement " + class(obj));
+ end
+
+ % Call superclass method
+ cpObj = copyElement@matlab.mixin.Copyable(obj);
+
+ % Perform deep copy of any aggregated handle properties
+ props = obj.getAggregatedModelProperties();
+ for thisProp = props
+ cpObj.(thisProp) = copy( obj.(thisProp) );
+ end
+
+ % Create listeners
+ cpObj.createPropListeners();
+ cpObj.attachModelListeners();
+
+ end %function
+
+
function varargout = assignPVPairs(obj,varargin)
% Assign the specified property-value pairs
-
+
+ % This method is similar to
+ % matlab.io.internal.mixin.HasPropertiesAsNVPairs, but this one
+ % generally performs faster.
+
if nargin > 1
-
+
% Get a singleton parser for this class
keepUnmatched = nargout > 0;
p = getParser(obj, keepUnmatched);
-
+
% Parse the P-V pairs
p.parse(varargin{:});
-
+
% Set just the parameters the user passed in
- ParamNamesToSet = varargin(1:2:end);
+ ParamNamesToSet = p.Parameters;
ParamsInResults = fieldnames(p.Results);
-
+
% Assign properties
for ThisName = ParamNamesToSet
isSettable = any(strcmpi(ThisName,ParamsInResults));
@@ -100,66 +271,223 @@
obj.(ThisName{1}) = p.Results.(ThisName{1});
end
end
-
+
% Return unmatched pairs
if nargout
varargout{1} = p.Unmatched;
end
-
+
elseif nargout
-
+
varargout{1} = struct;
-
+
end %if nargin > 1
-
+
end %function
-
-
+
+
function createPropListeners(obj)
-
+ % Create listeners to SetObservable properties in this class
+
+ if obj.Debug
+ disp("wt.model.BaseModel.createPropListeners " + class(obj));
+ end
+
+ % Loop on each instance (typically scalar though)
for idx = 1:numel(obj)
- mc = metaclass(obj(idx));
+
+ % Get one instance
+ thisObj = obj(idx);
+
+ % Which properties are observable?
+ mc = metaclass(thisObj);
isObservable = [mc.PropertyList.SetObservable];
- props = mc.PropertyList(isObservable);
- obj(idx).PropListeners = event.proplistener(obj(idx),props,...
- 'PostSet',@(h,e)onPropChanged(obj(idx),e) );
+ propInfo = mc.PropertyList(isObservable);
+
+ % Attach listeners to observable properties
+ thisObj.PropListeners = event.proplistener(thisObj, propInfo,...
+ 'PostSet',@(~,e)onPropChanged(thisObj,e) );
+
end %for
-
+
end %function
-
-
- function onPropChanged(obj,e)
-
- evt = wt.eventdata.PropertyChangedData(e.Source.Name, obj.(e.Source.Name));
- obj.notify('PropertyChanged',evt)
-
+
+
+ function onPropChanged(obj,evt)
+
+ arguments
+ obj (1,1) wt.model.BaseModel
+ evt (1,1) event.PropertyEvent
+ end
+
+ if obj.Debug
+ disp("wt.model.BaseModel.onPropChanged " + ...
+ " Model: " + class(evt.AffectedObject) + ...
+ " Prop: " + evt.Source.Name + ...
+ " Class: " + class(obj));
+ end
+
+ % Return now if disabled
+ if ~obj.EnableChangeListeners
+ return
+ end
+
+ % Notify listeners
+ evtOutP = wt.eventdata.PropertyChangedData(...
+ evt.Source.Name, obj.(evt.Source.Name));
+ obj.notify("PropertyChanged",evtOutP)
+
+ % Prepare model change eventdata
+ evtOutM = wt.eventdata.ModelChangedData;
+ evtOutM.Property = evt.Source.Name;
+ evtOutM.Model = evt.AffectedObject;
+ evtOutM.Value = evtOutM.Model.(evtOutM.Property);
+ % evtOutM.Stack = {obj};
+ % evtOutM.ClassStack = class(obj);
+
+ % Revise listeners for model changes given the new value
+ if isa(evtOutM.Value, "wt.model.BaseModel")
+ evtOutM.Model.attachModelListeners();
+ end
+
+ % Call onModelChanged method
+ obj.onModelChanged(evtOutM);
+
end %function
-
+
+
+ function onModelChanged(obj,evt)
+ % Runs on property changes to this class or an aggregated BaseModel
+ % class
+
+ arguments
+ obj (1,1) wt.model.BaseModel
+ evt (1,1) wt.eventdata.ModelChangedData
+ end
+
+ % Return now if disabled
+ if ~obj.EnableChangeListeners
+ return
+ end
+
+ % Prepare eventdata
+ evtOut = wt.eventdata.ModelChangedData;
+ evtOut.Model = evt.Model;
+ evtOut.Property = evt.Property;
+ evtOut.Value = evt.Value;
+ evtOut.Stack = [{obj}, evt.Stack];
+ evtOut.ClassStack = [class(obj), evt.ClassStack];
+
+ if obj.Debug
+ disp("wt.model.BaseModel.onModelChanged " + ...
+ " Model: " + evtOut.ClassStack(end) + ...
+ " Prop: " + evtOut.Property + ...
+ " ClassStack: " + join(evtOut.ClassStack, " <- ") );
+ end
+
+ % Notify listeners
+ obj.notify("ModelChanged",evtOut)
+
+
+ end %function
+
end %methods
-
-
-
+
+
%% Private methods
- methods (Access=private)
-
+ methods (Access = private)
+
+ function attachModelListeners(obj)
+ % Attach listeners to aggregated BaseModel changes
+
+ arguments
+ obj (1,1) wt.model.BaseModel
+ end
+
+ % Get the properties to listen for
+ propNames = obj.getAggregatedModelProperties();
+
+ if obj.Debug
+ if isempty(propNames)
+ propDisp = "";
+ else
+ propDisp = join(propNames, ", ");
+ end
+ disp("wt.model.BaseModel.attachModelListeners " + ...
+ class(obj) + " Prop: " + propDisp);
+ end
+
+ % Preallocate array of listeners
+ numProps = numel(propNames);
+ newPropListeners = repmat({event.listener.empty(0,1)}, numProps, 1);
+
+ % Loop on each property requested, in case of multiple
+ % properties having aggregated handle objects
+ for idx = 1:numProps
+
+ % Get the current property
+ thisProp = propNames(idx);
+
+ % Get the model(s) to listen to
+ aggregatedObjects = obj.(thisProp);
+
+ % Skip this property if empty
+ if isempty(aggregatedObjects)
+ continue
+ end
+
+ % These objects must be handle!
+ allAreHandle = all(isa(aggregatedObjects, "wt.model.BaseModel"));
+ assert(allAreHandle,...
+ "Expected %s to contain a handle class object.",...
+ thisProp);
+
+ % Clear any invalid objects
+ aggregatedObjects(~isvalid(aggregatedObjects)) = [];
+
+ % Create listener to property changes within the model(s)
+ fcnModelChange = @(src,evt)onModelChanged(obj,evt);
+ newPropListeners{idx} = event.listener(aggregatedObjects,...
+ 'ModelChanged',fcnModelChange);
+
+ end %for
+
+ % Flatten the lists
+ newPropListeners = vertcat(newPropListeners{:});
+
+ % Store the results
+ obj.AggregatedModelListeners = newPropListeners;
+
+ end %function
+
+
+ function clearModelListeners(obj)
+ % Removes aggregated model listeners
+
+ delete(obj.AggregatedModelListeners);
+ obj.AggregatedModelListeners(:) = [];
+
+ end %function
+
+
function thisParser = getParser(obj,keepUnmatched)
-
+
% What class is this?
className = class(obj);
-
+
% Keep a list of reusable parsers for each class
persistent allParsers
if isempty(allParsers)
allParsers = containers.Map('KeyType','char','ValueType','any');
end
-
+
% Get or make a custom parser for this class
try
-
+
thisParser = allParsers(className);
-
+
catch
-
+
% Get a list of public properties
metaObj = metaclass(obj);
isSettableProp = strcmp({metaObj.PropertyList.SetAccess}','public');
@@ -168,25 +496,25 @@ function onPropChanged(obj,e)
hasDefault = [settableProps.HasDefault]';
defaultValues = repmat({[]},size(hasDefault));
defaultValues(hasDefault) = {settableProps(hasDefault).DefaultValue};
-
+
% Create custom parser for this class
thisParser = inputParser;
thisParser.KeepUnmatched = keepUnmatched;
thisParser.FunctionName = className;
-
+
% Add each public property to the parser
for pIdx = 1:numel(publicPropNames)
thisParser.addParameter(publicPropNames{pIdx}, defaultValues{pIdx});
end
-
+
% Add this parser to the map
allParsers(className) = thisParser;
-
+
end %if allParsers.isKey(className)
-
+
end %function
-
+
end %methods
-
-
-end %classdef
+
+
+end %classdef
\ No newline at end of file
diff --git a/widgets/+wt/+model/BaseSession.m b/widgets/+wt/+model/BaseSession.m
index c87f80e..6910e87 100644
--- a/widgets/+wt/+model/BaseSession.m
+++ b/widgets/+wt/+model/BaseSession.m
@@ -5,93 +5,201 @@
% data. Any properties tagged with the "SetObservable" attribute will
% trigger a public event "PropertyChanged" when value is set. The app
% will listen for these changes.
-
- % Copyright 2020-2021 The MathWorks, Inc.
-
-
+
+ % Copyright 2020-2024 The MathWorks, Inc.
+
+
+ %% Events
+ events
+
+ % Triggered when dirty flag toggles
+ MarkedDirty
+
+ % Triggered when dirty flag toggles
+ MarkedClean
+
+ end %events
+
+
%% Properties
properties (Dependent, SetAccess = immutable)
-
+
% FileName of the session (dependent on FileName)
FileName (1,1) string
-
+
end %properties
-
-
- properties (AbortSet, Transient)
-
+
+
+ properties (AbortSet, SetObservable)
+
% Path to store the session file
FilePath (1,1) string
-
- % Indicates modifications have not been saved
- Dirty (1,1) logical = false
-
- end %properties
-
-
- properties
-
+
% Description of the session (optional)
Description (1,1) string
-
+
+ end %properties
+
+
+ properties (AbortSet, Transient)
+
+ % Indicates modifications have not been saved
+ Dirty (1,1) logical = false
+
end %properties
-
-
+
+
+ % Accessors
+ methods
+ function set.Dirty(obj,value)
+
+ if obj.Debug
+ disp("wt.model.BaseSession.set.Dirty = " + string(value));
+ end
+
+ obj.Dirty = value;
+ if value
+ obj.notify("MarkedDirty")
+ else
+ obj.notify("MarkedClean")
+ end
+ end
+ end
+
+
%% Public methods (subclass may override these)
methods
-
- function save(session)
+
+ function save(session, filePath)
% Save a session object into a MAT file
-
- if ~strlength(session.FilePath)
- error('Session FilePath is empty.');
+
+ % Define arguments
+ arguments
+ session (1,1) wt.model.BaseSession
+ filePath (1,1) string = session.FilePath
end
- save(session.FilePath,'session');
-
+
+ if session.Debug
+ disp("wt.model.BaseSession.save");
+ end
+
+ if ~strlength(filePath)
+ error("Session FilePath is empty.");
+ end
+
+ session.FilePath = filePath;
+ session.Dirty = false;
+ save(filePath,'session');
+
end %function
-
+
end %methods
-
-
+
+
%% Public static methods
methods (Static, Sealed)
-
- function sessionObj = open(sessionPath)
+
+ function session = open(sessionPath)
% Load a session object from a MAT file - subclass may override
-
- contents = load(sessionPath,"session");
- sessionObj = contents.session;
-
+
+ % Attempt to load
+ try
+ contents = load(sessionPath,"session");
+ catch
+ % Throw an error
+ id = "wt:BaseSession:InvalidSessionFile";
+ msg = "Invalid session file: %s";
+ error(id, msg, sessionPath);
+ end
+
+ % Is it a valid file?
+ if isfield(contents,'session') ...
+ && isa(contents.session, "wt.model.BaseSession") ...
+ && isscalar(contents.session)
+
+ % Get the session
+ session = contents.session;
+
+ % Update file path if changed
+ session.FilePath = sessionPath;
+ session.Dirty = false;
+
+ else
+
+ % Throw an error
+ id = "wt:BaseSession:InvalidSessionFile";
+ msg = "Invalid session file: %s";
+ error(id, msg, sessionPath);
+
+ end
+
end %function
-
+
end %methods
-
+
+
+ % %% Hidden methods
+ % methods (Hidden)
+ %
+ % function setFilePathSilently(obj, filePath)
+ % % Set FilePath without triggering change notifications
+ %
+ % % Define arguments
+ % arguments
+ % obj (1,1) wt.model.BaseModel
+ % filePath (1,1) string
+ % end
+ %
+ % oldValue = obj.EnableChangeListeners;
+ % obj.EnableChangeListeners = false;
+ % obj.FilePath = filePath;
+ % obj.EnableChangeListeners = oldValue;
+ %
+ % end %function
+ %
+ % end %methods
+
%% Protected methods (subclass may override these)
methods (Access = protected)
+ function name = getDefaultName(obj)
+ % Defines what the default name should be. A subclass may
+ % override this to customize the default name.
+
+ [~,name,~] = fileparts(obj.FileName);
+
+ end %function
+
+
+ function onModelChanged(obj,evt)
+ % Triggered when the Session or any aggregated BaseModel
+ % classes have triggered a ModelChanged event (typically when
+ % SetObservable properties have changed)
+
+ if obj.Debug
+ classStack = [class(obj), evt.ClassStack];
+ disp("wt.model.BaseSession.onModelChanged " + ...
+ " Model: " + class(evt.Model) + ...
+ " Prop: " + evt.Property + ...
+ " ClassStack: " + join(classStack, " <- ") + ...
+ " (sets Session.Dirty = true)");
+ end
- function onPropChanged(obj,e)
- % Triggered when SetObservable properties have changed
-
% Mark the session dirty
obj.Dirty = true;
-
+
% Call superclass method to notify PropertyChanged event
- obj.onPropChanged@wt.model.BaseModel(e);
-
+ obj.onModelChanged@wt.model.BaseModel(evt);
+
end %function
-
+
end %methods
-
-
-
-
-
-
+
+
%% Accessors
methods
-
+
function value = get.FileName(obj)
if strlength(obj.FilePath)
[~,name,ext] = fileparts(obj.FilePath);
@@ -100,8 +208,8 @@ function onPropChanged(obj,e)
value = "untitled";
end
end %function
-
+
end %methods
-
-
+
+
end % classdef
diff --git a/widgets/+wt/+toolbar/HorizontalSection.m b/widgets/+wt/+toolbar/HorizontalSection.m
index d57d1de..10be7ab 100644
--- a/widgets/+wt/+toolbar/HorizontalSection.m
+++ b/widgets/+wt/+toolbar/HorizontalSection.m
@@ -109,22 +109,22 @@ function onButtonPushed(obj,evt)
% Use custom event data
if isa(evt,'matlab.ui.eventdata.ButtonPushedData')
% Push button
- evt = wt.eventdata.ButtonPushedData(evt.Source);
+ evtOut = wt.eventdata.ButtonPushedData(evt.Source);
elseif isa(evt,'matlab.ui.eventdata.ValueChangedData')
% State button
- evt = wt.eventdata.ButtonPushedData(evt.Source, evt.Source.Value);
+ evtOut = wt.eventdata.ButtonPushedData(evt.Source, evt.Source.Value);
end
% Add section name to event data
- addprop(evt,'Section');
- evt.Section = obj.Title;
+ addprop(evtOut,'Section');
+ evtOut.Section = obj.Title;
% Trigger event
- notify(obj,"ButtonPushed",evt);
+ notify(obj,"ButtonPushed",evtOut);
% Trigger callback
if ~isempty(obj.ButtonPushedFcn)
- feval(obj.ButtonPushedFcn, obj, evt);
+ feval(obj.ButtonPushedFcn, obj, evtOut);
end
end %function
@@ -146,9 +146,9 @@ function onButtonPushed(obj,evt)
end
function set.ComponentWidth(obj,value)
- evt = wt.eventdata.PropertyChangedData('ComponentWidth',value,obj.ComponentWidth);
+ evtOut = wt.eventdata.PropertyChangedData('ComponentWidth',value,obj.ComponentWidth);
obj.ComponentWidth = value;
- notify(obj,'PropertyChanged',evt)
+ notify(obj,'PropertyChanged',evtOut)
end
@@ -171,16 +171,16 @@ function onButtonPushed(obj,evt)
function set.Title(obj,value)
- evt = wt.eventdata.PropertyChangedData('Title',value,obj.Title);
+ evtOut = wt.eventdata.PropertyChangedData('Title',value,obj.Title);
obj.Title = value;
- notify(obj,'PropertyChanged',evt)
+ notify(obj,'PropertyChanged',evtOut)
end
function set.MinimizedWidth(obj,value)
- evt = wt.eventdata.PropertyChangedData('MinimizedWidth',value,obj.MinimizedWidth);
+ evtOut = wt.eventdata.PropertyChangedData('MinimizedWidth',value,obj.MinimizedWidth);
obj.MinimizedWidth = value;
- notify(obj,'PropertyChanged',evt)
+ notify(obj,'PropertyChanged',evtOut)
end
end %methods
diff --git a/widgets/+wt/+toolbar/VerticalSection.m b/widgets/+wt/+toolbar/VerticalSection.m
index 5480df1..e1dc215 100644
--- a/widgets/+wt/+toolbar/VerticalSection.m
+++ b/widgets/+wt/+toolbar/VerticalSection.m
@@ -82,18 +82,18 @@ function onButtonPushed(obj,evt)
% Use custom event data
if isa(evt,'matlab.ui.eventdata.ButtonPushedData')
% Push button
- evt = wt.eventdata.ButtonPushedData(evt.Source);
+ evtOut = wt.eventdata.ButtonPushedData(evt.Source);
elseif isa(evt,'matlab.ui.eventdata.ValueChangedData')
% State button
- evt = wt.eventdata.ButtonPushedData(evt.Source, evt.Source.Value);
+ evtOut = wt.eventdata.ButtonPushedData(evt.Source, evt.Source.Value);
end
% Trigger event
- notify(obj,"ButtonPushed",evt);
+ notify(obj,"ButtonPushed",evtOut);
% Trigger callback
if ~isempty(obj.ButtonPushedFcn)
- feval(obj.ButtonPushedFcn, obj, evt);
+ feval(obj.ButtonPushedFcn, obj, evtOut);
end
end %function
diff --git a/widgets/+wt/+utility/setStylePropsInPriority.m b/widgets/+wt/+utility/setStylePropsInPriority.m
index 1d9c142..54113d7 100644
--- a/widgets/+wt/+utility/setStylePropsInPriority.m
+++ b/widgets/+wt/+utility/setStylePropsInPriority.m
@@ -30,6 +30,17 @@ function setStylePropsInPriority(comps, propNames, value)
isDone(needsSet) = true;
end
+ %RJ - Tried this but still the default componentcontainer has wuite
+ %background color. Need to investigate more.
+ % Also need to change BackgroundColorableComponents (:,1) to row vector
+ % Set as needed
+ % for thisComp = comps(needsSet)
+ % if ~isequal(thisComp.(thisProp), value)
+ % thisComp.(thisProp) = value;
+ % end
+ % end
+ % isDone(needsSet) = true;
+
% Return early if complete
if all(isDone)
return;
diff --git a/widgets/+wt/ColorSelector.m b/widgets/+wt/ColorSelector.m
index 4875c7b..aea41fa 100644
--- a/widgets/+wt/ColorSelector.m
+++ b/widgets/+wt/ColorSelector.m
@@ -1,203 +1,195 @@
classdef ColorSelector < matlab.ui.componentcontainer.ComponentContainer & ...
wt.mixin.BackgroundColorable & ...
- wt.mixin.Enableable & wt.mixin.FontStyled & wt.mixin.Tooltipable & ...
- wt.mixin.FieldColorable & wt.mixin.PropertyViewable
-
+ wt.mixin.Enableable & ...
+ wt.mixin.FieldColorable & ...
+ wt.mixin.FontStyled & ...
+ wt.mixin.Tooltipable
+
% Color selection control with browse button
-
- % Copyright 2020-2022 The MathWorks Inc.
-
-
+
+ % Copyright 2020-2024 The MathWorks Inc.
+
+
%% Public properties
properties (AbortSet)
-
+
% The current value shown
- Value (1,3) double {wt.validators.mustBeBetweenZeroAndOne} = [0 1 0]
-
+ Value (1,3) double {mustBeInRange(Value,0,1)} = [0 1 0]
+
end %properties
-
-
+
+
% These properties do not trigger the update method
properties (AbortSet, UsedInUpdate = false)
-
+
% Indicates whether to show the edit field
ShowEditField (1,1) matlab.lang.OnOffSwitchState = true
-
+
end %properties
-
-
+
+
%% Events
events (HasCallbackProperty, NotifyAccess = protected)
-
+
% Triggered on value changed, has companion callback
ValueChanged
-
+
end %events
-
-
-
+
+
+
%% Internal Properties
properties (Transient, NonCopyable, Hidden, SetAccess = protected)
-
+
% Button
ButtonControl (1,1) matlab.ui.control.Button
% Grid
Grid (1,1) matlab.ui.container.GridLayout
-
+
% Edit control
EditControl (1,1) matlab.ui.control.EditField
-
+
end %properties
-
-
-
+
+
%% Protected methods
methods (Access = protected)
-
+
function setup(obj)
-
+
% Construct Grid Layout to Manage Building Blocks
obj.Grid = uigridlayout(obj);
obj.Grid.ColumnWidth = {'1x'};
obj.Grid.RowHeight = {'1x'};
obj.Grid.RowSpacing = 2;
obj.Grid.ColumnSpacing = 2;
- obj.Grid.Padding = 0;
-
+ obj.Grid.Padding = 0;
+
% Set default size
obj.Position(3:4) = [100 25];
-
+
% Configure Grid
obj.Grid.ColumnWidth = {'1x',25};
obj.Grid.RowHeight = {'1x'};
-
+
% Create the standard edit control
obj.EditControl = matlab.ui.control.EditField(...
"Parent",obj.Grid,...
"ValueChangedFcn",@(h,e)obj.onTextChanged(e));
-
+
% Create Button
obj.ButtonControl = matlab.ui.control.Button(...
"Parent",obj.Grid,...
"Text","",...
"ButtonPushedFcn",@(h,e)obj.onButtonPushed(e));
-
+
% Update the internal component lists
obj.FontStyledComponents = [obj.EditControl];
obj.FieldColorableComponents = [obj.EditControl];
obj.EnableableComponents = [obj.EditControl, obj.ButtonControl];
obj.TooltipableComponents = [obj.EditControl, obj.ButtonControl];
obj.BackgroundColorableComponents = obj.Grid;
-
+
end %function
-
-
+
+
function update(obj)
-
+
% Update the edit control text
obj.EditControl.Value = mat2str(obj.Value,2);
-
+
% Update the button color
obj.ButtonControl.BackgroundColor = obj.Value;
-
- end %function
-
- function propGroups = getPropertyGroups(obj)
- % Override the ComponentContainer GetPropertyGroups with newly
- % customiziable mixin. This can probably also be specific to each control.
+ end %function
- propGroups = getPropertyGroups@wt.mixin.PropertyViewable(obj);
- end %function
-
-
function updateFieldVisibility(obj)
-
+
% Is history being shown? If so, update history and items
if obj.ShowEditField
% Showing edit field
-
+
if isempty(obj.EditControl.Parent)
obj.ButtonControl.Layout.Column = 2;
obj.EditControl.Parent = obj.Grid;
obj.EditControl.Layout.Column = 1;
obj.EditControl.Layout.Row = 1;
end
-
+
else
% Hiding edit field
-
+
if ~isempty(obj.EditControl.Parent)
obj.EditControl.Parent = [];
obj.ButtonControl.Layout.Column = [1 2];
end
-
+
end %if
-
+
end %function
-
-
+
+
function onButtonPushed(obj,~)
% Triggered on button pushed
-
+
% Get prior value
oldValue = obj.Value;
-
+
% Prompt for a new color
newColor = uisetcolor(oldValue);
-
+
% Did user make a choice or cancel?
if ~isequal(newColor,0)
% Update the color
obj.Value = newColor;
end
-
+
% Trigger event
evtOut = wt.eventdata.ValueChangedData(obj.Value, oldValue);
notify(obj,"ValueChanged",evtOut);
-
+
end %function
-
-
+
+
function onTextChanged(obj,evt)
% Triggered on text interaction - subclass may override
-
+
% Get prior value
oldValue = obj.Value;
-
+
% Trap errors
try
-
+
% Store new result
obj.Value = str2num(evt.Value); %#ok
-
+
% Trigger event
evtOut = wt.eventdata.ValueChangedData(obj.Value, oldValue);
notify(obj,"ValueChanged",evtOut);
-
+
catch
-
+
% Restore original value
obj.update();
-
+
end %try
-
+
end %function
-
+
end %methods
-
-
+
+
%% Accessors
methods
-
+
function set.ShowEditField(obj,value)
obj.ShowEditField = value;
obj.updateFieldVisibility()
end
-
+
end % methods
-
-
+
+
end % classdef
\ No newline at end of file
diff --git a/widgets/+wt/ContextualView.m b/widgets/+wt/ContextualView.m
new file mode 100644
index 0000000..6025a4e
--- /dev/null
+++ b/widgets/+wt/ContextualView.m
@@ -0,0 +1,613 @@
+classdef ContextualView < matlab.ui.componentcontainer.ComponentContainer & ...
+ wt.mixin.BackgroundColorable & wt.mixin.ErrorHandling
+ % wt.mixin.Enableable & wt.mixin.FontStyled & wt.mixin.Tooltipable & ...
+ % wt.mixin.FieldColorable & wt.mixin.PropertyViewable
+
+ % Contextual View/Controller pane that can present varied views
+ % This pane can switch its contents between multiple different
+ % contextual components inside. It is much like a tabpanel, but without
+ % the tab headers and it's optimized to only render one view at a time.
+ % This is useful in design patterns such as when you have a tree on the
+ % left and on the right a contextual view that displays different
+ % content determined by the selected tree node.
+ %
+ % Content views that are placed in this ContextualView should
+ % inherit the following classes:
+ % matlab.ui.componentcontainer.ComponentContainer
+ % wt.mixin.ContextualView (if auto-populating Model property)
+
+ % Copyright 2024 The MathWorks Inc.
+
+
+ %% Events
+ events (HasCallbackProperty, NotifyAccess = protected)
+
+ % Triggered when the Model has changed
+ ModelSet
+
+ % Triggered when a property within the model has changed
+ ModelChanged
+
+ end %events
+
+
+ %% Public Properties
+ properties (AbortSet, Access = public)
+
+ % Block while loading
+ BlockWhileLoading (1,1) logical = true
+
+ % Image to use on loading screen
+ LoadingImageSource (1,1) string = "loading_32.gif"
+
+ end %properties
+
+
+ %% Internal Properties
+ properties (AbortSet, Transient, NonCopyable, Hidden, ...
+ SetAccess = protected, UsedInUpdate = false)
+
+ % Top-level grid to manage content vs. loading
+ MainGrid matlab.ui.container.GridLayout
+
+ % The internal grid to manage contents
+ ContentGrid matlab.ui.container.GridLayout
+
+ % Image to show when loading a pane
+ LoadingImage matlab.ui.control.Image
+
+ % Listener for a new model being attached
+ ModelSetListener event.listener
+
+ % Listener for property changes within the model
+ ModelPropertyChangedListener event.listener
+
+ end %properties
+
+
+ properties (SetAccess = private, UsedInUpdate = false)
+
+ % The currently active view, or empty if none
+ ActiveView (:,1) matlab.graphics.Graphics ...
+ {mustBeScalarOrEmpty, mustBeValidView(ActiveView)} = ...
+ wt.abstract.BaseViewController.empty(0,1)
+
+ % The array of views loaded into memory
+ LoadedViews (:,1) matlab.graphics.Graphics ...
+ {mustBeValidView(LoadedViews)} = ...
+ wt.abstract.BaseViewController.empty(0,1)
+
+ end %properties
+
+
+ % Accessors
+ methods
+
+ function value = get.ActiveView(obj)
+ value = obj.ActiveView;
+ % Remove view if deleted
+ value(~isvalid(value)) = [];
+ end
+
+ function set.ActiveView(obj,value)
+ % Remove view if deleted
+ value(~isvalid(value)) = [];
+ obj.ActiveView = value;
+ end
+
+ function value = get.LoadedViews(obj)
+ value = obj.LoadedViews;
+ % Remove any deleted views from the list
+ keepItems = arrayfun(@isvalid, value);
+ value(~keepItems) = [];
+ end
+
+ function set.LoadedViews(obj,value)
+ % Remove any deleted views from the list
+ keepItems = arrayfun(@isvalid, value);
+ value(~keepItems) = [];
+ obj.LoadedViews = value;
+ end
+
+ end %methods
+
+
+ %% Public Methods
+ methods
+
+ function varargout = launchView(obj, viewClass, model)
+ % This method may be overloaded as needed
+
+ arguments
+ obj (1,1) wt.ContextualView
+ viewClass (1,1) string
+ model wt.model.BaseModel = wt.model.BaseModel.empty
+ end
+
+ % After launch is complete, toggle off loading image
+ cleanupObj = onCleanup(@()set(obj.LoadingImage,"Visible","off"));
+
+ % Is the loading image non-visible?
+ if ~obj.LoadingImage.Visible
+
+ % If the view will change, show the loading image
+ obj.prepareToLaunchView(viewClass)
+
+ end %if
+
+ % Was a view provided?
+ if strlength(viewClass)
+
+ % Validate view class
+ view = validateViewClass(obj, viewClass);
+
+ % If no existing view found, instantiate the view
+ if isempty(view)
+ obj.instantiateView_Private(viewClass, model);
+ else
+ obj.activateView_Private(view, model)
+ end
+
+ else
+
+ % Empty view, clear the contents
+ obj.clearView();
+
+ end %if
+
+ % Return the active view
+ if nargout
+ varargout{1} = obj.ActiveView;
+ end
+
+ end %function
+
+
+ function prepareToLaunchView(objArray, viewClassArray)
+ % Puts the ContextualView in a loading state if a different
+ % view is about to be launched. Use this if your app has
+ % multiple ContextualView instances and you need to potentially
+ % load multiple simultaneously. You can provide an array of
+ % views here to turn them to loading state together.
+
+ arguments
+ objArray wt.ContextualView
+ viewClassArray string
+ end
+
+ for idx = 1:numel(objArray)
+
+ % Get one at a time
+ obj = objArray(idx);
+ viewClass = viewClassArray(idx);
+
+ % Is the view class going to change?
+ willLaunchView = isempty(obj.ActiveView) && strlength(viewClass);
+ willChangeView = isscalar(obj.ActiveView) && ...
+ class(obj.ActiveView) ~= viewClass;
+
+ % If the view will change, show the loading image
+ if obj.BlockWhileLoading && (willLaunchView || willChangeView)
+
+ % Prevent interaction during launch
+ obj.LoadingImage.Visible = "on";
+
+ end %if
+
+ end %for
+
+ % Enable them all to update
+ drawnow
+
+ end %function
+
+
+ function clearView(obj)
+ % This method may be overloaded as needed
+
+ arguments
+ obj (1,1) wt.ContextualView
+ end
+
+ % Remove listeners
+ obj.ModelSetListener(:) = [];
+ obj.ModelPropertyChangedListener(:) = [];
+
+ % Clear the view
+ obj.deactivateView_Private();
+
+ % Delete any orphaned children
+ delete(obj.ContentGrid.Children);
+
+ end %function
+
+
+ function relaunchActiveView(obj)
+ % Delete and reload the active view
+
+ % Is there an active view? If no, return early
+ activeView = obj.ActiveView;
+ if isempty(activeView)
+ warning("wt:ContextualView:noActiveView",...
+ "No active view is present.")
+ return
+ end
+
+ % Get the current view and model
+ viewClass = class(activeView);
+ model = activeView.Model;
+
+ % Deactivate the active view
+ obj.clearView();
+
+ % Delete the previously active view
+ delete(activeView);
+ obj.LoadedViews(obj.LoadedViews == activeView) = [];
+
+ % Launch the same view again
+ obj.launchView(viewClass, model);
+
+ end %function
+
+
+ function reset(obj)
+ % Reset the control by deactivating current view and delete loaded views
+
+ % Remove listeners
+ obj.ModelSetListener(:) = [];
+ obj.ModelPropertyChangedListener(:) = [];
+
+ % Deactivate any active view
+ obj.deactivateView_Private();
+
+ % Delete any loaded views
+ delete(obj.LoadedViews);
+ obj.LoadedViews(:) = [];
+
+ % Delete any orphaned children
+ delete(obj.ContentGrid.Children);
+
+ % Reset the layout state
+ obj.ContentGrid.ColumnWidth = {'1x'};
+ obj.ContentGrid.RowHeight = {'1x'};
+
+ end %function
+
+ end %methods
+
+
+ %% Protected Methods
+ methods (Access=protected)
+
+ function setup(obj)
+ % Configure the widget
+
+ obj.MainGrid = uigridlayout(obj,[1 1]);
+ obj.MainGrid.Padding = [0 0 0 0];
+
+ % Grid Layout to place the contents
+ obj.ContentGrid = uigridlayout(obj.MainGrid,[1 1]);
+ obj.ContentGrid.Padding = [0 0 0 0];
+ obj.ContentGrid.Layout.Row = 1;
+ obj.ContentGrid.Layout.Column = 1;
+
+ % Image to display while loading content
+ obj.LoadingImage = uiimage(obj.MainGrid);
+ obj.LoadingImage.Layout.Row = 1;
+ obj.LoadingImage.Layout.Column = 1;
+ obj.LoadingImage.Visible = "off";
+ obj.LoadingImage.ScaleMethod = "none";
+
+ % Components to apply background color
+ obj.BackgroundColorableComponents = ...
+ [obj.ContentGrid, obj.MainGrid, obj.LoadingImage];
+
+ end %function
+
+
+ function update(obj)
+
+ % Configure the loading image
+ obj.LoadingImage.Visible = "off";
+ obj.LoadingImage.ImageSource = obj.LoadingImageSource;
+
+ end %function
+
+ end %methods
+
+
+ %% Private methods
+ methods (Access=private)
+
+ function view = validateViewClass(obj, viewClass)
+ % This validates the view class and check for existing
+ % instances. If an existing instance is found, it is returned.
+ % Otherwise, an empty view is returned
+
+ arguments (Input)
+ obj (1,1) wt.ContextualView
+ viewClass (1,1) string
+ end
+
+ arguments (Output)
+ view (:,1) {mustBeScalarOrEmpty, mustBeValidView(view)}
+ end
+
+ % Try to locate a valid view
+ if ~exist(viewClass,"class")
+
+ % Throw an error and return
+ id = "wt:ContextualView:InvalidPaneType";
+ message = "Invalid view type (%s). The viewClass " + ...
+ "must be a valid class path.";
+ error(id, message, viewClass);
+
+ elseif isequal(viewClass, class(obj.ActiveView))
+
+ % Pane is already active
+ view = obj.ActiveView;
+
+ else
+
+ % Check if the view already exists
+ view = wt.abstract.BaseViewController.empty(0,1);
+ for thisView = obj.LoadedViews'
+ if viewClass == class(thisView)
+ view = thisView;
+ break
+ end
+ end
+
+ end %if
+
+ end %function
+
+
+ function view = instantiateView_Private(obj, viewClass, model)
+ % Launch a view based on the class path
+
+ arguments (Input)
+ obj (1,1) wt.ContextualView
+ viewClass (1,1) string
+ model wt.model.BaseModel %= wt.model.BaseModel.empty(0)
+ end
+
+ arguments (Output)
+ view (:,1) {mustBeScalarOrEmpty, mustBeValidView(view)}
+ end
+
+ % Trap errors
+ % try
+
+ % Get function handle to the view's constructor
+ viewConstructorFcn = str2func(viewClass);
+
+ % Launch the view
+ view = viewConstructorFcn(obj.ContentGrid);
+ % view = viewConstructorFcn("Parent",obj.Grid);
+
+ % Position the view in the single grid cell
+ view.Layout.Row = 1;
+ view.Layout.Column = 1;
+
+ % Add the new view to the list
+ obj.LoadedViews = vertcat(obj.LoadedViews, view);
+
+ % catch err
+ %
+ % % Clean up partially loaded children
+ % delete(obj.Grid.Children(2:end))
+ %
+ % % Throw an error
+ % message = sprintf("Error launching view (%s).\n\n%s",...
+ % viewClass, err.message);
+ % obj.throwError(message);
+ %
+ % % Deactivate current pane
+ % obj.deactivateView_Private();
+ %
+ % % Rethrow the error to the command window
+ % rethrow(err)
+ %
+ % end %try
+
+ % Activate the view
+ obj.activateView_Private(view, model)
+
+ end %function
+
+
+ function activateView_Private(obj, view, model)
+ % Activate a view, placing it in view and attaching a model
+
+ arguments
+ obj (1,1) wt.ContextualView
+ view (1,1) {mustBeValidView(view)}
+ model wt.model.BaseModel
+ end
+
+ % Does this view need to be made active?
+ needToMarkActive = ~isequal(obj.ActiveView, view);
+ if needToMarkActive
+
+ % Remove the old view at the end of this function
+ oldView = obj.ActiveView;
+ cleanupObj = onCleanup(@()obj.deactivateView_Private(oldView));
+
+ % Assign parent
+ if ~isequal(view.Parent, obj.ContentGrid)
+ view.Parent = obj.ContentGrid;
+ end
+ view.Visible = true;
+
+ % Store this view as active view
+ obj.ActiveView = view;
+
+ end %if
+
+ % Attach model
+ if ~isempty(model) && all(isvalid(model(:)))
+
+ % Attach the model
+ obj.attachModel_Private(view, model)
+
+ end %if
+
+ % Did this view need to be made active?
+ if needToMarkActive
+
+ % Listen to the active view indicating that its model has been
+ % set or changed
+ obj.ModelSetListener = listener(view,...
+ "ModelSet",@(~,evt)obj.onModelSet(evt));
+
+ obj.ModelPropertyChangedListener = listener(view,...
+ "ModelChanged",@(~,evt)obj.onModelChanged(evt));
+
+ end %if
+
+ end %function
+
+
+ % function attachModelPropertyChangedListener(obj)
+ %
+ % % Listen to the active view indicating model changes
+ % model = obj.ActiveView.Model;
+ % obj.ModelPropertyChangedListener = listener(model,...
+ % "ModelChanged",@(~,evt)obj.onModelChanged(evt));
+ %
+ % end %function
+
+
+ function deactivateView_Private(obj, view)
+ % Deactivate a view, removing from view and removing model
+ arguments
+ obj (1,1) wt.ContextualView
+ view {mustBeValidView(view)} = obj.ActiveView
+ end
+
+ % Return now if view provided is empty
+ if isempty(view) || ~isvalid(view)
+ return
+ end
+
+ % Enable any prior view changes to finish, avoiding any
+ % "flashing" effects during the change
+ % drawnow
+ % drawnow('nocallbacks','limitrate')
+
+ % Remove any listeners on the view
+ obj.ModelSetListener = obj.deleteListenersForSource(...
+ obj.ModelSetListener, view);
+ obj.ModelPropertyChangedListener = obj.deleteListenersForSource(...
+ obj.ModelPropertyChangedListener, view);
+
+ % Deactivate the view, removing model and parent
+ view.Model(:) = [];
+ view.Parent(:) = [];
+
+ % Remove view from the active view property
+ if isequal(obj.ActiveView, view)
+ obj.ActiveView(:) = [];
+ end
+
+ % Clean up partially loaded children
+ delete(obj.ContentGrid.Children(2:end))
+
+ end %function
+
+
+ function modelListener = deleteListenersForSource(~, modelListener, source)
+ % Deletes listeners with the given source
+
+ deleteListener = false(size(modelListener));
+ for idx = numel(modelListener) : -1 : 1
+ thisListener = modelListener(idx);
+ for sIdx = 1:numel(thisListener.Source)
+ thisSrc = thisListener.Source{sIdx};
+ if thisSrc == source
+ deleteListener(idx) = true;
+ end
+ end
+ end
+ delete(modelListener(deleteListener))
+ modelListener(deleteListener) = [];
+
+ end %function
+
+
+ function attachModel_Private(obj, view, model)
+ % Attach model to the active view
+
+ arguments
+ obj (1,1) wt.ContextualView
+ view (1,1) {mustBeValidView(view)}
+ model wt.model.BaseModel
+ end
+
+ % Trap errors during model assignment
+ if ~isempty(model) && all(isvalid(model(:)))
+
+ try
+ % Assign model
+ view.Model = model;
+
+ % Listen to model changes
+ % obj.attachModelPropertyChangedListener();
+
+ catch err
+
+ message = sprintf("Unable to assign model (%s) " + ...
+ "to view/controller (%s).\n\n%s",...
+ class(model), class(view), err.message);
+ obj.throwError(message);
+
+ end %try
+
+ end %if
+
+ end %function
+
+
+ function onModelSet(obj,evt)
+
+ % Prepare eventdata
+ evtOut = wt.eventdata.ModelSetData;
+ evtOut.Model = evt.Model;
+ evtOut.Controller = evt.Controller;
+
+ % Notify listeners
+ obj.notify("ModelSet",evtOut);
+
+ end %function
+
+
+ function onModelChanged(obj,evt)
+
+ % Prepare eventdata
+ evtOut = wt.eventdata.ModelChangedData;
+ evtOut.Model = evt.Model;
+ evtOut.Property = evt.Property;
+ evtOut.Value = evt.Value;
+ evtOut.Stack = [{obj}, evt.Stack];
+ evtOut.ClassStack = [class(obj), evt.ClassStack];
+
+ % Notify listeners
+ obj.notify("ModelChanged",evtOut);
+
+ end %function
+
+ end %methods
+
+end %classdef
+
+
+% Validation function
+function mustBeValidView(view)
+
+for idx = 1:numel(view)
+ mustBeA(view(idx), "wt.mixin.ModelObserver")
+ mustBeA(view(idx), ["wt.abstract.BaseViewController", "wt.abstract.BaseViewChart"])
+end
+
+end %function
\ No newline at end of file
diff --git a/widgets/+wt/DropDownListManager.m b/widgets/+wt/DropDownListManager.m
new file mode 100644
index 0000000..68e6698
--- /dev/null
+++ b/widgets/+wt/DropDownListManager.m
@@ -0,0 +1,545 @@
+classdef DropDownListManager < matlab.ui.componentcontainer.ComponentContainer & ...
+ wt.mixin.BackgroundColorable & ...
+ wt.mixin.ButtonColorable &...
+ wt.mixin.Enableable & ...
+ wt.mixin.FieldColorable & ...
+ wt.mixin.FontStyled & ...
+ wt.mixin.Tooltipable
+ % Manage a list of text entries using a dropdown control
+
+ % Copyright 2024 The MathWorks Inc.
+
+ %RJ - supports R2023b and later, but test in earlier releases
+
+
+ %% Events
+ events (HasCallbackProperty, NotifyAccess = protected)
+
+ % Triggered when the value of the list selection changes
+ ItemsChanged
+
+ end %events
+
+
+ %% Public properties
+ properties (AbortSet, Dependent, UsedInUpdate = false)
+
+ % Index of selected list item
+ Index {mustBeNonnegative, mustBeInteger, mustBeScalarOrEmpty}
+
+ % The current selection
+ Value (1,:)
+
+ % List of items in the dropdown
+ Items (1,:) string
+
+ end %properties
+
+
+ properties (AbortSet)
+
+ % Data associated with items (optional)
+ ItemsData (1,:)
+
+ end %properties
+
+
+ properties (AbortSet)
+
+ % Show the remove button?
+ AllowRemove (1,1) matlab.lang.OnOffSwitchState = true
+
+ % Show the rename button?
+ AllowRename (1,1) matlab.lang.OnOffSwitchState = true
+
+ % Can each item be removed?
+ AllowItemRemove (1,:) logical
+
+ % Can each item be renamed?
+ AllowItemRename (1,:) logical
+
+ % List of items to add to the list
+ NewItemName (1,1) string = ""
+
+ end %properties
+
+
+ % Accessors
+ methods
+
+ function value = get.Items(obj)
+ value = string(obj.DropDown.Items);
+ end
+
+ function set.Items(obj,value)
+ obj.DropDown.Items = value;
+ obj.DropDown.ItemsData = 1:numel(value);
+ end
+
+ function value = get.Index(obj)
+ if isMATLABReleaseOlderThan("R2023b")
+ warnState = warning('off','MATLAB:structOnObject');
+ s = struct(obj.DropDown);
+ warning(warnState);
+ value = s.SelectedIndex;
+ else
+ value = obj.DropDown.ValueIndex;
+ end
+ end
+
+ function set.Index(obj,value)
+ if isMATLABReleaseOlderThan("R2023b")
+ obj.DropDown.Value = obj.DropDown.ItemsData(value);
+ else
+ obj.DropDown.ValueIndex = value;
+ end
+ end
+
+ function value = get.Value(obj)
+ value = obj.DropDown.Value;
+ end
+
+ function set.Value(obj,value)
+ obj.DropDown.Value = value;
+ end
+
+ function set.AllowRemove(obj,value)
+ obj.AllowRemove = value;
+ obj.updateButtonVisibilities();
+ end
+
+ function set.AllowRename(obj,value)
+ obj.AllowRename = value;
+ obj.updateButtonVisibilities();
+ end
+
+ function value = get.AllowItemRemove(obj)
+ value = resize(obj.AllowItemRemove, numel(obj.Items), ...
+ "FillValue", true);
+ end
+
+ function value = get.AllowItemRename(obj)
+ value = resize(obj.AllowItemRename, numel(obj.Items), ...
+ "FillValue", true);
+ end
+
+ end %methods
+
+
+
+ %% Internal Properties
+ properties (UsedInUpdate, SetAccess = protected)
+
+ % Indicates when new item is being entered
+ IsAddingNewItem (1,1) logical = false
+
+ % Indicates when an item is being renamed
+ IsRenamingItem (1,1) logical = false
+
+ end %properties
+
+
+ properties (Transient, NonCopyable, Hidden, SetAccess = protected)
+
+ % The dropdown control
+ DropDown (1,1) matlab.ui.control.DropDown
+
+ % The edit field
+ EditField (1,1) matlab.ui.control.EditField
+
+ % Grid
+ Grid (1,1) matlab.ui.container.GridLayout
+
+ % The list sorting buttons
+ ListButtons wt.ButtonGrid
+
+ % Buttons
+ AddButton matlab.ui.control.Button
+ RenameButton matlab.ui.control.Button
+ RemoveButton matlab.ui.control.Button
+
+ % Listeners to enable editing to stop
+ StopEditingListeners event.listener
+
+ end %properties
+
+
+
+
+ %% Protected methods
+ methods (Access = protected)
+
+ function setup(obj)
+
+ % Set default size
+ obj.Position(3:4) = [250 30];
+
+ % Construct Default Grid Layout to Manage Building Blocks
+ obj.Grid = uigridlayout(obj,[1 3]);
+ obj.Grid.ColumnWidth = {'1x',30,30,30};
+ obj.Grid.RowHeight = {'1x'};
+ obj.Grid.ColumnSpacing = 2;
+ obj.Grid.Padding = 0;
+
+ % Create the DropDown
+ obj.DropDown = uidropdown(obj.Grid);
+ obj.DropDown.Items ={'Item One','Item Two'};
+ obj.DropDown.ItemsData = [1 2];
+ obj.DropDown.ValueChangedFcn = @(~,evt)obj.onValueChanged(evt);
+ obj.DropDown.Layout.Column = 1;
+ obj.DropDown.Layout.Row = 1;
+
+ % Create the EditField
+ obj.EditField = uieditfield(obj.Grid);
+ obj.EditField.Value = "";
+ obj.EditField.ValueChangedFcn = @(~,evt)obj.onEditFieldChanged(evt);
+ obj.EditField.Layout.Column = 1;
+ obj.EditField.Layout.Row = 1;
+ obj.EditField.Visible = false;
+
+ % Create the buttons
+ obj.AddButton = uibutton(obj.Grid);
+ obj.AddButton.Icon = "addYellow_24.png";
+ obj.AddButton.Text = "";
+ obj.AddButton.Layout.Column = 2;
+ obj.AddButton.Layout.Row = 1;
+ obj.AddButton.ButtonPushedFcn = @(~,~)obj.onAddButton();
+
+ obj.RenameButton = uibutton(obj.Grid);
+ obj.RenameButton.Icon = "edit_24.png";
+ obj.RenameButton.Text = "";
+ obj.RenameButton.Layout.Column = 3;
+ obj.RenameButton.Layout.Row = 1;
+ obj.RenameButton.ButtonPushedFcn = @(~,~)obj.onRenameButton();
+
+ obj.RemoveButton = uibutton(obj.Grid);
+ obj.RemoveButton.Icon = "delete_24.png";
+ obj.RemoveButton.Text = "";
+ obj.RemoveButton.Layout.Column = 4;
+ obj.RemoveButton.Layout.Row = 1;
+ obj.RemoveButton.ButtonPushedFcn = @(~,~)obj.onRemoveButton();
+
+ % Update the internal component lists
+ obj.BackgroundColorableComponents = obj.Grid;
+ obj.ButtonColorableComponents = [obj.AddButton, ...
+ obj.RenameButton, obj.RemoveButton];
+ obj.EnableableComponents = [obj.DropDown, obj.EditField, ...
+ obj.AddButton, obj.RenameButton, obj.RemoveButton];
+ obj.FieldColorableComponents = [obj.DropDown, obj.EditField];
+ obj.FontStyledComponents = [obj.DropDown, obj.EditField];
+ obj.TooltipableComponents = [obj.DropDown];
+
+ end %function
+
+
+ function update(obj)
+
+ % Toggle between dropdown and edit field
+ isEditMode = obj.IsAddingNewItem || obj.IsRenamingItem;
+ obj.EditField.Visible = isEditMode;
+ obj.DropDown.Visible = ~isEditMode;
+
+ % Remove any listeners if no longer editing
+ if ~isEditMode
+ obj.StopEditingListeners(:) = [];
+ end
+
+ % Update button enable states
+ obj.updateEnableableComponents();
+
+ end %function
+
+
+ function onEditFieldChanged(obj,evt)
+ % Triggered when editing in edit field mode
+
+ if obj.IsRenamingItem
+
+ % Data for this event
+ action = "Renamed";
+ item = string(evt.Value);
+ index = obj.Index;
+ data = obj.getItemDataByIndex(index);
+
+ % Strip off any whitespace
+ item = strip(item);
+
+ % Update the list item and select it
+ obj.Items(index) = item;
+ obj.Index = index;
+
+ % Toggle mode OFF
+ obj.IsRenamingItem = false;
+
+ elseif obj.IsAddingNewItem
+
+ % Data for this event
+ action = "Added";
+ item = string(evt.Value);
+ index = numel(obj.Items) + 1;
+ data = [];
+
+ % Strip off any whitespace
+ item = strip(item);
+
+ % Add the new item to the list and select it
+ obj.Items(index) = item;
+ obj.Index = index;
+
+ % Toggle mode OFF
+ obj.IsAddingNewItem = false;
+
+ else
+ % Should not get here
+
+ % Toggle mode OFF
+ obj.IsRenamingItem = false;
+ obj.IsAddingNewItem = false;
+
+ % Exit
+ return
+
+ end %if
+
+ % Prepare event data
+ evtOut = wt.eventdata.ListManagerEventData();
+ evtOut.Action = action;
+ evtOut.Item = item;
+ evtOut.ItemData = data;
+ evtOut.Index = index;
+
+ % Notify listeners
+ notify(obj,"ItemsChanged",evtOut);
+
+ end %function
+
+
+ function onValueChanged(obj,evt)
+ % Triggered on dropdown selection
+
+ % Prepare event data
+ evtOut = wt.eventdata.ListManagerEventData();
+ evtOut.Action = "Selected";
+ evtOut.Item = obj.Items(evt.Value);
+ evtOut.ItemData = obj.getItemDataByIndex(evt.Value);
+ evtOut.Index = evt.Value;
+
+ % Notify listeners
+ notify(obj,"ItemsChanged",evtOut);
+
+ % Update button enable states
+ obj.updateEnableableComponents();
+
+ end %function
+
+
+ function data = getItemDataByIndex(obj,index)
+ % Retrieve the ItemsData value for a given index
+
+ itemsData = obj.ItemsData;
+ if isnumeric(index) && isscalar(index) && ...
+ index <= numel(itemsData)
+ data = itemsData(index);
+ else
+ data = [];
+ end
+
+ end %function
+
+
+ function onAddButton(obj)
+
+ % Toggle mode ON
+ obj.IsAddingNewItem = true;
+
+ % Configure the edit field
+ obj.EditField.Value = obj.NewItemName;
+ drawnow
+ obj.EditField.focus();
+
+ % Attach figure listeners to stop editing in special
+ % circumstances
+ obj.attachStopEditingListeners();
+
+ end %function
+
+
+ function onRenameButton(obj)
+
+ % Toggle mode ON
+ obj.IsRenamingItem = true;
+
+ % Get the item being edited
+ item = obj.Items(obj.Index);
+
+ % Configure the edit field
+ obj.EditField.Value = item;
+ drawnow
+ obj.EditField.focus();
+
+ % Attach figure listeners to stop editing in special
+ % circumstances
+ obj.attachStopEditingListeners();
+
+ end %function
+
+
+ function onRemoveButton(obj)
+ % Removes selcted item
+
+ % Data for this event
+ action = "Removed";
+ index = obj.Index;
+ item = obj.Items(index);
+ data = obj.getItemDataByIndex(index);
+
+ % Remove the item from the list
+ obj.Items(index) = [];
+ if index <= numel(obj.ItemsData)
+ obj.ItemsData(index) = [];
+ end
+
+ % Prepare event data
+ evtOut = wt.eventdata.ListManagerEventData();
+ evtOut.Action = action;
+ evtOut.Item = item;
+ evtOut.ItemData = data;
+ evtOut.Index = index;
+
+ % Notify listeners
+ notify(obj,"ItemsChanged",evtOut);
+
+ end %function
+
+
+ function attachStopEditingListeners(obj)
+ % Attach listeners to enable stop editing under special
+ % circumstances
+
+ % Find the figure
+ fig = ancestor(obj,'figure');
+
+ % If no figure, just exit
+ if isempty(fig)
+ return
+ end
+
+ % Create listeners
+ obj.StopEditingListeners = [
+ listener(fig,"WindowKeyPress",@(src,evt)obj.onWindowKeyPress(evt))
+ listener(fig,"WindowMousePress",@(src,evt)obj.onWindowMousePress(evt))
+ ];
+
+ end %function
+
+
+ function onWindowKeyPress(obj,evt)
+ % Triggered on key presses while editing
+
+ % If the user pressed Escape, cancel editing
+ if strcmp(evt.Key, 'escape')
+
+ % Toggle mode OFF
+ obj.IsRenamingItem = false;
+ obj.IsAddingNewItem = false;
+
+ end
+
+ end %function
+
+
+ function onWindowMousePress(obj,evt)
+ % Triggered on mouse presses while editing
+
+ % If the user clicked anywhere except the edit field, cancel
+ % editing.
+ if ~isequal(evt.HitObject, obj.EditField)
+
+ % Toggle mode OFF
+ obj.IsRenamingItem = false;
+ obj.IsAddingNewItem = false;
+
+ end
+
+
+ end %function
+
+
+ function updateEnableableComponents(obj)
+ % Handle changes to Enable flag
+
+ % What item is selected?
+ hasEntries = ~isempty(obj.Items);
+ valIdx = obj.Index;
+
+ % Can we add an item?
+ isEditMode = obj.IsAddingNewItem || obj.IsRenamingItem;
+ canAddItem = obj.Enable && ~isEditMode;
+
+ % Can we rename or remove given the current selection?
+ canRenameItem = obj.Enable && hasEntries && ...
+ obj.AllowItemRename(valIdx) && ~isEditMode;
+ canRemoveItem = obj.Enable && hasEntries && ...
+ obj.AllowItemRemove(valIdx) && ~isEditMode;
+
+ % Update buttons
+ obj.AddButton.Enable = canAddItem;
+ obj.RenameButton.Enable = canRenameItem;
+ obj.RemoveButton.Enable = canRemoveItem;
+
+ % Update fields
+ obj.EditField.Enable = obj.Enable;
+ obj.DropDown.Enable = obj.Enable && hasEntries;
+
+ end %function
+
+
+ function updateButtonVisibilities(obj)
+ % Update button visibilities
+
+ if obj.AllowRename && obj.AllowRemove
+
+ obj.Grid.ColumnWidth = {'1x',30,30,30};
+
+ obj.RenameButton.Parent = obj.Grid;
+ obj.RenameButton.Layout.Column = 3;
+ obj.RenameButton.Layout.Row = 1;
+
+ obj.RemoveButton.Parent = obj.Grid;
+ obj.RemoveButton.Layout.Column = 4;
+ obj.RemoveButton.Layout.Row = 1;
+
+ elseif obj.AllowRename
+
+ obj.RemoveButton.Parent(:) = [];
+
+ obj.Grid.ColumnWidth = {'1x',30,30};
+
+ obj.RenameButton.Parent = obj.Grid;
+ obj.RenameButton.Layout.Column = 3;
+ obj.RenameButton.Layout.Row = 1;
+
+ elseif obj.AllowRemove
+
+ obj.RenameButton.Parent(:) = [];
+
+ obj.Grid.ColumnWidth = {'1x',30,30};
+
+ obj.RemoveButton.Parent = obj.Grid;
+ obj.RemoveButton.Layout.Column = 3;
+ obj.RemoveButton.Layout.Row = 1;
+
+ else
+
+ obj.RenameButton.Parent(:) = [];
+ obj.RemoveButton.Parent(:) = [];
+
+ obj.Grid.ColumnWidth = {'1x',30};
+
+ end
+
+ end %function
+
+ end %methods
+
+end % classdef
\ No newline at end of file
diff --git a/widgets/+wt/ListSelector.m b/widgets/+wt/ListSelector.m
index 9de02ae..48598d5 100644
--- a/widgets/+wt/ListSelector.m
+++ b/widgets/+wt/ListSelector.m
@@ -1,12 +1,17 @@
classdef ListSelector < matlab.ui.componentcontainer.ComponentContainer & ...
- wt.mixin.BackgroundColorable & wt.mixin.Enableable &...
- wt.mixin.FontStyled & wt.mixin.ButtonColorable &...
- wt.mixin.FieldColorable & wt.mixin.PropertyViewable
-
+ wt.mixin.BackgroundColorable & ...
+ wt.mixin.ButtonColorable &...
+ wt.mixin.Enableable & ...
+ wt.mixin.FieldColorable & ...
+ wt.mixin.FontStyled & ...
+ wt.mixin.Orderable & ...
+ wt.mixin.PropertyViewable
+
% Select from an array of items and add them to a list
% Copyright 2020-2023 The MathWorks Inc.
+ %RJ - add unit test for ValueIndex vs SelectedIndex
%% Events
events (HasCallbackProperty, NotifyAccess = protected)
@@ -49,7 +54,7 @@
properties (AbortSet, Dependent)
% Indices of displayed items that are currently added to the list
- SelectedIndex (1,:)
+ ValueIndex (1,:)
% The current selection
Value (1,:)
@@ -60,6 +65,14 @@
end %properties
+ properties (AbortSet, Dependent, Hidden)
+
+ % Indices of displayed items that are currently added to the list (for backward compatibility - use ValueIndex instead)
+ SelectedIndex (1,:)
+
+ end %properties
+
+
properties (AbortSet, Dependent, UsedInUpdate = false)
% Width of the buttons
@@ -81,7 +94,7 @@
%% Internal Properties
properties (Transient, NonCopyable, Hidden, SetAccess = protected)
-
+
% The ListBox control
ListBox (1,1) matlab.ui.control.ListBox
@@ -187,20 +200,22 @@ function updateEnables(obj)
% What is selected?
selIdx = obj.SelectedIndex;
+ numRows = numel(selIdx);
% Highlighted selection in list?
hiliteIdx = obj.getListBoxSelectedIndex();
+ % Should the sort buttons be enabled?
+ [backEnabled, fwdEnabled] = obj.areOrderButtonsEnabled(numRows, hiliteIdx);
+
% How many items selected into list
- numRows = numel(selIdx);
- numHilite = numel(hiliteIdx);
% Toggle button enables
obj.ListButtons.ButtonEnable = [
obj.AllowDuplicates || ( numel(selIdx) < numel(obj.Items) ) %Add Button
~isempty(hiliteIdx) % Delete Button
- numHilite && ( hiliteIdx(end) > numHilite ) %Up Button
- numHilite && ( hiliteIdx(1) <= (numRows - numHilite) ) %Down Button
+ backEnabled %Up Button
+ fwdEnabled %Down Button
];
end %if obj.Enable
@@ -317,12 +332,16 @@ function promptToAddListItems(obj)
function selIdx = getListBoxSelectedIndex(obj)
% Get the current selected row indices in the listbox
- warnState = warning('off','MATLAB:structOnObject');
- s = struct(obj.ListBox);
- warning(warnState);
- selIdx = s.SelectedIndex;
- if isequal(selIdx, -1)
- selIdx = [];
+ if isMATLABReleaseOlderThan("R2023b")
+ warnState = warning('off','MATLAB:structOnObject');
+ s = struct(obj.ListBox);
+ warning(warnState);
+ selIdx = s.SelectedIndex;
+ if isequal(selIdx, -1)
+ selIdx = [];
+ end
+ else
+ selIdx = obj.ListBox.ValueIndex;
end
end %function
@@ -357,81 +376,27 @@ function shiftListBoxIndex(obj, shift)
% Shift selected items up/down within a listbox
% This assumes ItemsData contains unique values
- % What is the current order?
- selIdx = obj.getListBoxSelectedIndex();
-
- % Make indices to all items as they are now
- idxNew = 1:numel(obj.ListBox.Items);
- idxOld = idxNew;
-
- % Find the last stable item that doesn't move
- [~,idxStable] = setdiff(idxNew, selIdx, 'stable');
- if ~isempty(idxStable)
- idxFirstStable = idxStable(1);
- idxLastStable = idxStable(end);
- else
- idxFirstStable = inf;
- idxLastStable = 0;
- end
-
- % Which way do we loop?
- if shift > 0 %Shift to end
-
- for idxToMove = numel(selIdx):-1:1
-
- % Calculate if there's room to move this item
- idxThisBefore = selIdx(idxToMove);
- thisShift = max( min(idxLastStable-idxThisBefore, shift), 0 );
-
- % Where does this item move from/to
- idxThisAfter = idxThisBefore + thisShift;
-
- % Where do other items move from/to
- idxOthersBefore = selIdx(idxToMove)+1:1:idxThisAfter;
- idxOthersAfter = idxOthersBefore - thisShift;
-
- % Move the items
- idxNew([idxThisAfter idxOthersAfter]) = idxNew([idxThisBefore idxOthersBefore]);
-
- end
-
- elseif shift < 0 %Shift to start
-
- for idxToMove = 1:numel(selIdx)
-
- % Calculate if there's room to move this item
- idxThisBefore = selIdx(idxToMove);
- thisShift = min( max(idxFirstStable-idxThisBefore, shift), 0 );
-
- % Where does this item move from/to
- idxThisAfter = idxThisBefore + thisShift;
-
- % Where do other items move from/to
- idxOthersBefore = idxThisAfter:1:selIdx(idxToMove)-1;
- idxOthersAfter = idxOthersBefore - thisShift;
-
- % Move the items
- idxNew([idxThisAfter idxOthersAfter]) = idxNew([idxThisBefore idxOthersBefore]);
-
- end
+ % What is the current order and total items?
+ idxSel = obj.getListBoxSelectedIndex();
+ numItems = numel(obj.ListBox.Items);
- end %if shift > 0
+ % Shift the list indices
+ [idxNew, idxSelAfter] = obj.shiftListIndices(shift, numItems, idxSel);
- % Was a change made?
- if ~isequal(idxOld, idxNew)
+ % Get the original value
+ oldValue = obj.Value;
- % Get the original value
- oldValue = obj.Value;
+ % Make the shift
+ obj.ListBox.Items = obj.ListBox.Items(idxNew);
+ obj.ListBox.ItemsData = obj.ListBox.ItemsData(idxNew);
+ % obj.ListBox.Selection = idxSelAfter;
- % Make the shift
- obj.ListBox.Items = obj.ListBox.Items(idxNew);
- obj.ListBox.ItemsData = obj.ListBox.ItemsData(idxNew);
-
- % Trigger event
- evtOut = wt.eventdata.ValueChangedData(obj.Value, oldValue);
- notify(obj,"ValueChanged",evtOut);
+ % Trigger event
+ evtOut = wt.eventdata.ValueChangedData(obj.Value, oldValue);
+ notify(obj,"ValueChanged",evtOut);
- end %if
+ % Update buttons
+ obj.updateEnables()
end %function
@@ -445,11 +410,21 @@ function shiftListBoxIndex(obj, shift)
function value = get.SelectedIndex(obj)
value = obj.ListBox.ItemsData;
end
+
function set.SelectedIndex(obj,value)
obj.ListBox.Items = obj.Items(value);
obj.ListBox.ItemsData = value;
end
+ function value = get.ValueIndex(obj)
+ value = obj.ListBox.ItemsData;
+ end
+
+ function set.ValueIndex(obj,value)
+ obj.ListBox.Items = obj.Items(value);
+ obj.ListBox.ItemsData = value;
+ end
+
function value = get.Value(obj)
if isempty(obj.ItemsData)
value = obj.Items(:,obj.ListBox.ItemsData);
@@ -457,6 +432,7 @@ function shiftListBoxIndex(obj, shift)
value = obj.ItemsData(:,obj.ListBox.ItemsData);
end
end
+
function set.Value(obj,value)
if isempty(value)
obj.SelectedIndex = [];
@@ -486,6 +462,7 @@ function shiftListBoxIndex(obj, shift)
value = obj.ItemsData(:,selIdx);
end
end
+
function set.HighlightedValue(obj,value)
if isempty(obj.ItemsData)
[~, obj.ListBox.Value] = ismember(value, obj.Items);
@@ -497,6 +474,7 @@ function shiftListBoxIndex(obj, shift)
function value = get.ButtonWidth(obj)
value = obj.Grid.ColumnWidth{2};
end
+
function set.ButtonWidth(obj,value)
obj.Grid.ColumnWidth{2} = value;
end
diff --git a/widgets/+wt/NumericArray.m b/widgets/+wt/NumericArray.m
new file mode 100644
index 0000000..590a051
--- /dev/null
+++ b/widgets/+wt/NumericArray.m
@@ -0,0 +1,199 @@
+classdef NumericArray < matlab.ui.componentcontainer.ComponentContainer & ...
+ wt.mixin.BackgroundColorable & ...
+ wt.mixin.Enableable & ...
+ wt.mixin.FieldColorable & ...
+ wt.mixin.FontStyled & ...
+ wt.mixin.Tooltipable
+ % Set of N numeric edit fields for small numeric arrays
+
+ % Copyright 2024 The MathWorks Inc.
+
+ %RJ - Need unit tests
+ %RJ - need to limit max array size and incorporate pagination or
+ %similar
+ %RJ - Improve error if a restriction needs enforcement. Like display a
+ % message but still accept their input?
+
+ %% Public properties
+ properties (AbortSet)
+
+ % Values of the range (min/max)
+ Value (1,:) double = [1 2 3]
+
+ % Optional limits of the entries (min/max)
+ Limits (1,2) double = [-inf inf]
+
+ % Is the lower limit inclusive?
+ LowerLimitInclusive (1,1) matlab.lang.OnOffSwitchState = true
+
+ % Is the upper limit inclusive?
+ UpperLimitInclusive (1,1) matlab.lang.OnOffSwitchState = true
+
+ % Restriction on array order
+ Restriction (1,1) wt.enum.ArrayRestriction = "none"
+
+ % Orientation of the fields
+ Orientation (1,1) wt.enum.HorizontalVerticalState = "horizontal"
+
+ end %properties
+
+
+ %% Events
+ events (HasCallbackProperty, NotifyAccess = protected)
+
+ % Triggered on value changed, has companion callback
+ ValueChanged
+
+ end %events
+
+
+ %% Internal Properties
+ properties (Transient, NonCopyable, Hidden, SetAccess = protected)
+
+ % Grid
+ Grid (1,1) matlab.ui.container.GridLayout
+
+ % Edit fields
+ EditField (1,:) matlab.ui.control.NumericEditField
+
+ end %properties
+
+
+ %% Protected methods
+ methods (Access = protected)
+
+ function setup(obj)
+
+ % Construct Grid Layout to Manage Building Blocks
+ obj.Grid = uigridlayout(obj);
+ obj.Grid.ColumnWidth = {'1x','1x','1x'};
+ obj.Grid.RowHeight = {'1x'};
+ obj.Grid.ColumnSpacing = 5;
+ obj.Grid.RowSpacing = 5;
+ obj.Grid.Padding = 0;
+
+ % Set default size
+ obj.Position(3:4) = [100 25];
+
+ % Update the internal component lists
+ obj.BackgroundColorableComponents = obj.Grid;
+
+ end %function
+
+
+ function update(obj)
+
+ % How many fields?
+ numElements = numel(obj.Value);
+ numFields = numel(obj.EditField);
+ numRows = numel(obj.Grid.RowHeight);
+ numCols = numel(obj.Grid.ColumnWidth);
+ if numFields ~= numElements || ...
+ numRows > 1 && numElements > 1 && obj.Orientation == "horizontal" || ...
+ numCols > 1 && numElements > 1 && obj.Orientation == "vertical"
+ obj.createEditFields(numElements);
+ end
+
+ % Loop on each edit field to update it
+ for idx = 1:numElements
+
+ % Calculate limits
+ lowerLimit = obj.Limits(1);
+ upperLimit = obj.Limits(2);
+ lowerLimitInclusive = obj.LowerLimitInclusive;
+ upperLimitInclusive = obj.UpperLimitInclusive;
+
+ switch (obj.Restriction)
+
+ case wt.enum.ArrayRestriction.increasing
+
+ if idx > 1
+ lowerLimit = obj.Value(idx-1);
+ lowerLimitInclusive = false;
+ end
+
+ if idx < numElements
+ upperLimit = obj.Value(idx+1);
+ upperLimitInclusive = false;
+ end
+
+ case wt.enum.ArrayRestriction.decreasing
+
+ if idx > 1
+ upperLimit = obj.Value(idx-1);
+ upperLimitInclusive = false;
+ end
+
+ if idx < numElements
+ lowerLimit = obj.Value(idx+1);
+ lowerLimitInclusive = false;
+ end
+
+ end %switch
+
+ % Apply restrictions
+ % obj.EditField(idx).Limits = [lowerLimit upperLimit];
+ % obj.EditField(idx).LowerLimitInclusive = lowerLimitInclusive;
+ % obj.EditField(idx).UpperLimitInclusive = upperLimitInclusive;
+
+ % Update the edit fields values
+ obj.EditField(idx).Value = obj.Value(idx);
+
+ end %for
+
+ end %function
+
+
+ function createEditFields(obj, numFields)
+
+ % Delete existing content
+ delete(obj.EditField);
+ obj.EditField(:) = [];
+
+ % Configure Grid
+ gridRep = repmat({'1x'},1,numFields);
+ if obj.Orientation == wt.enum.HorizontalVerticalState.vertical
+ obj.Grid.ColumnWidth = {'1x'};
+ obj.Grid.RowHeight = gridRep;
+ else
+ obj.Grid.ColumnWidth = gridRep;
+ obj.Grid.RowHeight = {'1x'};
+ end
+
+ % Create edit fields
+ for idx = 1:numFields
+ obj.EditField(idx) = uieditfield(obj.Grid,'numeric');
+ end %for
+ set(obj.EditField,"ValueChangedFcn",@(h,e)obj.onValueChanged(e));
+
+ % Update the internal component lists
+ obj.FontStyledComponents = obj.EditField;
+ obj.FieldColorableComponents = obj.EditField;
+ obj.EnableableComponents = obj.EditField;
+ obj.TooltipableComponents = obj.EditField;
+
+ end %function
+
+
+ function onValueChanged(obj,~)
+ % Triggered on edit field interaction
+
+ % Get prior value
+ oldValue = obj.Value;
+
+ % Get new value
+ newValue = [obj.EditField.Value];
+
+ % Store new result
+ obj.Value = newValue;
+
+ % Trigger event
+ evtOut = wt.eventdata.ValueChangedData(obj.Value, oldValue);
+ notify(obj,"ValueChanged",evtOut);
+
+ end %function
+
+ end %methods
+
+
+end % classdef
\ No newline at end of file
diff --git a/widgets/+wt/ProgressBar.m b/widgets/+wt/ProgressBar.m
index 76c33c1..b379e1c 100644
--- a/widgets/+wt/ProgressBar.m
+++ b/widgets/+wt/ProgressBar.m
@@ -31,7 +31,7 @@
properties (Dependent, UsedInUpdate = false)
% Bar color
- BarColor (1,3) double {wt.validators.mustBeBetweenZeroAndOne}
+ BarColor (1,3) double {mustBeInRange(BarColor,0,1)}
end %properties
@@ -41,7 +41,7 @@
properties (SetAccess = protected, UsedInUpdate = false)
% Current progress value
- Value (1,1) double {mustBeNonnegative, mustBeLessThanOrEqual(Value,1)} = 0
+ Value (1,1) double {mustBeInRange(Value,0,1)} = 0
% Current status text displayed
StatusText (1,1) string
@@ -182,8 +182,8 @@ function cancel(obj)
drawnow
% Trigger event
- evt = wt.eventdata.ButtonPushedData(obj.CancelButton, "Cancel");
- notify(obj,"CancelPressed",evt);
+ evtOut = wt.eventdata.ButtonPushedData(obj.CancelButton, "Cancel");
+ notify(obj,"CancelPressed",evtOut);
end %function
diff --git a/widgets/+wt/RangeField.m b/widgets/+wt/RangeField.m
new file mode 100644
index 0000000..0f2f654
--- /dev/null
+++ b/widgets/+wt/RangeField.m
@@ -0,0 +1,139 @@
+classdef RangeField < matlab.ui.componentcontainer.ComponentContainer & ...
+ wt.mixin.BackgroundColorable & ...
+ wt.mixin.Enableable & ...
+ wt.mixin.FieldColorable & ...
+ wt.mixin.FontStyled & ...
+ wt.mixin.Tooltipable
+ % Pair of numeric edit fields for range selection
+
+ % Copyright 2024 The MathWorks Inc.
+
+ %RJ - Need unit tests
+ %RJ - Improve error if a restriction needs enforcement. Like display a
+ % message but still accept their input?
+
+
+ %% Public properties
+ properties (AbortSet)
+
+ % Values of the range (min/max)
+ Value (1,2) double = [0 1]
+
+ % Optional limits of the range (min/max)
+ Limits (1,2) double = [-inf inf]
+
+ % Is the lower limit inclusive?
+ LowerLimitInclusive (1,1) matlab.lang.OnOffSwitchState = true
+
+ % Is the upper limit inclusive?
+ UpperLimitInclusive (1,1) matlab.lang.OnOffSwitchState = true
+
+ end %properties
+
+
+ methods
+ function set.Value(obj,value)
+ validateattributes(value,{'double'},{'increasing'})
+ obj.Value = value;
+ end
+ function set.Limits(obj,value)
+ validateattributes(value,{'double'},{'increasing'})
+ obj.Limits = value;
+ end
+ end
+
+
+ %% Events
+ events (HasCallbackProperty, NotifyAccess = protected)
+
+ % Triggered on value changed, has companion callback
+ ValueChanged
+
+ end %events
+
+
+ %% Internal Properties
+ properties (Transient, NonCopyable, Hidden, SetAccess = protected)
+
+ % Grid
+ Grid (1,1) matlab.ui.container.GridLayout
+
+ % Edit fields
+ EditField (1,2) matlab.ui.control.NumericEditField
+
+ end %properties
+
+
+ %% Protected methods
+ methods (Access = protected)
+
+ function setup(obj)
+
+ % Construct Grid Layout to Manage Building Blocks
+ obj.Grid = uigridlayout(obj);
+ obj.Grid.ColumnWidth = {'1x','1x'};
+ obj.Grid.RowHeight = {'1x'};
+ obj.Grid.ColumnSpacing = 5;
+ obj.Grid.Padding = 0;
+
+ % Set default size
+ obj.Position(3:4) = [100 25];
+
+ % Create the edit field
+ obj.EditField = [...
+ uieditfield(obj.Grid,'numeric'), ...
+ uieditfield(obj.Grid,'numeric') ];
+ obj.EditField(1).UpperLimitInclusive = false;
+ obj.EditField(2).LowerLimitInclusive = false;
+ set(obj.EditField,"ValueChangedFcn",@(h,e)obj.onValueChanged(e));
+
+ % Update the internal component lists
+ obj.BackgroundColorableComponents = obj.Grid;
+ obj.EnableableComponents = obj.EditField;
+ obj.FieldColorableComponents = obj.EditField;
+ obj.FontStyledComponents = obj.EditField;
+ obj.TooltipableComponents = obj.EditField;
+
+ end %function
+
+
+ function update(obj)
+
+ % Update the edit field limits
+ obj.EditField(1).LowerLimitInclusive = obj.LowerLimitInclusive;
+ obj.EditField(1).Limits = [obj.Limits(1) obj.Value(2)];
+
+ obj.EditField(2).UpperLimitInclusive = obj.UpperLimitInclusive;
+ obj.EditField(2).Limits = [obj.Value(1) obj.Limits(2)];
+
+ % Update the edit field values
+ obj.EditField(1).Value = obj.Value(1);
+ obj.EditField(2).Value = obj.Value(2);
+
+ end %function
+
+
+ function onValueChanged(obj,evt)
+ % Triggered on edit field interaction
+
+ % Get prior value
+ oldValue = obj.Value;
+
+ % Get new value
+ newValue = [obj.EditField.Value];
+ index = find(evt.Source == obj.EditField, 1);
+
+ % Store new result
+ obj.Value = newValue;
+
+ % Trigger event
+ evtOut = wt.eventdata.ValueChangedData(obj.Value, oldValue,...
+ "Index", index);
+ notify(obj,"ValueChanged",evtOut);
+
+ end %function
+
+ end %methods
+
+
+end % classdef
\ No newline at end of file
diff --git a/widgets/+wt/RowEntriesTable.m b/widgets/+wt/RowEntriesTable.m
new file mode 100644
index 0000000..726ac5a
--- /dev/null
+++ b/widgets/+wt/RowEntriesTable.m
@@ -0,0 +1,482 @@
+classdef RowEntriesTable < matlab.ui.componentcontainer.ComponentContainer & ...
+ wt.mixin.BackgroundColorable & ...
+ wt.mixin.ButtonColorable &...
+ wt.mixin.Enableable & ...
+ wt.mixin.FieldColorable & ...
+ wt.mixin.FontStyled & ...
+ wt.mixin.Orderable & ...
+ wt.mixin.Tooltipable
+ % A table showing status of multiple tasks
+
+ % Copyright 2024 The MathWorks Inc.
+
+
+ %RJ - Need unit tests
+ %RJ - Connect ordering buttons and make them optional
+ %RJ - Table enable didn't work
+
+
+ %% Public properties
+ properties (AbortSet)
+
+ % Table entries
+ Data table
+
+ % Format for new table row
+ NewRowFormat (1,:) cell = {"NewRow",0}
+
+ % Indicates whether to allow sort controls
+ Sortable (1,1) matlab.lang.OnOffSwitchState = false
+
+ % Can each item be removed?
+ AllowItemRemove (1,:) logical
+
+ % Can each item be sorted/ordered?
+ AllowItemSort (1,:) logical
+
+ end %properties
+
+
+ % Accessors
+ methods
+
+ function value = get.AllowItemRemove(obj)
+ value = resize(obj.AllowItemRemove, height(obj.Data), ...
+ "FillValue", true);
+ end
+
+ function value = get.AllowItemSort(obj)
+ value = resize(obj.AllowItemSort, height(obj.Data), ...
+ "FillValue", true);
+ end
+
+ end %methods
+
+
+ %% Dependent properties
+ properties (Dependent, SetAccess = private)
+
+ StyleConfigurations
+
+ end %properties
+
+ properties (Dependent)
+
+ TableWidth
+
+ TableHeight
+
+ end %properties
+
+
+ methods
+
+ function value = get.TableWidth(obj)
+ value = obj.Grid.ColumnWidth{1};
+ end
+ function set.TableWidth(obj, value)
+ obj.Grid.ColumnWidth{1} = value;
+ end
+
+ function value = get.TableHeight(obj)
+ value = obj.Grid.RowHeight{end};
+ end
+ function set.TableHeight(obj, value)
+ obj.Grid.RowHeight{end} = value;
+ end
+
+ function value = get.StyleConfigurations(obj)
+ value = obj.Table.StyleConfigurations;
+ end
+
+ end %methods
+
+
+ %% Events
+ events (HasCallbackProperty, NotifyAccess = protected)
+
+ % Triggered on value changed, has companion callback
+ ValueChanged
+
+ end %events
+
+
+ %% Internal Properties
+ properties (Transient, NonCopyable, Hidden, SetAccess = protected)
+
+ % Grid
+ Grid matlab.ui.container.GridLayout
+
+ % Table for entries
+ Table matlab.ui.control.Table
+
+ % Buttons
+ AddButton matlab.ui.control.Button
+ RemoveButton matlab.ui.control.Button
+ UpButton matlab.ui.control.Button
+ DownButton matlab.ui.control.Button
+
+ end %properties
+
+
+ %% Public methods
+ methods
+
+ function addStyle(obj,s,tableTarget,tableIndex)
+ % Add a style to the table
+
+ arguments (Input)
+ obj (1,1) wt.RowEntriesTable
+ s (1,1) matlab.ui.style.Style
+ tableTarget (1,1) string {mustBeMember(tableTarget,...
+ ["table","row","column","cell"])} = "table"
+ tableIndex = ""
+ end
+
+ % Add the style to the internal table
+ addStyle(obj.Table, s, tableTarget, tableIndex)
+
+ end %function
+
+
+ function removeStyle(obj,orderNum)
+ % Add a style to the table
+
+ arguments (Input)
+ obj (1,1) wt.RowEntriesTable
+ orderNum = []
+ end
+
+ % Remove the style from the internal table
+ if isempty(orderNum)
+ removeStyle(obj.Table)
+ else
+ removeStyle(obj.Table, orderNum)
+ end
+
+ end %function
+
+ end %methods
+
+
+ %% Protected methods
+ methods (Access = protected)
+
+ function setup(obj)
+
+ % Construct Grid Layout to Manage Building Blocks
+ obj.Grid = uigridlayout(obj,[5,2]);
+ obj.Grid.ColumnWidth = {'1x',30};
+ obj.Grid.RowHeight = {30,30,30,30,'1x'};
+ obj.Grid.Padding = 0;
+ obj.Grid.ColumnSpacing = 2;
+ obj.Grid.RowSpacing = 5;
+
+ % Set default size
+ obj.Position(3:4) = [300 200];
+
+ % Create the Table
+ obj.Table = uitable(obj.Grid);
+ obj.Table.ColumnEditable = true;
+ obj.Table.SelectionType = 'row';
+ obj.Table.Layout.Column = 1;
+ obj.Table.Layout.Row = [1 5];
+ obj.Table.CellEditCallback = @(src,evt)obj.onCellEdited(evt);
+ obj.Table.SelectionChangedFcn = @(src,evt)obj.onSelectionChanged(evt);
+
+ % Create the buttons
+ obj.AddButton = uibutton(obj.Grid);
+ obj.AddButton.Icon = "addYellow_24.png";
+ obj.AddButton.Text = "";
+ obj.AddButton.Layout.Column = 2;
+ obj.AddButton.Layout.Row = 1;
+ obj.AddButton.ButtonPushedFcn = @(src,evt)obj.onAddButton(evt);
+
+ obj.RemoveButton = uibutton(obj.Grid);
+ obj.RemoveButton.Icon = "delete_24.png";
+ obj.RemoveButton.Text = "";
+ obj.RemoveButton.Layout.Column = 2;
+ obj.RemoveButton.Layout.Row = 2;
+ obj.RemoveButton.ButtonPushedFcn = @(src,evt)obj.onRemoveButton(evt);
+
+ obj.UpButton = uibutton(obj.Grid);
+ obj.UpButton.Icon = "up_24.png";
+ obj.UpButton.Text = "";
+ obj.UpButton.Layout.Column = 2;
+ obj.UpButton.Layout.Row = 3;
+ obj.UpButton.ButtonPushedFcn = @(src,evt)obj.onUpButton(evt);
+
+ obj.DownButton = uibutton(obj.Grid);
+ obj.DownButton.Icon = "down_24.png";
+ obj.DownButton.Text = "";
+ obj.DownButton.Layout.Column = 2;
+ obj.DownButton.Layout.Row = 4;
+ obj.DownButton.ButtonPushedFcn = @(src,evt)obj.onDownButton(evt);
+
+ % Update the internal component lists
+ obj.BackgroundColorableComponents = obj.Grid;
+ obj.ButtonColorableComponents = [obj.AddButton, ...
+ obj.RemoveButton, obj.UpButton, obj.DownButton];
+ obj.EnableableComponents = [obj.Table, obj.AddButton, ...
+ obj.RemoveButton, obj.UpButton, obj.DownButton];
+ obj.FieldColorableComponents = obj.Table;
+ obj.FontStyledComponents = obj.Table;
+ obj.TooltipableComponents = obj.Table;
+
+ end %function
+
+
+ function update(obj)
+
+ % Get the data
+ data = obj.Data;
+
+ % If empty and no vars, use NewRowFormat
+ if width(data) == 0
+ data = cell(0, numel(obj.NewRowFormat));
+ end
+
+ % Update the table content
+ obj.Table.Data = data;
+
+ % Update sort buttons
+ obj.updateSortButtons();
+
+ end %function
+
+
+ function updateEnableableComponents(obj)
+ % Handle changes to Enable flag
+
+ % Update sort buttons
+ obj.updateSortButtons();
+
+ % Update fields
+ obj.Table.Enable = string(obj.Enable);
+
+ end %function
+
+
+ function updateSortButtons(obj)
+
+ % Which rows are selected?
+ numItems = height(obj.Data);
+ idxSel = obj.Table.Selection;
+
+ % Which items allow removal?
+ allowRemove = obj.AllowItemRemove(idxSel);
+ canRemove = obj.Enable && ~isempty(idxSel) && all(allowRemove);
+
+ % Which items allow sorting
+ alloItemSort = obj.AllowItemSort;
+ % canSort = obj.Enable && obj.Sortable && all(allowSort(idxSel));
+
+ % Toggle sorting button visibility
+ obj.UpButton.Visible = obj.Sortable;
+ obj.DownButton.Visible = obj.Sortable;
+
+ % Update button enables
+ obj.AddButton.Enable = obj.Enable;
+ obj.RemoveButton.Enable = canRemove;
+ if obj.Sortable
+ [backEnabled, fwdEnabled] = obj.areOrderButtonsEnabled(...
+ numItems, idxSel, alloItemSort);
+ obj.UpButton.Enable = backEnabled;
+ obj.DownButton.Enable = fwdEnabled;
+ end
+
+ end %function
+
+
+ function onAddButton(obj,~)
+ % Triggered on button pushed
+
+ % Get old/new data
+ oldData = obj.Data;
+ newRowData = obj.NewRowFormat;
+ numOldRows = height(oldData);
+ numNewRows = numOldRows + 1;
+ numOldCols = width(oldData);
+
+ % Determine placement
+ selRowIdx = obj.Table.Selection;
+ if isempty(selRowIdx)
+ selRowIdx = numOldRows;
+ end
+ newRowIdx = selRowIdx + 1;
+
+ % Does table already have columns?
+ if numOldCols > 0
+
+ % New row must be same width as table
+ newRowData = resize(newRowData, [1 numOldCols]);
+
+ % Add content to table
+ newData = paddata(oldData,numNewRows,"FillValue",newRowData);
+
+ end
+
+ % Order new data
+ newData = [
+ newData(1:selRowIdx,:)
+ newData(end,:)
+ newData(newRowIdx:numOldRows,:)
+ ];
+
+ % Prepare event data
+ evtOut = wt.eventdata.RowEntriesTableChangedData();
+ evtOut.Action = "RowAdded";
+ evtOut.Row = newRowIdx;
+ evtOut.Column = 1:size(newData,2);
+ evtOut.Value = newData(newRowIdx,:);
+ evtOut.PreviousValue = oldData([],:);
+ evtOut.TableData = newData;
+ evtOut.PreviousTableData = oldData;
+
+ % Store new result and select it
+ obj.Table.Data = newData;
+ obj.Table.Selection = newRowIdx;
+ obj.Data = newData;
+
+ % Trigger event
+ notify(obj,"ValueChanged",evtOut);
+
+ end %function
+
+
+ function onRemoveButton(obj,~)
+ % Triggered on button pushed
+
+ % Get the selection and total rows
+ selRows = obj.Table.Selection;
+ if isempty(selRows)
+ return
+ end
+ numSelRows = numel(selRows);
+ numRows = height(obj.Data);
+
+ % Prepare the new data
+ oldData = obj.Data;
+ removedData = oldData(selRows,:);
+ newData = oldData;
+ newData(selRows,:) = [];
+
+ % Calculate new row selection
+ newSelRows = selRows;
+ newNumRows = numRows - numSelRows;
+ isOver = newSelRows > newNumRows;
+ newSelRows(isOver) = newSelRows(isOver) - numSelRows;
+ newSelRows(newSelRows < 1 | newSelRows > newNumRows) = [];
+ newSelRows = unique(newSelRows);
+
+ % Prepare event data
+ evtOut = wt.eventdata.RowEntriesTableChangedData();
+ evtOut.Action = "RowRemoved";
+ evtOut.Row = selRows;
+ evtOut.Column = 1:size(oldData,2);
+ evtOut.Value = removedData([],:);
+ evtOut.PreviousValue = removedData;
+ evtOut.TableData = newData;
+ evtOut.PreviousTableData = oldData;
+
+ % Store new result and update selection
+ obj.Table.Data = newData;
+ obj.Table.Selection = newSelRows;
+ obj.Data = newData;
+
+ % Trigger event
+ notify(obj,"ValueChanged",evtOut);
+
+ end %function
+
+
+ function onUpButton(obj,~)
+ % Triggered on button pushed
+
+ obj.shiftSelectedRows(-1);
+
+ end %function
+
+
+ function onDownButton(obj,~)
+ % Triggered on button pushed
+
+ obj.shiftSelectedRows(1);
+
+ end %function
+
+
+ function shiftSelectedRows(obj,shift)
+ % Shift the selected rows up/down
+
+ numItems = height(obj.Data);
+ idxSel = obj.Table.Selection;
+
+ % Shift the list indices
+ [idxNew, idxSelAfter] = obj.shiftListIndices(shift, numItems, idxSel);
+
+ % Get the original value
+ oldData = obj.Table.Data;
+
+ % Make the shift
+ newData = oldData(idxNew,:);
+
+ % Prepare event data
+ evtOut = wt.eventdata.RowEntriesTableChangedData();
+ evtOut.Action = "RowOrdered";
+ % evtOut.Row =
+ % evtOut.Column =
+ evtOut.Value = idxNew;
+ % evtOut.PreviousValue =
+ evtOut.TableData = newData;
+ evtOut.PreviousTableData = oldData;
+
+ % Update the table
+ obj.Data = newData;
+ obj.Table.Selection = idxSelAfter;
+
+ % Trigger event
+ notify(obj,"ValueChanged",evtOut);
+
+ % Update buttons
+ obj.updateSortButtons()
+
+ end %function
+
+
+ function onSelectionChanged(obj,~)
+ % Triggered on table selection changed
+
+ % Update button enables
+ obj.updateEnableableComponents();
+
+ end %function
+
+
+ function onCellEdited(obj,evt)
+ % Triggered on cell edited
+
+ % Prepare event data
+ evtOut = wt.eventdata.RowEntriesTableChangedData();
+ evtOut.Action = "CellEdited";
+ evtOut.Row = evt.Indices(1);
+ evtOut.Column = evt.Indices(2);
+ evtOut.Value = evt.NewData;
+ evtOut.PreviousValue = evt.PreviousData;
+ evtOut.EditValue = evt.EditData;
+ evtOut.TableData = [obj.Table.Data];
+ evtOut.PreviousTableData = obj.Data;
+ evtOut.Error = evt.Error;
+
+ % Store new result
+ obj.Data = [obj.Table.Data];
+
+ % Trigger event
+ notify(obj,"ValueChanged",evtOut);
+
+ end %function
+
+ end %methods
+
+
+end % classdef
\ No newline at end of file
diff --git a/widgets/+wt/SliderCheckboxGroup.m b/widgets/+wt/SliderCheckboxGroup.m
index 741e07c..db949f0 100644
--- a/widgets/+wt/SliderCheckboxGroup.m
+++ b/widgets/+wt/SliderCheckboxGroup.m
@@ -175,42 +175,42 @@ function updateEnableableComponents(obj)
end %function
- function onSliderChanged(obj,e)
+ function onSliderChanged(obj,evt)
% Triggered on button pushed
% What changed?
- newValue = e.Value;
- idx = find(e.Source == obj.Slider, 1);
+ newValue = evt.Value;
+ idx = find(evt.Source == obj.Slider, 1);
% Update the state
obj.Value(idx) = newValue;
% Create event data
- evt = wt.eventdata.SliderCheckboxChangedData(...
+ evtOut = wt.eventdata.SliderCheckboxChangedData(...
obj.Name(idx), idx, "Value", obj.State(idx), obj.Value(idx) );
% Trigger event
- notify(obj,"ValueChanged",evt);
+ notify(obj,"ValueChanged",evtOut);
end %function
- function onCheckboxChanged(obj,e)
+ function onCheckboxChanged(obj,evtOut)
% Triggered on button pushed
% What changed?
- newValue = e.Value;
- idx = find(e.Source == obj.Checkbox, 1);
+ newValue = evtOut.Value;
+ idx = find(evtOut.Source == obj.Checkbox, 1);
% Update the state
obj.State(idx) = newValue;
% Create event data
- evt = wt.eventdata.SliderCheckboxChangedData(...
+ evtOut = wt.eventdata.SliderCheckboxChangedData(...
obj.Name(idx), idx, "State", obj.State(idx), obj.Value(idx) );
% Trigger event
- notify(obj,"ValueChanged",evt);
+ notify(obj,"ValueChanged",evtOut);
end %function
diff --git a/widgets/+wt/SliderSpinner.m b/widgets/+wt/SliderSpinner.m
index 8b97dde..d7fe09a 100644
--- a/widgets/+wt/SliderSpinner.m
+++ b/widgets/+wt/SliderSpinner.m
@@ -155,11 +155,11 @@ function updateLayout(obj)
end %function
- function onSliderChanged(obj,e)
+ function onSliderChanged(obj,evt)
% Triggered on button pushed
% What changed?
- newValue = e.Value;
+ newValue = evt.Value;
oldValue = obj.Spinner.Value;
% Round?
@@ -178,16 +178,16 @@ function onSliderChanged(obj,e)
end
% Trigger event ("ValueChanged" or "ValueChanging")
- notify(obj, e.EventName, evtOut);
+ notify(obj, evt.EventName, evtOut);
end %function
- function onSliderChanging(obj,e)
+ function onSliderChanging(obj,evt)
% Triggered on button pushed
% What changed?
- newValue = e.Value;
+ newValue = evt.Value;
oldValue = obj.Spinner.Value;
% Round?
@@ -203,7 +203,7 @@ function onSliderChanging(obj,e)
obj.Spinner.Value = newValue;
% Trigger event ("ValueChanging")
- notify(obj, e.EventName, evtOut);
+ notify(obj, evt.EventName, evtOut);
end %function
diff --git a/widgets/+wt/TaskStatusTable.m b/widgets/+wt/TaskStatusTable.m
index ba2aed1..de3f4ff 100644
--- a/widgets/+wt/TaskStatusTable.m
+++ b/widgets/+wt/TaskStatusTable.m
@@ -34,7 +34,7 @@
% Selection color
SelectionColor (1,3) double ...
- {wt.validators.mustBeBetweenZeroAndOne} = [.8 .8 1];
+ {mustBeInRange(SelectionColor,0,1)} = [.8 .8 1];
% Enables button and status row to display
ShowButtonRow (1,1) matlab.lang.OnOffSwitchState = true
diff --git a/widgets/examples/ZooAppExample.m b/widgets/examples/ZooAppExample.m
new file mode 100644
index 0000000..4b1b1b9
--- /dev/null
+++ b/widgets/examples/ZooAppExample.m
@@ -0,0 +1,30 @@
+function varargout = ZooAppExample(varargin)
+% Launch app
+
+% Default
+varargout{1} = [];
+
+
+%% Check dependencies
+if ~exist("wt.apps.BaseApp","class")
+ error("Widgets Toolbox is required to run this app. " + ...
+ "Install from Add-Ons browser.");
+end
+
+
+%% Bugfixes
+
+
+
+%% Instantiate the application
+app = wtexample.app.ContextualViewExample(varargin{:});
+
+
+%% Load default session
+app.loadSession(which("robyn_session.mat"))
+
+
+%% Collect output
+if nargout
+ varargout{1} = app;
+end
diff --git a/widgets/examples/apps/contextual_view/+wtexample/+app/@ContextualViewExample/ContextualViewExample.m b/widgets/examples/apps/contextual_view/+wtexample/+app/@ContextualViewExample/ContextualViewExample.m
new file mode 100644
index 0000000..8446381
--- /dev/null
+++ b/widgets/examples/apps/contextual_view/+wtexample/+app/@ContextualViewExample/ContextualViewExample.m
@@ -0,0 +1,316 @@
+classdef ContextualViewExample < wt.apps.BaseMultiSessionApp
+ % Example app showing a tree with contextual views
+
+ % Copyright 2024 The MathWorks Inc.
+
+
+ %% Internal properties
+ properties (Hidden, Transient, SetAccess = private)
+
+ % Toolbar at top of the app window
+ Toolbar wt.Toolbar
+
+ % Navigation tree on the left of the app
+ Tree matlab.ui.container.Tree
+
+ % Contextual pane to show view/controller for selected tree node
+ ContextualView wt.ContextualView
+
+ ExhibitAddButton
+ ExhibitDeleteButton
+
+
+ % NewSelection (:,1) wt.model.BaseModel {mustBeScalarOrEmpty}
+
+ end %properties
+
+
+ %% Protected Methods
+ methods (Access = protected)
+
+ % Declarations for methods in separate files
+ updateTreeHierarchy(app)
+
+
+ function session = createNewSession(app)
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
+ session = wtexample.model.Session;
+
+ end %function
+
+
+ function setup(app)
+ % Initial setup / creation of the app
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
+ % Set the name
+ app.Name = "Contextual View Example";
+
+ % Configure the main grid
+ app.Grid.Padding = 10;
+ app.Grid.ColumnSpacing = 10;
+ app.Grid.RowHeight = {90,'1x'};
+ app.Grid.ColumnWidth = {250,'1x'};
+
+ % Create toolbar file section
+ fileSection = wt.toolbar.HorizontalSection();
+ fileSection.Title = "FILE";
+ fileSection.addButton("new_24.png","New");
+ fileSection.addButton("open_24.png","Open");
+ fileSection.addButton("saveClean_24.png","Save");
+ fileSection.addButton("saveClean_24.png","Save As");
+ fileSection.addButton("import_24.png","Import");
+ fileSection.ButtonPushedFcn = @(~,evt)onFileButtonPushed(app,evt);
+
+ % Create exhibit section
+ exhibitSection = wt.toolbar.HorizontalSection();
+ exhibitSection.Title = "EXHIBIT";
+ app.ExhibitAddButton = exhibitSection.addButton("addGreen_24.png","New");
+ app.ExhibitDeleteButton = exhibitSection.addButton("delete_24.png","Delete");
+ exhibitSection.ButtonPushedFcn = @(~,evt)onExhibitButtonPushed(app,evt);
+
+ % Add toolbar
+ app.Toolbar = wt.Toolbar(app.Grid);
+ app.Toolbar.DividerColor = [.8 .8 .8];
+ app.Toolbar.Layout.Row = 1;
+ app.Toolbar.Layout.Column = [1 2];
+ app.Toolbar.Section = [fileSection, exhibitSection];
+
+ % Create navigation tree
+ app.Tree = uitree(app.Grid);
+ app.Tree.SelectionChangedFcn = @(~,evt)onTreeSelection(app,evt);
+ app.Tree.FontSize = 14;
+ app.Tree.Layout.Row = 2;
+ app.Tree.Layout.Column = 1;
+
+ % Create contextual view pane
+ app.ContextualView = wt.ContextualView(app.Grid);
+ app.ContextualView.Layout.Row = 2;
+ app.ContextualView.Layout.Column = 2;
+
+ end %function
+
+
+ function update(app)
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
+ % Update the tree hierarchy
+ app.updateTreeHierarchy()
+
+ % If a new selection is requested, change it now
+ % if isscalar(app.NewSelection) && isvalid(app.NewSelection)
+ %
+ % end
+
+ % Select the new node
+ % app.Tree.SelectedNodes = app.Tree.Children(end);
+ %RJ - need to add selection states in the session! That
+ %way, update can properly select the tree choice.
+
+ end %function
+
+
+ function session = getSessionFromTreeNode(app,node)
+ % Determines the session of the selected tree node
+
+ arguments
+ app (1,1) wt.apps.BaseApp
+ node (1,1) matlab.ui.container.TreeNode
+ end
+
+ nodeData = node.NodeData;
+ if ~isscalar(nodeData)
+
+ % Shouldn't happen, but return empty
+ session = wtexample.model.Session.empty(1,0);
+
+ elseif isa(nodeData, "wt.model.BaseSession")
+
+ % Found it!
+ session = nodeData;
+
+ elseif isa(nodeData, "wt.model.BaseModel") && ~isempty(node.Parent)
+
+ % Look to parent
+ session = app.getSessionFromTreeNode(node.Parent);
+
+ else
+
+ % Can't find, return empty
+ session = getEmptySession();
+
+ end %if
+
+ end %function
+
+
+ function onTreeSelection(app,evt)
+ % On selected tree node changed
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
+ % What node(s) are selected?
+ selNode = evt.SelectedNodes;
+
+ % Is it a scalar selection?
+ if isscalar(selNode)
+
+ % Set the selected session
+ session = app.getSessionFromTreeNode(selNode);
+ app.selectSession(session);
+
+ % Get the data model, which is attached to this node
+ model = selNode.NodeData;
+ modelClass = class(model);
+
+ % Which view to launch?
+ viewClass = replace(modelClass,".model.",".viewcontroller.");
+
+ % Launch the contextual pane for the selected data type
+ app.ContextualView.launchView(viewClass, model);
+
+ else
+
+ % Clear the contextual pane
+ app.ContextualView.clearView();
+
+ end %if
+
+ end %function
+
+
+ function onFileButtonPushed(app,evt)
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
+ % Get the selected session
+ session = app.SelectedSession;
+
+ switch evt.Text
+
+ case 'New'
+
+ % Add a new session
+ session = app.newSession();
+
+ % Select the new session
+ if ~isempty(session)
+ app.selectSession(session);
+ end
+
+ case 'Open'
+
+ % Prompt and load a session
+ session = app.loadSession();
+
+ % Select the new session
+ if ~isempty(session)
+ app.selectSession(session);
+ end
+
+ case 'Save'
+
+ % Save the session
+ app.saveSession(false, session);
+
+ case 'Save As'
+
+ % Save the session as different file
+ app.saveSession(true, session);
+
+ case 'Import'
+
+ filter = ["*.xlsx","Excel Document"];
+ title = "Select spreadsheet to import";
+ filePath = app.promptToLoad(filter,title);
+
+ if isfile(filePath)
+ try
+ session.importManifest(filePath);
+ catch err
+ message = sprintf("Unable to load " + ...
+ "manifest: %s\n\n%s",filePath,err.message);
+ app.throwError(message)
+ end
+ end
+
+ end %switch
+
+ % Update the app
+ app.update()
+
+ end %function
+
+
+ function onExhibitButtonPushed(app,evt)
+
+ % Show output if Debug is on
+ app.displayDebugText();
+
+ % Get the selected session
+ session = app.SelectedSession;
+
+ % Which button was pressed?
+ switch evt.Text
+
+ case 'New'
+
+ % Add a new exhibit
+ newItem = wtexample.model.Exhibit;
+ newItem.Name = "New Exhibit";
+
+ % Add the new exhibit
+ session.Exhibit(end+1) = newItem;
+
+ % Indicate the new exhibit should be selected at the next update
+ % app.NewSelection = newItem;
+
+ case 'Delete'
+
+ % Get the selected node
+ selNode = app.Tree.SelectedNodes;
+
+ % Return if nonscalar node
+ if ~isscalar(selNode) || ~isvalid(selNode)
+ return
+ end
+
+ % Get the data model, which is attached to this node
+ model = selNode.NodeData;
+ modelClass = class(model);
+
+ % Is the selected node a valid exhibit?
+ if modelClass == "wtexample.model.Exhibit"
+
+ % Prompt user before deleting
+ message = "Are you sure you want to delete the exhibit """...
+ + model.Name + """?";
+ response = app.promptYesNoCancel(message);
+ if matches(response,"yes","IgnoreCase",true)
+
+ % Delete the exhibit
+ isMatch = session.Exhibit == model;
+ session.Exhibit(isMatch) = [];
+
+ end %if
+
+ end %if
+
+ end %switch
+
+ app.update()
+
+ end %function
+
+ end %methods
+
+end %classdef
\ No newline at end of file
diff --git a/widgets/examples/apps/contextual_view/+wtexample/+app/@ContextualViewExample/updateTreeHierarchy.m b/widgets/examples/apps/contextual_view/+wtexample/+app/@ContextualViewExample/updateTreeHierarchy.m
new file mode 100644
index 0000000..217fbc4
--- /dev/null
+++ b/widgets/examples/apps/contextual_view/+wtexample/+app/@ContextualViewExample/updateTreeHierarchy.m
@@ -0,0 +1,215 @@
+function updateTreeHierarchy(app)
+% Creates/updates the tree hierarchy by synchronizing each level
+
+% Copyright 2024 The MathWorks Inc.
+
+% Show output if Debug is on
+app.displayDebugText();
+
+% Sync session nodes
+syncSessionNodes(app.Tree, app.Session)
+
+
+end %function
+
+
+%% Sync Session Nodes
+function syncSessionNodes(parentNode,model)
+% Synchronizes child nodes to a model array
+
+arguments
+ parentNode (1,1) matlab.ui.container.Tree
+ model (1,:) wtexample.model.Session
+end
+
+% Capture existing session nodes
+oldSessionNodes = parentNode.Children;
+
+% Sync nodes at this level
+sessionNodes = syncNodes(parentNode, model);
+
+% Loop on each model
+for idx = 1:numel(model)
+
+ % Get current node/model pair
+ thisNode = sessionNodes(idx);
+ thisModel = model(idx);
+
+ % Update the node's text and icon
+ nodeText = "Session: " + thisModel.FileName;
+ if thisModel.Dirty
+ nodeText = nodeText + " *";
+ end
+ thisNode.Text = nodeText;
+ thisNode.Icon = "document2_24.png";
+
+ % Sync children of node
+ syncExhibitNodes(thisNode, thisModel.Exhibit)
+
+end %for
+
+% Expand any new session nodes
+isNew = ~ismember(sessionNodes, oldSessionNodes);
+if any(isNew)
+ expand(sessionNodes(isNew))
+end
+
+end %function
+
+
+%% Sync Exhibit Nodes
+function syncExhibitNodes(parentNode,model)
+% Synchronizes child nodes to a model array
+
+arguments
+ parentNode (1,1) matlab.ui.container.TreeNode
+ model (1,:) wtexample.model.Exhibit
+end
+
+% Sync nodes at this level
+exhibitNodes = syncNodes(parentNode, model);
+
+% Loop on each model
+for idx = 1:numel(model)
+
+ % Get current node/model pair
+ thisNode = exhibitNodes(idx);
+ thisModel = model(idx);
+
+ % Update the node's text and icon
+ thisNode.Text = "Exhibit: " + thisModel.Name;
+ thisNode.Icon = "exhibit.png";
+
+ % Sync children of node
+ syncEnclosureNodes(thisNode, thisModel.Enclosure)
+
+end %for
+
+end %function
+
+
+%% Sync Enclosure Nodes
+function syncEnclosureNodes(parentNode,model)
+% Synchronizes child nodes to a model array
+
+arguments
+ parentNode (1,1) matlab.ui.container.TreeNode
+ model (1,:) wtexample.model.Enclosure
+end
+
+% Sync nodes at this level
+enclosureNodes = syncNodes(parentNode, model);
+
+% Loop on each model
+for idx = 1:numel(model)
+
+ % Get current node/model pair
+ thisNode = enclosureNodes(idx);
+ thisModel = model(idx);
+
+ % Update the node's text and icon
+ thisNode.Text = "Enclosure: " + thisModel.Name;
+ thisNode.Icon = "enclosure.png";
+
+ % Sync children of node
+ syncAnimalNodes(thisNode, thisModel.Animal)
+
+end %for
+
+end %function
+
+
+%% Sync Animal Nodes
+function syncAnimalNodes(parentNode,model)
+% Synchronizes child nodes to a model array
+
+arguments
+ parentNode (1,1) matlab.ui.container.TreeNode
+ model (1,:) wtexample.model.Animal
+end
+
+% Sync nodes at this level
+animalNodes = syncNodes(parentNode, model);
+
+% Loop on each model
+for idx = 1:numel(model)
+
+ % Get current node/model pair
+ thisNode = animalNodes(idx);
+ thisModel = model(idx);
+
+ % Update the node's text and icon
+ thisNode.Text = "Animal: " + thisModel.Name;
+ thisNode.Icon = "animal.png";
+
+ % This is the lowest level - no children to sync
+
+end %for
+
+end %function
+
+
+
+%% Synchronize one level of tree nodes with a model class
+function newNodeList = syncNodes(parentNode, model)
+% Synchronizes nodes for one level of hierarchy
+
+arguments
+ parentNode % matlab.ui.container.TreeNode or matlab.ui.container.Tree
+ model (1,:) wt.model.BaseModel
+end
+
+% Get existing child nodes
+childNodes = parentNode.Children;
+
+% Get the models they reference
+if isempty(childNodes)
+ % Empty of same type as input
+ childNodeData = model([]);
+else
+ childNodeData = horzcat(childNodes.NodeData);
+end
+
+% Preallocate new node list
+persistent dummyNode emptyNodeList
+if isempty(dummyNode)
+ dummyNode = matlab.ui.container.TreeNode;
+ emptyNodeList = matlab.ui.container.TreeNode.empty(1,0);
+end
+numModels = numel(model);
+if numModels > 0
+ newNodeList(1, numModels) = dummyNode;
+else
+ newNodeList = emptyNodeList;
+end
+
+% Track which existing child nodes are updated so we can
+% remove old ones later
+childNodeUpdated = false(size(childNodes));
+
+% Loop on each model
+for idx = 1:numModels
+
+ % Does this model already have a node?
+ isMatch = model(idx) == childNodeData;
+ idxMatch = find(isMatch, 1);
+ if isempty(idxMatch)
+ % NO - Create a new node
+ newNode = uitreenode(parentNode);
+ newNode.NodeData = model(idx);
+ else
+ % YES - Use existing node
+ newNode = childNodes(idxMatch);
+ childNodeUpdated(idxMatch) = true;
+ end
+
+ % Store tne new node
+ newNodeList(idx) = newNode;
+
+end %for
+
+% Remove any unused nodes
+unusedNodes = childNodes(~childNodeUpdated);
+delete(unusedNodes)
+
+end %function
\ No newline at end of file
diff --git a/widgets/examples/apps/contextual_view/+wtexample/+enum/Sex.m b/widgets/examples/apps/contextual_view/+wtexample/+enum/Sex.m
new file mode 100644
index 0000000..380a352
--- /dev/null
+++ b/widgets/examples/apps/contextual_view/+wtexample/+enum/Sex.m
@@ -0,0 +1,11 @@
+classdef Sex
+ % Implements a selection list
+
+ enumeration
+ unspecified
+ male
+ female
+ other
+ end
+
+end %classdef
\ No newline at end of file
diff --git a/widgets/examples/apps/contextual_view/+wtexample/+model/Animal.m b/widgets/examples/apps/contextual_view/+wtexample/+model/Animal.m
new file mode 100644
index 0000000..1a36ce7
--- /dev/null
+++ b/widgets/examples/apps/contextual_view/+wtexample/+model/Animal.m
@@ -0,0 +1,53 @@
+classdef Animal < wt.model.BaseModel
+ % Implements the model class for a zoo animal
+
+
+ %% Public Properties
+ properties (AbortSet, SetObservable)
+
+ % Species of the animal
+ Species (1,1) string
+
+ % Birth date of the animal
+ BirthDate (1,1) datetime = NaT
+
+ % Sex of the animal
+ Sex (1,1) wtexample.enum.Sex = wtexample.enum.Sex.unspecified
+
+ end %properties
+
+
+ %% Read-Only Properties
+ properties (Dependent, SetAccess = immutable)
+
+ % Birth date of the animal (years)
+ Age (1,1) double
+
+ end %properties
+
+
+ % Accessors
+ methods
+
+ function value = get.Age(obj)
+ value = years(datetime("now") - obj.BirthDate);
+ end
+
+ end %methods
+
+
+ %% Constructor
+ methods
+ function obj = Animal(varargin)
+ % Constructor
+
+ % Call superclass method
+ % obj@wt.model.BaseModel(varargin{:});
+
+ % Debug instead
+ obj@wt.model.BaseModel(varargin{:},"Debug",true);
+
+ end %function
+ end %methods
+
+end %classdef
\ No newline at end of file
diff --git a/widgets/examples/apps/contextual_view/+wtexample/+model/Enclosure.m b/widgets/examples/apps/contextual_view/+wtexample/+model/Enclosure.m
new file mode 100644
index 0000000..6fd3bef
--- /dev/null
+++ b/widgets/examples/apps/contextual_view/+wtexample/+model/Enclosure.m
@@ -0,0 +1,48 @@
+classdef Enclosure < wt.model.BaseModel
+ % Implements the model class for an enclosure
+
+
+ %% Public Properties
+ properties (AbortSet, SetObservable)
+
+ % Point location of the enclosure on the map
+ Location (1,2) double
+
+ % Animals within this enclosure
+ Animal (1,:) wtexample.model.Animal
+
+ end %properties
+
+
+ % Accessors
+ % methods
+ % function set.Animal(obj,value)
+ % obj.Animal = value;
+ % obj.attachModelListeners("Animal");
+ % end
+ % end %methods
+
+
+ %% Constructor
+ methods
+ function obj = Enclosure(varargin)
+ % Constructor
+
+
+
+ % Call superclass method
+ % obj@wt.model.BaseModel(varargin{:});
+
+ % Debug instead
+ obj@wt.model.BaseModel(...
+ "Debug",true,...
+ varargin{:});
+
+ % obj@wt.model.BaseModel(...
+ % "AggregatedModelProperties","Animal",...
+ % "Debug",true,...
+ % varargin{:});
+ end %function
+ end %methods
+
+end %classdef
\ No newline at end of file
diff --git a/widgets/examples/apps/contextual_view/+wtexample/+model/Exhibit.m b/widgets/examples/apps/contextual_view/+wtexample/+model/Exhibit.m
new file mode 100644
index 0000000..21a8f80
--- /dev/null
+++ b/widgets/examples/apps/contextual_view/+wtexample/+model/Exhibit.m
@@ -0,0 +1,40 @@
+classdef Exhibit < wt.model.BaseModel
+ % Implements the model class for an exhibit
+
+
+ %% Public Properties
+ properties (AbortSet, SetObservable)
+
+ % Point location of the exhibit on the map
+ Location (1,2) double
+
+ % Enclosures within this exhibit
+ Enclosure (1,:) wtexample.model.Enclosure
+
+ end %properties
+
+
+ % Accessors
+ % methods
+ % function set.Enclosure(obj,value)
+ % obj.Enclosure = value;
+ % obj.attachModelListeners("Enclosure");
+ % end
+ % end %methods
+
+
+ %% Constructor
+ methods
+ function obj = Exhibit(varargin)
+ % Constructor
+
+ % Call superclass method
+ % obj@wt.model.BaseModel(varargin{:});
+
+ % Debug instead
+ obj@wt.model.BaseModel(varargin{:},"Debug",true);
+
+ end %function
+ end %methods
+
+end %classdef
\ No newline at end of file
diff --git a/widgets/examples/apps/contextual_view/+wtexample/+model/Session.m b/widgets/examples/apps/contextual_view/+wtexample/+model/Session.m
new file mode 100644
index 0000000..2e8bcd1
--- /dev/null
+++ b/widgets/examples/apps/contextual_view/+wtexample/+model/Session.m
@@ -0,0 +1,135 @@
+classdef Session < wt.model.BaseSession
+ % Implements the session class for the app
+
+
+ %% Public Properties
+ properties (SetObservable)
+
+ % The array of all exhibits
+ Exhibit (1,:) wtexample.model.Exhibit
+
+ % Selected indices
+ % SelectedIndices (1,3) {mustBeInteger}
+
+ end %properties
+
+
+ % Accessors
+ % methods
+ % function set.Exhibit(obj,value)
+ % obj.Exhibit = value;
+ % obj.attachModelListeners("Exhibit");
+ % end
+ % end %methods
+
+
+
+ %% Methods
+ methods
+
+ function importManifest(session, filePath)
+
+ arguments
+ session (1,1) wtexample.model.Session
+ filePath (1,1) string {mustBeFile}
+ end
+
+ % Import data
+ animalTable = readtable(filePath,"Sheet","Animals","TextType","string");
+ enclosureTable = readtable(filePath,"Sheet","Enclosures","TextType","string");
+ exhibitTable = readtable(filePath,"Sheet","Exhibits","TextType","string");
+
+
+ % === Parse Animals into objects ===
+
+ % Get data
+ thisTable = animalTable;
+ numRows = height(thisTable);
+
+ % Preallocate output
+ animal(numRows,1) = wtexample.model.Animal;
+
+ for idx = 1:numRows
+
+ % Create a new instance
+ newItem = wtexample.model.Animal;
+
+ % Populate data
+ newItem.Name = thisTable.Name(idx);
+ newItem.Species = thisTable.Species(idx);
+ newItem.BirthDate = thisTable.BirthDate(idx);
+ try
+ newItem.Sex = thisTable.Sex(idx);
+ catch
+ newItem.Sex = 'unspecified';
+ end
+
+ % Place in the list
+ animal(idx) = newItem;
+
+ end %for
+
+
+ % === Parse Enclosures into objects ===
+
+ % Get data
+ thisTable = enclosureTable;
+ numRows = height(thisTable);
+
+ % Preallocate output
+ enclosure(numRows,1) = wtexample.model.Enclosure;
+
+ for idx = 1:numRows
+
+ % Create a new instance
+ newItem = wtexample.model.Enclosure;
+
+ % Populate data
+ newItem.Name = thisTable.Name(idx);
+ newItem.Location = [thisTable.LocationX(idx), thisTable.LocationY(idx)];
+
+ % Attach animals belonging to this enclosure
+ isMatch = matches(animalTable.Enclosure, newItem.Name);
+ newItem.Animal = animal(isMatch);
+
+ % Place in the list
+ enclosure(idx) = newItem;
+
+ end %for
+
+
+ % === Parse Exhibits into objects ===
+
+ % Get data
+ thisTable = exhibitTable;
+ numRows = height(thisTable);
+
+ % Preallocate output
+ exhibit(numRows,1) = wtexample.model.Exhibit;
+
+ for idx = 1:numRows
+
+ % Create a new instance
+ newItem = wtexample.model.Exhibit;
+
+ % Populate data
+ newItem.Name = thisTable.Name(idx);
+ newItem.Location = [thisTable.LocationX(idx), thisTable.LocationY(idx)];
+
+ % Attach enclosures belonging to this exhibit
+ isMatch = matches(enclosureTable.Exhibit, newItem.Name);
+ newItem.Enclosure = enclosure(isMatch);
+
+ % Place in the list
+ exhibit(idx) = newItem;
+
+ end %for
+
+ % Add to session
+ session.Exhibit = vertcat(session.Exhibit, exhibit);
+
+ end %function
+
+ end %methods
+
+end %classdef
\ No newline at end of file
diff --git a/widgets/examples/apps/contextual_view/+wtexample/+viewcontroller/Animal.m b/widgets/examples/apps/contextual_view/+wtexample/+viewcontroller/Animal.m
new file mode 100644
index 0000000..c215cc4
--- /dev/null
+++ b/widgets/examples/apps/contextual_view/+wtexample/+viewcontroller/Animal.m
@@ -0,0 +1,158 @@
+classdef Animal < wt.abstract.BaseViewController
+ % View/Controller for Exhibit
+
+ %% Properties
+ properties (AbortSet, SetObservable)
+
+ % Model class
+ Model = wtexample.model.Animal.empty(0,0)
+
+ end %properties
+
+
+ %% Internal Components
+ properties (Transient, Hidden, SetAccess = protected)
+
+ SpeciesLabel matlab.ui.control.Label
+ SpeciesField matlab.ui.control.Label
+
+ NameLabel matlab.ui.control.Label
+ NameField matlab.ui.control.EditField
+
+ BirthDateLabel matlab.ui.control.Label
+ BirthDateField matlab.ui.control.DatePicker
+
+ AgeLabel matlab.ui.control.Label
+ AgeField matlab.ui.control.Label
+
+ SexLabel matlab.ui.control.Label
+ SexField matlab.ui.control.DropDown
+
+ end %properties
+
+
+ %% Protected Methods
+ methods (Access = protected)
+
+ function setup(obj)
+
+ % Call superclass setup first
+ obj.setup@wt.abstract.BaseViewController();
+
+ % Configure grid
+ obj.Grid.ColumnWidth = {'fit','1x'};
+ obj.Grid.RowHeight = {'fit','fit','fit','fit','fit'};
+
+
+ % Species field
+ tooltip = "Species of the animal";
+
+ obj.SpeciesLabel = uilabel(obj.Grid,...
+ "Text","Species:",...
+ "Tooltip",tooltip,...
+ "HorizontalAlignment","right");
+
+ obj.SpeciesField = uilabel(obj.Grid,...
+ "Text","",...
+ "Tooltip",tooltip);
+
+
+ % Name field
+ tooltip = "Name of the animal";
+
+ obj.NameLabel = uilabel(obj.Grid,...
+ "Text","Name:",...
+ "Tooltip",tooltip,...
+ "HorizontalAlignment","right");
+
+ obj.NameField = uieditfield(obj.Grid,...
+ "Tooltip",tooltip,...
+ "ValueChangedFcn",@(~,evt)onFieldEdited(obj,evt,"Name"));
+
+
+ % BirthDate field
+ tooltip = "Birthdate of the animal";
+
+ obj.BirthDateLabel = uilabel(obj.Grid,...
+ "Text","BirthDate:",...
+ "Tooltip",tooltip,...
+ "HorizontalAlignment","right");
+
+ obj.BirthDateField = uidatepicker(obj.Grid,...
+ "Tooltip",tooltip,...
+ "ValueChangedFcn",@(~,evt)onFieldEdited(obj,evt,"BirthDate"));
+
+
+ % Age field
+ tooltip = "Age of the animal";
+
+ obj.AgeLabel = uilabel(obj.Grid,...
+ "Text","Age:",...
+ "Tooltip",tooltip,...
+ "HorizontalAlignment","right");
+
+ obj.AgeField = uilabel(obj.Grid,...
+ "Text","",...
+ "Tooltip",tooltip);
+
+
+ % Sex field
+ tooltip = "Sex of the animal";
+
+ obj.SexLabel = uilabel(obj.Grid,...
+ "Text","Sex:",...
+ "Tooltip",tooltip,...
+ "HorizontalAlignment","right");
+
+ items = string(enumeration("wtexample.enum.Sex"));
+ obj.SexField = uidropdown(obj.Grid,...
+ "Tooltip",tooltip,...
+ "Items",items,...
+ "ValueChangedFcn",@(~,evt)onFieldEdited(obj,evt,"Sex"));
+
+ end %function
+
+
+ function update(obj)
+
+ % Call superclass method
+ obj.update@wt.abstract.BaseViewController();
+
+ % Is there a valid model?
+ if isscalar(obj.Model) && isvalid(obj.Model)
+ % YES - enable controls and get the model
+ enable = true;
+ model = obj.Model;
+ else
+ % NO - disable controls, show default model values
+ enable = false;
+ model = wtexample.model.Animal;
+ end
+
+ % Update the fields
+ species = model.Species;
+ obj.SpeciesField.Text = species;
+ obj.SpeciesField.Enable = enable;
+
+ name = model.Name;
+ obj.NameField.Value = name;
+ obj.NameField.Enable = enable;
+
+ birthDate = model.BirthDate;
+ obj.BirthDateField.Value = birthDate;
+ obj.BirthDateField.Enable = enable;
+
+ age = model.Age;
+ obj.AgeField.Text = string(age);
+ obj.AgeField.Enable = enable;
+
+ sex = model.Sex;
+ obj.SexField.Value = sex;
+ obj.SexField.Enable = enable;
+
+ end %function
+
+ end %methods
+
+end %classdef
+
diff --git a/widgets/examples/apps/contextual_view/+wtexample/+viewcontroller/Enclosure.m b/widgets/examples/apps/contextual_view/+wtexample/+viewcontroller/Enclosure.m
new file mode 100644
index 0000000..a208c91
--- /dev/null
+++ b/widgets/examples/apps/contextual_view/+wtexample/+viewcontroller/Enclosure.m
@@ -0,0 +1,116 @@
+classdef Enclosure < wt.abstract.BaseViewController
+ % View/Controller for Exhibit
+
+ %% Properties
+ properties (AbortSet, SetObservable)
+
+ % Model class
+ Model = wtexample.model.Enclosure.empty(0,0)
+
+ end %properties
+
+
+ %% Internal Components
+ properties (Transient, Hidden, SetAccess = protected)
+
+ NameLabel matlab.ui.control.Label
+ NameField matlab.ui.control.EditField
+
+ LocationLabel matlab.ui.control.Label
+ LocationFieldX matlab.ui.control.NumericEditField
+ LocationFieldY matlab.ui.control.NumericEditField
+
+ end %properties
+
+
+ %% Protected Methods
+ methods (Access = protected)
+
+ function setup(obj)
+
+ % Call superclass setup first
+ obj.setup@wt.abstract.BaseViewController();
+
+ % Configure grid
+ obj.Grid.ColumnWidth = {'fit','1x','1x'};
+ obj.Grid.RowHeight = {'fit','fit'};
+
+
+ % Name field
+ tooltip = "Name of the enclosure";
+
+ obj.NameLabel = uilabel(obj.Grid,...
+ "Text","Name:",...
+ "Tooltip",tooltip,...
+ "HorizontalAlignment","right");
+ obj.NameLabel.Layout.Row = 1;
+ obj.NameLabel.Layout.Column = 1;
+
+ obj.NameField = uieditfield(obj.Grid,...
+ "Tooltip",tooltip,...
+ "ValueChangedFcn",@(~,evt)onFieldEdited(obj,evt,"Name"));
+ obj.NameField.Layout.Row = 1;
+ obj.NameField.Layout.Column = [2 3];
+
+
+ % Location fields
+ tooltip = "Point location of the enclosure";
+
+ obj.LocationLabel = uilabel(obj.Grid,...
+ "Text","Location:",...
+ "Tooltip",tooltip,...
+ "HorizontalAlignment","right");
+ obj.LocationLabel.Layout.Row = 2;
+ obj.LocationLabel.Layout.Column = 1;
+
+
+ obj.LocationFieldX = uieditfield(obj.Grid,'numeric',...
+ "Tooltip",tooltip,...
+ "ValueChangedFcn",@(~,evt)onFieldEdited(obj,evt,"Location",1));
+ obj.LocationFieldX.Layout.Row = 2;
+ obj.LocationFieldX.Layout.Column = 2;
+
+ obj.LocationFieldY = uieditfield(obj.Grid,'numeric',...
+ "Tooltip",tooltip,...
+ "ValueChangedFcn",@(~,evt)onFieldEdited(obj,evt,"Location",2));
+ obj.LocationFieldY.Layout.Row = 2;
+ obj.LocationFieldY.Layout.Column = 3;
+
+ end %function
+
+
+ function update(obj)
+
+ % Call superclass method
+ obj.update@wt.abstract.BaseViewController();
+
+ % Is there a valid model?
+ if isscalar(obj.Model) && isvalid(obj.Model)
+ % YES - enable controls and get the model
+ enable = true;
+ model = obj.Model;
+ else
+ % NO - disable controls, show default model values
+ enable = false;
+ model = wtexample.model.Enclosure;
+ end
+
+ % Update the fields
+ name = model.Name;
+ obj.NameField.Value = name;
+ obj.NameField.Enable = enable;
+
+ locX = model.Location(1);
+ obj.LocationFieldX.Value = locX;
+ obj.LocationFieldX.Enable = enable;
+
+ locY = model.Location(2);
+ obj.LocationFieldY.Value = locY;
+ obj.LocationFieldY.Enable = enable;
+
+ end %function
+
+ end %methods
+
+end %classdef
+
diff --git a/widgets/examples/apps/contextual_view/+wtexample/+viewcontroller/Exhibit.m b/widgets/examples/apps/contextual_view/+wtexample/+viewcontroller/Exhibit.m
new file mode 100644
index 0000000..527cd83
--- /dev/null
+++ b/widgets/examples/apps/contextual_view/+wtexample/+viewcontroller/Exhibit.m
@@ -0,0 +1,116 @@
+classdef Exhibit < wt.abstract.BaseViewController
+ % View/Controller for Exhibit
+
+ %% Properties
+ properties (AbortSet, SetObservable)
+
+ % Model class
+ Model = wtexample.model.Exhibit.empty(0,0)
+
+ end %properties
+
+
+ %% Internal Components
+ properties (Transient, Hidden, SetAccess = protected)
+
+ NameLabel matlab.ui.control.Label
+ NameField matlab.ui.control.EditField
+
+ LocationLabel matlab.ui.control.Label
+ LocationFieldX matlab.ui.control.NumericEditField
+ LocationFieldY matlab.ui.control.NumericEditField
+
+ end %properties
+
+
+ %% Protected Methods
+ methods (Access = protected)
+
+ function setup(obj)
+
+ % Call superclass setup first
+ obj.setup@wt.abstract.BaseViewController();
+
+ % Configure grid
+ obj.Grid.ColumnWidth = {'fit','1x','1x'};
+ obj.Grid.RowHeight = {'fit','fit'};
+
+
+ % Name field
+ tooltip = "Name of the exhibit";
+
+ obj.NameLabel = uilabel(obj.Grid,...
+ "Text","Name:",...
+ "Tooltip",tooltip,...
+ "HorizontalAlignment","right");
+ obj.NameLabel.Layout.Row = 1;
+ obj.NameLabel.Layout.Column = 1;
+
+ obj.NameField = uieditfield(obj.Grid,...
+ "Tooltip",tooltip,...
+ "ValueChangedFcn",@(~,evt)onFieldEdited(obj,evt,"Name"));
+ obj.NameField.Layout.Row = 1;
+ obj.NameField.Layout.Column = [2 3];
+
+
+ % Location fields
+ tooltip = "Point location of the exhibit";
+
+ obj.LocationLabel = uilabel(obj.Grid,...
+ "Text","Location:",...
+ "Tooltip",tooltip,...
+ "HorizontalAlignment","right");
+ obj.LocationLabel.Layout.Row = 2;
+ obj.LocationLabel.Layout.Column = 1;
+
+
+ obj.LocationFieldX = uieditfield(obj.Grid,'numeric',...
+ "Tooltip",tooltip,...
+ "ValueChangedFcn",@(~,evt)onFieldEdited(obj,evt,"Location",1));
+ obj.LocationFieldX.Layout.Row = 2;
+ obj.LocationFieldX.Layout.Column = 2;
+
+ obj.LocationFieldY = uieditfield(obj.Grid,'numeric',...
+ "Tooltip",tooltip,...
+ "ValueChangedFcn",@(~,evt)onFieldEdited(obj,evt,"Location",2));
+ obj.LocationFieldY.Layout.Row = 2;
+ obj.LocationFieldY.Layout.Column = 3;
+
+ end %function
+
+
+ function update(obj)
+
+ % Call superclass method
+ obj.update@wt.abstract.BaseViewController();
+
+ % Is there a valid model?
+ if isscalar(obj.Model) && isvalid(obj.Model)
+ % YES - enable controls and get the model
+ enable = true;
+ model = obj.Model;
+ else
+ % NO - disable controls, show default model values
+ enable = false;
+ model = wtexample.model.Exhibit;
+ end
+
+ % Update the fields
+ name = model.Name;
+ obj.NameField.Value = name;
+ obj.NameField.Enable = enable;
+
+ locX = model.Location(1);
+ obj.LocationFieldX.Value = locX;
+ obj.LocationFieldX.Enable = enable;
+
+ locY = model.Location(2);
+ obj.LocationFieldY.Value = locY;
+ obj.LocationFieldY.Enable = enable;
+
+ end %function
+
+ end %methods
+
+end %classdef
+
diff --git a/widgets/examples/apps/contextual_view/+wtexample/+viewcontroller/Session.m b/widgets/examples/apps/contextual_view/+wtexample/+viewcontroller/Session.m
new file mode 100644
index 0000000..91a067f
--- /dev/null
+++ b/widgets/examples/apps/contextual_view/+wtexample/+viewcontroller/Session.m
@@ -0,0 +1,112 @@
+classdef Session < wt.abstract.BaseViewController
+ % View for wtexample.model.Session
+
+ %% Properties
+ properties (SetObservable)
+ Model = wtexample.model.Session.empty(0,0)
+ end
+
+
+ %% Internal Properties
+ properties (Transient, Hidden, SetAccess = protected)
+
+ FileNameLabel matlab.ui.control.Label
+ FileNameField matlab.ui.control.Label
+
+ FilePathLabel matlab.ui.control.Label
+ FilePathField matlab.ui.control.Label
+
+ DescriptionLabel matlab.ui.control.Label
+ DescriptionField matlab.ui.control.EditField
+
+ end %properties
+
+
+ %% Protected Methods
+ methods (Access = protected)
+
+ function setup(obj)
+
+ % Call superclass setup first
+ obj.setup@wt.abstract.BaseViewController();
+
+ % Configure grid
+ obj.Grid.ColumnWidth = {'fit','1x'};
+ obj.Grid.RowHeight = {'fit','fit','fit'};
+
+ % Add fields
+
+ % --- Height --- %
+ tooltip = "File name of the session";
+
+ obj.FileNameLabel = uilabel(obj.Grid);
+ obj.FileNameLabel.Text = "File Name:";
+ obj.FileNameLabel.Tooltip = tooltip;
+ obj.FileNameLabel.HorizontalAlignment = "right";
+
+ obj.FileNameField = uilabel(obj.Grid);
+ obj.FileNameField.Text = "";
+ obj.FileNameField.Tooltip = tooltip;
+ obj.FileNameField.HorizontalAlignment = "left";
+
+
+ % --- FilePath --- %
+ tooltip = "File path of the session";
+
+ obj.FilePathLabel = uilabel(obj.Grid);
+ obj.FilePathLabel.Text = "File Path:";
+ obj.FilePathLabel.Tooltip = tooltip;
+ obj.FilePathLabel.HorizontalAlignment = "right";
+
+ obj.FilePathField = uilabel(obj.Grid);
+ obj.FilePathField.Text = "";
+ obj.FilePathField.Tooltip = tooltip;
+ obj.FilePathField.HorizontalAlignment = "left";
+
+
+ % --- Description --- %
+ tooltip = "Description of the session";
+
+ obj.DescriptionLabel = uilabel(obj.Grid);
+ obj.DescriptionLabel.Text = "Description:";
+ obj.DescriptionLabel.Tooltip = tooltip;
+ obj.DescriptionLabel.HorizontalAlignment = "right";
+
+ obj.DescriptionField = uieditfield(obj.Grid);
+ obj.DescriptionField.Tooltip = tooltip;
+ obj.DescriptionField.ValueChangedFcn = ...
+ @(~,evt)onFieldEdited(obj,evt,"Description");
+
+ end %function
+
+
+ function update(obj)
+
+ % Call superclass method first
+ obj.update@wt.abstract.BaseViewController();
+
+ % Get the model to display
+ [model, validToDisplay] = obj.getScalarModelToDisplay();
+
+ % Update the fields
+ obj.FileNameField.Enable = validToDisplay;
+ obj.FilePathField.Enable = validToDisplay;
+ obj.DescriptionField.Enable = validToDisplay;
+
+ obj.FileNameField.Text = model.FileName;
+ obj.FilePathField.Text = model.FilePath;
+ obj.DescriptionField.Value = model.Description;
+
+ end %function
+
+
+ function onDescriptionEdited(obj,evt)
+
+ newValue = evt.Value;
+ obj.Model.Description = newValue;
+
+ end %function
+
+ end %methods
+
+end %classdef
\ No newline at end of file
diff --git a/widgets/examples/apps/contextual_view/ExampleZooManifest.xlsx b/widgets/examples/apps/contextual_view/ExampleZooManifest.xlsx
new file mode 100644
index 0000000..8b92d4d
Binary files /dev/null and b/widgets/examples/apps/contextual_view/ExampleZooManifest.xlsx differ
diff --git a/widgets/examples/apps/contextual_view/UML_Backend.mldatx b/widgets/examples/apps/contextual_view/UML_Backend.mldatx
new file mode 100644
index 0000000..3eda413
Binary files /dev/null and b/widgets/examples/apps/contextual_view/UML_Backend.mldatx differ
diff --git a/widgets/examples/apps/contextual_view/UML_Backend.png b/widgets/examples/apps/contextual_view/UML_Backend.png
new file mode 100644
index 0000000..85fed0a
Binary files /dev/null and b/widgets/examples/apps/contextual_view/UML_Backend.png differ
diff --git a/widgets/examples/apps/contextual_view/UML_ContextualViewPane.mldatx b/widgets/examples/apps/contextual_view/UML_ContextualViewPane.mldatx
new file mode 100644
index 0000000..d3050b3
Binary files /dev/null and b/widgets/examples/apps/contextual_view/UML_ContextualViewPane.mldatx differ
diff --git a/widgets/examples/apps/contextual_view/UML_ContextualViewPane.png b/widgets/examples/apps/contextual_view/UML_ContextualViewPane.png
new file mode 100644
index 0000000..d44bd4b
Binary files /dev/null and b/widgets/examples/apps/contextual_view/UML_ContextualViewPane.png differ
diff --git a/widgets/examples/apps/contextual_view/UML_Frontend.mldatx b/widgets/examples/apps/contextual_view/UML_Frontend.mldatx
new file mode 100644
index 0000000..66526e5
Binary files /dev/null and b/widgets/examples/apps/contextual_view/UML_Frontend.mldatx differ
diff --git a/widgets/examples/apps/contextual_view/UML_Frontend_ViewControllers.png b/widgets/examples/apps/contextual_view/UML_Frontend_ViewControllers.png
new file mode 100644
index 0000000..ff32084
Binary files /dev/null and b/widgets/examples/apps/contextual_view/UML_Frontend_ViewControllers.png differ
diff --git a/widgets/examples/apps/contextual_view/UML_all.mldatx b/widgets/examples/apps/contextual_view/UML_all.mldatx
new file mode 100644
index 0000000..18dcb70
Binary files /dev/null and b/widgets/examples/apps/contextual_view/UML_all.mldatx differ
diff --git a/widgets/examples/apps/contextual_view/animal.png b/widgets/examples/apps/contextual_view/animal.png
new file mode 100644
index 0000000..44bb930
Binary files /dev/null and b/widgets/examples/apps/contextual_view/animal.png differ
diff --git a/widgets/examples/apps/contextual_view/enclosure.png b/widgets/examples/apps/contextual_view/enclosure.png
new file mode 100644
index 0000000..ae4970a
Binary files /dev/null and b/widgets/examples/apps/contextual_view/enclosure.png differ
diff --git a/widgets/examples/apps/contextual_view/example_rj.m b/widgets/examples/apps/contextual_view/example_rj.m
new file mode 100644
index 0000000..73c18c2
--- /dev/null
+++ b/widgets/examples/apps/contextual_view/example_rj.m
@@ -0,0 +1,33 @@
+%% Create animal
+a = wtexample.model.Animal("Debug",true);
+a.Name = "Giraffe X";
+a.BirthDate = datetime("today") - years(7.5);
+a.Species = "giraffe";
+
+%% Create enclosure, add animal to it
+n = wtexample.model.Enclosure("Debug",true);
+n.Name = "Giraffe Pen";
+n.Animal = a;
+
+%% Create exhibit, add enclosure to it
+x = wtexample.model.Exhibit("Debug",true);
+x.Name = "African Safari";
+x.Enclosure = n;
+
+%% Add another animal to the enclosure
+a2 = wtexample.model.Animal("Debug",true);
+a2.Name = "Giraffe Y";
+a2.Species = "giraffe";
+a2.BirthDate = datetime("today") - years(4);
+a2.Sex = "male";
+n.Animal(end+1) = a2;
+
+%% Attach to session
+s = wtexample.model.Session("Debug",true,"Exhibit",x);
+
+
+%% Change a property
+a.Sex = "female";
+
+%% Investigate listener/event issue:
+a.BirthDate = a.BirthDate - days(1);
\ No newline at end of file
diff --git a/widgets/examples/apps/contextual_view/exhibit.png b/widgets/examples/apps/contextual_view/exhibit.png
new file mode 100644
index 0000000..532b8ac
Binary files /dev/null and b/widgets/examples/apps/contextual_view/exhibit.png differ
diff --git a/widgets/examples/apps/contextual_view/robyn_session.mat b/widgets/examples/apps/contextual_view/robyn_session.mat
new file mode 100644
index 0000000..f12ec13
Binary files /dev/null and b/widgets/examples/apps/contextual_view/robyn_session.mat differ
diff --git a/widgets/icons/abort_24.png b/widgets/icons/abort_24.png
new file mode 100644
index 0000000..f5d19ad
Binary files /dev/null and b/widgets/icons/abort_24.png differ
diff --git a/widgets/icons/addBlue_24.png b/widgets/icons/addBlue_24.png
new file mode 100644
index 0000000..99c1777
Binary files /dev/null and b/widgets/icons/addBlue_24.png differ
diff --git a/widgets/icons/addColumn_24.png b/widgets/icons/addColumn_24.png
new file mode 100644
index 0000000..6462765
Binary files /dev/null and b/widgets/icons/addColumn_24.png differ
diff --git a/widgets/icons/addGreen_24.png b/widgets/icons/addGreen_24.png
new file mode 100644
index 0000000..1a19846
Binary files /dev/null and b/widgets/icons/addGreen_24.png differ
diff --git a/widgets/icons/addRow_24.png b/widgets/icons/addRow_24.png
new file mode 100644
index 0000000..4383be1
Binary files /dev/null and b/widgets/icons/addRow_24.png differ
diff --git a/widgets/icons/addYellow_24.png b/widgets/icons/addYellow_24.png
new file mode 100644
index 0000000..fe72d3d
Binary files /dev/null and b/widgets/icons/addYellow_24.png differ
diff --git a/widgets/icons/add_plot_24.png b/widgets/icons/add_plot_24.png
new file mode 100644
index 0000000..0d329fb
Binary files /dev/null and b/widgets/icons/add_plot_24.png differ
diff --git a/widgets/icons/adjustSliders_24.png b/widgets/icons/adjustSliders_24.png
new file mode 100644
index 0000000..d803e2e
Binary files /dev/null and b/widgets/icons/adjustSliders_24.png differ
diff --git a/widgets/icons/adjust_24.png b/widgets/icons/adjust_24.png
new file mode 100644
index 0000000..4833266
Binary files /dev/null and b/widgets/icons/adjust_24.png differ
diff --git a/widgets/icons/annotation_24.png b/widgets/icons/annotation_24.png
new file mode 100644
index 0000000..5f673b1
Binary files /dev/null and b/widgets/icons/annotation_24.png differ
diff --git a/widgets/icons/architecture_24.png b/widgets/icons/architecture_24.png
new file mode 100644
index 0000000..f86c269
Binary files /dev/null and b/widgets/icons/architecture_24.png differ
diff --git a/widgets/icons/archive_24.png b/widgets/icons/archive_24.png
new file mode 100644
index 0000000..4c6b1e7
Binary files /dev/null and b/widgets/icons/archive_24.png differ
diff --git a/widgets/icons/auto_order_24.png b/widgets/icons/auto_order_24.png
new file mode 100644
index 0000000..afc0b45
Binary files /dev/null and b/widgets/icons/auto_order_24.png differ
diff --git a/widgets/icons/back_24.png b/widgets/icons/back_24.png
new file mode 100644
index 0000000..4bb09f5
Binary files /dev/null and b/widgets/icons/back_24.png differ
diff --git a/widgets/icons/bar_plot_24.png b/widgets/icons/bar_plot_24.png
new file mode 100644
index 0000000..a4c80b3
Binary files /dev/null and b/widgets/icons/bar_plot_24.png differ
diff --git a/widgets/icons/box_24.png b/widgets/icons/box_24.png
new file mode 100644
index 0000000..d93b9d9
Binary files /dev/null and b/widgets/icons/box_24.png differ
diff --git a/widgets/icons/browseFolder_24.png b/widgets/icons/browseFolder_24.png
new file mode 100644
index 0000000..de6b487
Binary files /dev/null and b/widgets/icons/browseFolder_24.png differ
diff --git a/widgets/icons/brush_24.png b/widgets/icons/brush_24.png
new file mode 100644
index 0000000..7314b20
Binary files /dev/null and b/widgets/icons/brush_24.png differ
diff --git a/widgets/icons/build_24.png b/widgets/icons/build_24.png
new file mode 100644
index 0000000..7c98f9e
Binary files /dev/null and b/widgets/icons/build_24.png differ
diff --git a/widgets/icons/calendar_24.png b/widgets/icons/calendar_24.png
new file mode 100644
index 0000000..920ae6a
Binary files /dev/null and b/widgets/icons/calendar_24.png differ
diff --git a/widgets/icons/check_24.png b/widgets/icons/check_24.png
index 8cab0d5..3c9b4c3 100644
Binary files a/widgets/icons/check_24.png and b/widgets/icons/check_24.png differ
diff --git a/widgets/icons/chevronDownBlack_24.png b/widgets/icons/chevronDownBlack_24.png
new file mode 100644
index 0000000..670b6b5
Binary files /dev/null and b/widgets/icons/chevronDownBlack_24.png differ
diff --git a/widgets/icons/chevronLeftBlack_24.png b/widgets/icons/chevronLeftBlack_24.png
new file mode 100644
index 0000000..a354ced
Binary files /dev/null and b/widgets/icons/chevronLeftBlack_24.png differ
diff --git a/widgets/icons/chevronRightBlack_24.png b/widgets/icons/chevronRightBlack_24.png
new file mode 100644
index 0000000..24ce308
Binary files /dev/null and b/widgets/icons/chevronRightBlack_24.png differ
diff --git a/widgets/icons/chevronUpBlack_24.png b/widgets/icons/chevronUpBlack_24.png
new file mode 100644
index 0000000..e41230a
Binary files /dev/null and b/widgets/icons/chevronUpBlack_24.png differ
diff --git a/widgets/icons/clock_24.png b/widgets/icons/clock_24.png
index ba8df84..4faf07f 100644
Binary files a/widgets/icons/clock_24.png and b/widgets/icons/clock_24.png differ
diff --git a/widgets/icons/clock_digital_24.png b/widgets/icons/clock_digital_24.png
new file mode 100644
index 0000000..ce26ada
Binary files /dev/null and b/widgets/icons/clock_digital_24.png differ
diff --git a/widgets/icons/closeBlack_12.png b/widgets/icons/closeBlack_12.png
new file mode 100644
index 0000000..7320bad
Binary files /dev/null and b/widgets/icons/closeBlack_12.png differ
diff --git a/widgets/icons/close_24.png b/widgets/icons/close_24.png
index 1c6a51e..cee8126 100644
Binary files a/widgets/icons/close_24.png and b/widgets/icons/close_24.png differ
diff --git a/widgets/icons/code_24.png b/widgets/icons/code_24.png
new file mode 100644
index 0000000..f82cf05
Binary files /dev/null and b/widgets/icons/code_24.png differ
diff --git a/widgets/icons/collapseView.png b/widgets/icons/collapseView.png
new file mode 100644
index 0000000..c8070a0
Binary files /dev/null and b/widgets/icons/collapseView.png differ
diff --git a/widgets/icons/collapsedownBlack.png b/widgets/icons/collapsedownBlack.png
new file mode 100644
index 0000000..f7cf321
Binary files /dev/null and b/widgets/icons/collapsedownBlack.png differ
diff --git a/widgets/icons/collapseleftBlack.png b/widgets/icons/collapseleftBlack.png
new file mode 100644
index 0000000..8c67730
Binary files /dev/null and b/widgets/icons/collapseleftBlack.png differ
diff --git a/widgets/icons/collapserightBlack.png b/widgets/icons/collapserightBlack.png
new file mode 100644
index 0000000..46930d1
Binary files /dev/null and b/widgets/icons/collapserightBlack.png differ
diff --git a/widgets/icons/collapseupBlack.png b/widgets/icons/collapseupBlack.png
new file mode 100644
index 0000000..04e8d93
Binary files /dev/null and b/widgets/icons/collapseupBlack.png differ
diff --git a/widgets/icons/colorbar_24.png b/widgets/icons/colorbar_24.png
new file mode 100644
index 0000000..ca845b0
Binary files /dev/null and b/widgets/icons/colorbar_24.png differ
diff --git a/widgets/icons/compare_24.png b/widgets/icons/compare_24.png
new file mode 100644
index 0000000..c81abb9
Binary files /dev/null and b/widgets/icons/compare_24.png differ
diff --git a/widgets/icons/complete_16.png b/widgets/icons/complete_16.png
index c5b73cd..ff4d296 100644
Binary files a/widgets/icons/complete_16.png and b/widgets/icons/complete_16.png differ
diff --git a/widgets/icons/configuration_24.png b/widgets/icons/configuration_24.png
new file mode 100644
index 0000000..25242db
Binary files /dev/null and b/widgets/icons/configuration_24.png differ
diff --git a/widgets/icons/configure_view_24.png b/widgets/icons/configure_view_24.png
new file mode 100644
index 0000000..b5889c7
Binary files /dev/null and b/widgets/icons/configure_view_24.png differ
diff --git a/widgets/icons/copy_24.png b/widgets/icons/copy_24.png
index fbeb1f7..1d5fb7e 100644
Binary files a/widgets/icons/copy_24.png and b/widgets/icons/copy_24.png differ
diff --git a/widgets/icons/curveFit_22.png b/widgets/icons/curveFit_22.png
new file mode 100644
index 0000000..c4e0a27
Binary files /dev/null and b/widgets/icons/curveFit_22.png differ
diff --git a/widgets/icons/curveFittingApp_24.png b/widgets/icons/curveFittingApp_24.png
new file mode 100644
index 0000000..8d771fb
Binary files /dev/null and b/widgets/icons/curveFittingApp_24.png differ
diff --git a/widgets/icons/cut_blue_24.png b/widgets/icons/cut_blue_24.png
new file mode 100644
index 0000000..658aa00
Binary files /dev/null and b/widgets/icons/cut_blue_24.png differ
diff --git a/widgets/icons/database_24.png b/widgets/icons/database_24.png
new file mode 100644
index 0000000..0506e98
Binary files /dev/null and b/widgets/icons/database_24.png differ
diff --git a/widgets/icons/database_find_24.png b/widgets/icons/database_find_24.png
new file mode 100644
index 0000000..ae1e56f
Binary files /dev/null and b/widgets/icons/database_find_24.png differ
diff --git a/widgets/icons/deleteColumn_24.png b/widgets/icons/deleteColumn_24.png
new file mode 100644
index 0000000..194a25e
Binary files /dev/null and b/widgets/icons/deleteColumn_24.png differ
diff --git a/widgets/icons/deleteRow_24.png b/widgets/icons/deleteRow_24.png
new file mode 100644
index 0000000..859605f
Binary files /dev/null and b/widgets/icons/deleteRow_24.png differ
diff --git a/widgets/icons/digitalClock_24.png b/widgets/icons/digitalClock_24.png
new file mode 100644
index 0000000..e6439af
Binary files /dev/null and b/widgets/icons/digitalClock_24.png differ
diff --git a/widgets/icons/document2_24.png b/widgets/icons/document2_24.png
new file mode 100644
index 0000000..6ca3d0e
Binary files /dev/null and b/widgets/icons/document2_24.png differ
diff --git a/widgets/icons/document_24.png b/widgets/icons/document_24.png
new file mode 100644
index 0000000..963a567
Binary files /dev/null and b/widgets/icons/document_24.png differ
diff --git a/widgets/icons/down_24.png b/widgets/icons/down_24.png
index 808619a..961b1f4 100644
Binary files a/widgets/icons/down_24.png and b/widgets/icons/down_24.png differ
diff --git a/widgets/icons/download_24.png b/widgets/icons/download_24.png
new file mode 100644
index 0000000..a50ddfd
Binary files /dev/null and b/widgets/icons/download_24.png differ
diff --git a/widgets/icons/editDocument_24.png b/widgets/icons/editDocument_24.png
new file mode 100644
index 0000000..7be1670
Binary files /dev/null and b/widgets/icons/editDocument_24.png differ
diff --git a/widgets/icons/edit_24.png b/widgets/icons/edit_24.png
index 128f6a6..3b94788 100644
Binary files a/widgets/icons/edit_24.png and b/widgets/icons/edit_24.png differ
diff --git a/widgets/icons/erase_24.png b/widgets/icons/erase_24.png
index eece103..f9704a6 100644
Binary files a/widgets/icons/erase_24.png and b/widgets/icons/erase_24.png differ
diff --git a/widgets/icons/error_16.png b/widgets/icons/error_16.png
index 3bffbc8..47877fc 100644
Binary files a/widgets/icons/error_16.png and b/widgets/icons/error_16.png differ
diff --git a/widgets/icons/error_24.png b/widgets/icons/error_24.png
new file mode 100644
index 0000000..6011bd4
Binary files /dev/null and b/widgets/icons/error_24.png differ
diff --git a/widgets/icons/expandView.png b/widgets/icons/expandView.png
new file mode 100644
index 0000000..3b90f9d
Binary files /dev/null and b/widgets/icons/expandView.png differ
diff --git a/widgets/icons/exportVariable_24.png b/widgets/icons/exportVariable_24.png
new file mode 100644
index 0000000..6c85c84
Binary files /dev/null and b/widgets/icons/exportVariable_24.png differ
diff --git a/widgets/icons/export_24.png b/widgets/icons/export_24.png
index 2385c33..456fd4d 100644
Binary files a/widgets/icons/export_24.png and b/widgets/icons/export_24.png differ
diff --git a/widgets/icons/export_plot_24.png b/widgets/icons/export_plot_24.png
new file mode 100644
index 0000000..1f03250
Binary files /dev/null and b/widgets/icons/export_plot_24.png differ
diff --git a/widgets/icons/failed_24.png b/widgets/icons/failed_24.png
new file mode 100644
index 0000000..c43ce43
Binary files /dev/null and b/widgets/icons/failed_24.png differ
diff --git a/widgets/icons/figure_24.png b/widgets/icons/figure_24.png
new file mode 100644
index 0000000..42c4e4d
Binary files /dev/null and b/widgets/icons/figure_24.png differ
diff --git a/widgets/icons/filter_clear_24.png b/widgets/icons/filter_clear_24.png
new file mode 100644
index 0000000..fc8b706
Binary files /dev/null and b/widgets/icons/filter_clear_24.png differ
diff --git a/widgets/icons/filter_info_24.png b/widgets/icons/filter_info_24.png
new file mode 100644
index 0000000..3b72278
Binary files /dev/null and b/widgets/icons/filter_info_24.png differ
diff --git a/widgets/icons/findFiles_24.png b/widgets/icons/findFiles_24.png
new file mode 100644
index 0000000..e7a1a92
Binary files /dev/null and b/widgets/icons/findFiles_24.png differ
diff --git a/widgets/icons/find_files_24.png b/widgets/icons/find_files_24.png
new file mode 100644
index 0000000..1e163eb
Binary files /dev/null and b/widgets/icons/find_files_24.png differ
diff --git a/widgets/icons/fitLine_24.png b/widgets/icons/fitLine_24.png
new file mode 100644
index 0000000..952da65
Binary files /dev/null and b/widgets/icons/fitLine_24.png differ
diff --git a/widgets/icons/fit_curve_24.png b/widgets/icons/fit_curve_24.png
new file mode 100644
index 0000000..8aaa739
Binary files /dev/null and b/widgets/icons/fit_curve_24.png differ
diff --git a/widgets/icons/fit_line_24.png b/widgets/icons/fit_line_24.png
new file mode 100644
index 0000000..a314771
Binary files /dev/null and b/widgets/icons/fit_line_24.png differ
diff --git a/widgets/icons/fit_view_24.png b/widgets/icons/fit_view_24.png
new file mode 100644
index 0000000..01fd8b6
Binary files /dev/null and b/widgets/icons/fit_view_24.png differ
diff --git a/widgets/icons/floodFill_24.png b/widgets/icons/floodFill_24.png
new file mode 100644
index 0000000..41cbce3
Binary files /dev/null and b/widgets/icons/floodFill_24.png differ
diff --git a/widgets/icons/forward_24.png b/widgets/icons/forward_24.png
new file mode 100644
index 0000000..095d787
Binary files /dev/null and b/widgets/icons/forward_24.png differ
diff --git a/widgets/icons/function_24.png b/widgets/icons/function_24.png
new file mode 100644
index 0000000..3a44908
Binary files /dev/null and b/widgets/icons/function_24.png differ
diff --git a/widgets/icons/function_add_24.png b/widgets/icons/function_add_24.png
new file mode 100644
index 0000000..8d5fe9d
Binary files /dev/null and b/widgets/icons/function_add_24.png differ
diff --git a/widgets/icons/globe_24.png b/widgets/icons/globe_24.png
new file mode 100644
index 0000000..6f9ffd3
Binary files /dev/null and b/widgets/icons/globe_24.png differ
diff --git a/widgets/icons/goToEnd_24.png b/widgets/icons/goToEnd_24.png
new file mode 100644
index 0000000..c44fcff
Binary files /dev/null and b/widgets/icons/goToEnd_24.png differ
diff --git a/widgets/icons/goToStart_24.png b/widgets/icons/goToStart_24.png
new file mode 100644
index 0000000..7fba17b
Binary files /dev/null and b/widgets/icons/goToStart_24.png differ
diff --git a/widgets/icons/grid_24.png b/widgets/icons/grid_24.png
new file mode 100644
index 0000000..6a7f2f9
Binary files /dev/null and b/widgets/icons/grid_24.png differ
diff --git a/widgets/icons/historyDelete_24.png b/widgets/icons/historyDelete_24.png
new file mode 100644
index 0000000..92b3694
Binary files /dev/null and b/widgets/icons/historyDelete_24.png differ
diff --git a/widgets/icons/history_24.png b/widgets/icons/history_24.png
new file mode 100644
index 0000000..4faf07f
Binary files /dev/null and b/widgets/icons/history_24.png differ
diff --git a/widgets/icons/home_24.png b/widgets/icons/home_24.png
new file mode 100644
index 0000000..33d418c
Binary files /dev/null and b/widgets/icons/home_24.png differ
diff --git a/widgets/icons/import_24.png b/widgets/icons/import_24.png
index f8b4d1d..1bfa8fa 100644
Binary files a/widgets/icons/import_24.png and b/widgets/icons/import_24.png differ
diff --git a/widgets/icons/info_16.png b/widgets/icons/info_16.png
index 3893572..cc80c8f 100644
Binary files a/widgets/icons/info_16.png and b/widgets/icons/info_16.png differ
diff --git a/widgets/icons/info_24.png b/widgets/icons/info_24.png
index 7f1c838..ee7119a 100644
Binary files a/widgets/icons/info_24.png and b/widgets/icons/info_24.png differ
diff --git a/widgets/icons/insertColumn_24.png b/widgets/icons/insertColumn_24.png
new file mode 100644
index 0000000..4ec2f07
Binary files /dev/null and b/widgets/icons/insertColumn_24.png differ
diff --git a/widgets/icons/insertRow_24.png b/widgets/icons/insertRow_24.png
new file mode 100644
index 0000000..33177b7
Binary files /dev/null and b/widgets/icons/insertRow_24.png differ
diff --git a/widgets/icons/invertMask_24.png b/widgets/icons/invertMask_24.png
new file mode 100644
index 0000000..0a96c02
Binary files /dev/null and b/widgets/icons/invertMask_24.png differ
diff --git a/widgets/icons/left_24.png b/widgets/icons/left_24.png
index 784fe72..48f4f2b 100644
Binary files a/widgets/icons/left_24.png and b/widgets/icons/left_24.png differ
diff --git a/widgets/icons/legend_24.png b/widgets/icons/legend_24.png
new file mode 100644
index 0000000..f6993b1
Binary files /dev/null and b/widgets/icons/legend_24.png differ
diff --git a/widgets/icons/loading_32.gif b/widgets/icons/loading_32.gif
new file mode 100644
index 0000000..e4ab783
Binary files /dev/null and b/widgets/icons/loading_32.gif differ
diff --git a/widgets/icons/locked_24.png b/widgets/icons/locked_24.png
new file mode 100644
index 0000000..ae0a8d3
Binary files /dev/null and b/widgets/icons/locked_24.png differ
diff --git a/widgets/icons/map_marker_24.png b/widgets/icons/map_marker_24.png
new file mode 100644
index 0000000..45c18cd
Binary files /dev/null and b/widgets/icons/map_marker_24.png differ
diff --git a/widgets/icons/merge_24.png b/widgets/icons/merge_24.png
new file mode 100644
index 0000000..4168886
Binary files /dev/null and b/widgets/icons/merge_24.png differ
diff --git a/widgets/icons/minusBlue_24.png b/widgets/icons/minusBlue_24.png
new file mode 100644
index 0000000..8dd44bc
Binary files /dev/null and b/widgets/icons/minusBlue_24.png differ
diff --git a/widgets/icons/new_24.png b/widgets/icons/new_24.png
new file mode 100644
index 0000000..6539c13
Binary files /dev/null and b/widgets/icons/new_24.png differ
diff --git a/widgets/icons/new_chart_24.png b/widgets/icons/new_chart_24.png
new file mode 100644
index 0000000..9ad4833
Binary files /dev/null and b/widgets/icons/new_chart_24.png differ
diff --git a/widgets/icons/new_script_24.png b/widgets/icons/new_script_24.png
new file mode 100644
index 0000000..752fa5b
Binary files /dev/null and b/widgets/icons/new_script_24.png differ
diff --git a/widgets/icons/openDocument_24.png b/widgets/icons/openDocument_24.png
new file mode 100644
index 0000000..8ddad4b
Binary files /dev/null and b/widgets/icons/openDocument_24.png differ
diff --git a/widgets/icons/open_24.png b/widgets/icons/open_24.png
index 0c1664a..a1e929a 100644
Binary files a/widgets/icons/open_24.png and b/widgets/icons/open_24.png differ
diff --git a/widgets/icons/optimize_24.png b/widgets/icons/optimize_24.png
new file mode 100644
index 0000000..d375cb9
Binary files /dev/null and b/widgets/icons/optimize_24.png differ
diff --git a/widgets/icons/package_24.png b/widgets/icons/package_24.png
new file mode 100644
index 0000000..12705da
Binary files /dev/null and b/widgets/icons/package_24.png differ
diff --git a/widgets/icons/panBlue_30.png b/widgets/icons/panBlue_30.png
new file mode 100644
index 0000000..4c1be33
Binary files /dev/null and b/widgets/icons/panBlue_30.png differ
diff --git a/widgets/icons/people_24.png b/widgets/icons/people_24.png
new file mode 100644
index 0000000..c485942
Binary files /dev/null and b/widgets/icons/people_24.png differ
diff --git a/widgets/icons/play_24.png b/widgets/icons/play_24.png
index be5a346..0161264 100644
Binary files a/widgets/icons/play_24.png and b/widgets/icons/play_24.png differ
diff --git a/widgets/icons/play_32.png b/widgets/icons/play_32.png
new file mode 100644
index 0000000..9947229
Binary files /dev/null and b/widgets/icons/play_32.png differ
diff --git a/widgets/icons/playerSquare_24.png b/widgets/icons/playerSquare_24.png
new file mode 100644
index 0000000..2ce42d6
Binary files /dev/null and b/widgets/icons/playerSquare_24.png differ
diff --git a/widgets/icons/plotInfo_24.png b/widgets/icons/plotInfo_24.png
new file mode 100644
index 0000000..00fbd1b
Binary files /dev/null and b/widgets/icons/plotInfo_24.png differ
diff --git a/widgets/icons/plot_clear_24.png b/widgets/icons/plot_clear_24.png
new file mode 100644
index 0000000..28acf69
Binary files /dev/null and b/widgets/icons/plot_clear_24.png differ
diff --git a/widgets/icons/plot_curve_24.png b/widgets/icons/plot_curve_24.png
new file mode 100644
index 0000000..9fbba7c
Binary files /dev/null and b/widgets/icons/plot_curve_24.png differ
diff --git a/widgets/icons/plot_scatter_24.png b/widgets/icons/plot_scatter_24.png
new file mode 100644
index 0000000..47841ed
Binary files /dev/null and b/widgets/icons/plot_scatter_24.png differ
diff --git a/widgets/icons/publishHTML_24.png b/widgets/icons/publishHTML_24.png
new file mode 100644
index 0000000..0bfa045
Binary files /dev/null and b/widgets/icons/publishHTML_24.png differ
diff --git a/widgets/icons/publishPDF_24.png b/widgets/icons/publishPDF_24.png
new file mode 100644
index 0000000..2fdac3b
Binary files /dev/null and b/widgets/icons/publishPDF_24.png differ
diff --git a/widgets/icons/refreshDouble_24.png b/widgets/icons/refreshDouble_24.png
new file mode 100644
index 0000000..1978a5c
Binary files /dev/null and b/widgets/icons/refreshDouble_24.png differ
diff --git a/widgets/icons/refresh_24.png b/widgets/icons/refresh_24.png
index 56bd2d3..39b950c 100644
Binary files a/widgets/icons/refresh_24.png and b/widgets/icons/refresh_24.png differ
diff --git a/widgets/icons/repeat_24.png b/widgets/icons/repeat_24.png
new file mode 100644
index 0000000..f1066bc
Binary files /dev/null and b/widgets/icons/repeat_24.png differ
diff --git a/widgets/icons/restore_24.png b/widgets/icons/restore_24.png
new file mode 100644
index 0000000..0912f8c
Binary files /dev/null and b/widgets/icons/restore_24.png differ
diff --git a/widgets/icons/results_24.png b/widgets/icons/results_24.png
new file mode 100644
index 0000000..0003fd2
Binary files /dev/null and b/widgets/icons/results_24.png differ
diff --git a/widgets/icons/right_24.png b/widgets/icons/right_24.png
index 07f60f4..8d01424 100644
Binary files a/widgets/icons/right_24.png and b/widgets/icons/right_24.png differ
diff --git a/widgets/icons/roi_24.png b/widgets/icons/roi_24.png
new file mode 100644
index 0000000..96eaaf9
Binary files /dev/null and b/widgets/icons/roi_24.png differ
diff --git a/widgets/icons/ruler_24.png b/widgets/icons/ruler_24.png
new file mode 100644
index 0000000..ec6134f
Binary files /dev/null and b/widgets/icons/ruler_24.png differ
diff --git a/widgets/icons/run3_24.png b/widgets/icons/run3_24.png
new file mode 100644
index 0000000..4131783
Binary files /dev/null and b/widgets/icons/run3_24.png differ
diff --git a/widgets/icons/runCode_24.png b/widgets/icons/runCode_24.png
new file mode 100644
index 0000000..ae2182f
Binary files /dev/null and b/widgets/icons/runCode_24.png differ
diff --git a/widgets/icons/run_24.png b/widgets/icons/run_24.png
new file mode 100644
index 0000000..8248c06
Binary files /dev/null and b/widgets/icons/run_24.png differ
diff --git a/widgets/icons/run_sequence_24.png b/widgets/icons/run_sequence_24.png
new file mode 100644
index 0000000..8156082
Binary files /dev/null and b/widgets/icons/run_sequence_24.png differ
diff --git a/widgets/icons/saveAllData_24.png b/widgets/icons/saveAllData_24.png
new file mode 100644
index 0000000..f7469ed
Binary files /dev/null and b/widgets/icons/saveAllData_24.png differ
diff --git a/widgets/icons/saveAs_24.png b/widgets/icons/saveAs_24.png
new file mode 100644
index 0000000..aa2be59
Binary files /dev/null and b/widgets/icons/saveAs_24.png differ
diff --git a/widgets/icons/saveClean_24.png b/widgets/icons/saveClean_24.png
new file mode 100644
index 0000000..01e741c
Binary files /dev/null and b/widgets/icons/saveClean_24.png differ
diff --git a/widgets/icons/saveCopyAs_24.png b/widgets/icons/saveCopyAs_24.png
new file mode 100644
index 0000000..8ea4ede
Binary files /dev/null and b/widgets/icons/saveCopyAs_24.png differ
diff --git a/widgets/icons/saveDirty_24.png b/widgets/icons/saveDirty_24.png
new file mode 100644
index 0000000..6c7a0c8
Binary files /dev/null and b/widgets/icons/saveDirty_24.png differ
diff --git a/widgets/icons/sessionSaveAs_24.png b/widgets/icons/sessionSaveAs_24.png
new file mode 100644
index 0000000..3c8e9df
Binary files /dev/null and b/widgets/icons/sessionSaveAs_24.png differ
diff --git a/widgets/icons/settings_24.png b/widgets/icons/settings_24.png
index df3b79d..32d0c5f 100644
Binary files a/widgets/icons/settings_24.png and b/widgets/icons/settings_24.png differ
diff --git a/widgets/icons/simulink_24.png b/widgets/icons/simulink_24.png
new file mode 100644
index 0000000..26d7924
Binary files /dev/null and b/widgets/icons/simulink_24.png differ
diff --git a/widgets/icons/sortAZ_24.png b/widgets/icons/sortAZ_24.png
new file mode 100644
index 0000000..ce689da
Binary files /dev/null and b/widgets/icons/sortAZ_24.png differ
diff --git a/widgets/icons/stats_24.png b/widgets/icons/stats_24.png
new file mode 100644
index 0000000..252c78e
Binary files /dev/null and b/widgets/icons/stats_24.png differ
diff --git a/widgets/icons/stepBack_24.png b/widgets/icons/stepBack_24.png
new file mode 100644
index 0000000..3a51519
Binary files /dev/null and b/widgets/icons/stepBack_24.png differ
diff --git a/widgets/icons/stepForward_24.png b/widgets/icons/stepForward_24.png
new file mode 100644
index 0000000..7eae57f
Binary files /dev/null and b/widgets/icons/stepForward_24.png differ
diff --git a/widgets/icons/stop_24.png b/widgets/icons/stop_24.png
index 0eb96cf..53b7a5e 100644
Binary files a/widgets/icons/stop_24.png and b/widgets/icons/stop_24.png differ
diff --git a/widgets/icons/swapLR_24.png b/widgets/icons/swapLR_24.png
new file mode 100644
index 0000000..c7328ec
Binary files /dev/null and b/widgets/icons/swapLR_24.png differ
diff --git a/widgets/icons/tableSettings_24.png b/widgets/icons/tableSettings_24.png
new file mode 100644
index 0000000..bd55805
Binary files /dev/null and b/widgets/icons/tableSettings_24.png differ
diff --git a/widgets/icons/table_24.png b/widgets/icons/table_24.png
new file mode 100644
index 0000000..eebbe47
Binary files /dev/null and b/widgets/icons/table_24.png differ
diff --git a/widgets/icons/target_24.png b/widgets/icons/target_24.png
new file mode 100644
index 0000000..d004d6d
Binary files /dev/null and b/widgets/icons/target_24.png differ
diff --git a/widgets/icons/title_24.png b/widgets/icons/title_24.png
new file mode 100644
index 0000000..a0f1a90
Binary files /dev/null and b/widgets/icons/title_24.png differ
diff --git a/widgets/icons/tools_24.png b/widgets/icons/tools_24.png
new file mode 100644
index 0000000..ba07e89
Binary files /dev/null and b/widgets/icons/tools_24.png differ
diff --git a/widgets/icons/undo_24.png b/widgets/icons/undo_24.png
new file mode 100644
index 0000000..9b26b04
Binary files /dev/null and b/widgets/icons/undo_24.png differ
diff --git a/widgets/icons/unlocked_24.png b/widgets/icons/unlocked_24.png
new file mode 100644
index 0000000..9191973
Binary files /dev/null and b/widgets/icons/unlocked_24.png differ
diff --git a/widgets/icons/up_24.png b/widgets/icons/up_24.png
index 8f9d73c..7945557 100644
Binary files a/widgets/icons/up_24.png and b/widgets/icons/up_24.png differ
diff --git a/widgets/icons/upload_24.png b/widgets/icons/upload_24.png
new file mode 100644
index 0000000..46ae628
Binary files /dev/null and b/widgets/icons/upload_24.png differ
diff --git a/widgets/icons/verify_24.png b/widgets/icons/verify_24.png
new file mode 100644
index 0000000..b460990
Binary files /dev/null and b/widgets/icons/verify_24.png differ
diff --git a/widgets/icons/warning_16.png b/widgets/icons/warning_16.png
index 4b0e0da..471df91 100644
Binary files a/widgets/icons/warning_16.png and b/widgets/icons/warning_16.png differ
diff --git a/widgets/icons/warning_24.png b/widgets/icons/warning_24.png
new file mode 100644
index 0000000..b79c354
Binary files /dev/null and b/widgets/icons/warning_24.png differ
diff --git a/widgets/icons/window_24.png b/widgets/icons/window_24.png
new file mode 100644
index 0000000..240ab65
Binary files /dev/null and b/widgets/icons/window_24.png differ
diff --git a/widgets/icons/zoomFit_24.png b/widgets/icons/zoomFit_24.png
new file mode 100644
index 0000000..f8b05a2
Binary files /dev/null and b/widgets/icons/zoomFit_24.png differ
diff --git a/widgets/icons/zoomIn_24.png b/widgets/icons/zoomIn_24.png
new file mode 100644
index 0000000..49b39f2
Binary files /dev/null and b/widgets/icons/zoomIn_24.png differ
diff --git a/widgets/icons/zoomOut_24.png b/widgets/icons/zoomOut_24.png
new file mode 100644
index 0000000..a56203e
Binary files /dev/null and b/widgets/icons/zoomOut_24.png differ