diff --git a/Doc/Example/CSharp.texportf b/Doc/Example/CSharp.texportf
deleted file mode 100644
index c3188a57..00000000
--- a/Doc/Example/CSharp.texportf
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace TomeExampleProject
-{
- using System.Collections.Generic;
-
-$RECORDS$
-}
diff --git a/Doc/Example/CSharp.texportv b/Doc/Example/CSharp.texportv
deleted file mode 100644
index d22d1b10..00000000
--- a/Doc/Example/CSharp.texportv
+++ /dev/null
@@ -1 +0,0 @@
- public $FIELD_TYPE$ $FIELD_ID$ { get; set; }
\ No newline at end of file
diff --git a/Doc/Example/Tome Example Project.tproj b/Doc/Example/Tome Example Project.tproj
deleted file mode 100644
index 4bdd1332..00000000
--- a/Doc/Example/Tome Example Project.tproj
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
- Tome Example Project
- en_US
-
- Tome Example Project
-
-
- Tome Example Project
-
-
- Tome Example Project
-
-
- CSV
- CSharp
- HTML
- INI
- JSON
- Slash XML
- StarCraft II XML
-
-
- Tome Example Project
-
-
diff --git a/Doc/Example/Tome Example Project.ttypes b/Doc/Example/Tome Example Project.ttypes
deleted file mode 100644
index 6556d9a0..00000000
--- a/Doc/Example/Tome Example Project.ttypes
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Doc/Example/CSharp.texport b/Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texport
similarity index 58%
rename from Doc/Example/CSharp.texport
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texport
index 87ebfe6f..e0f2ed23 100644
--- a/Doc/Example/CSharp.texport
+++ b/Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texport
@@ -1,17 +1,13 @@
- CSharp
+ Event Classes (CSharp)
.cs
-
-
-
-
diff --git a/Doc/Example/CSV.texportc b/Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportc
similarity index 100%
rename from Doc/Example/CSV.texportc
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportc
diff --git a/Doc/Example/CSV.texportcd b/Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportcd
similarity index 100%
rename from Doc/Example/CSV.texportcd
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportcd
diff --git a/Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportf b/Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportf
new file mode 100644
index 00000000..5924916f
--- /dev/null
+++ b/Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportf
@@ -0,0 +1,19 @@
+namespace TomeExampleProject.Events
+{
+ using System.Collections.Generic;
+
+ public class Event
+ {
+ ///
+ /// Data of this event.
+ ///
+ public object Data { get; set; }
+
+ ///
+ /// Type of this event.
+ ///
+ public EventType Type { get; set; }
+ }
+
+$RECORDS$
+}
diff --git a/Doc/Example/CSharp.texportl b/Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportl
similarity index 100%
rename from Doc/Example/CSharp.texportl
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportl
diff --git a/Doc/Example/CSV.texportrd b/Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportld
similarity index 100%
rename from Doc/Example/CSV.texportrd
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportld
diff --git a/Doc/Example/CSharp.texportc b/Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportli
similarity index 100%
rename from Doc/Example/CSharp.texportc
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportli
diff --git a/Doc/Example/CSharp.texportm b/Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportm
similarity index 100%
rename from Doc/Example/CSharp.texportm
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportm
diff --git a/Doc/Example/HTML.texportrd b/Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportmd
similarity index 100%
rename from Doc/Example/HTML.texportrd
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportmd
diff --git a/Doc/Example/CSharp.texportcd b/Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportmi
similarity index 100%
rename from Doc/Example/CSharp.texportcd
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportmi
diff --git a/Doc/Example/CSharp.texportr b/Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportr
similarity index 100%
rename from Doc/Example/CSharp.texportr
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportr
diff --git a/Doc/Example/CSharp.texportrd b/Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportrd
similarity index 100%
rename from Doc/Example/CSharp.texportrd
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportrd
diff --git a/Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportv b/Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportv
new file mode 100644
index 00000000..023d7646
--- /dev/null
+++ b/Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportv
@@ -0,0 +1,4 @@
+ ///
+ /// $FIELD_DESCRIPTION$
+ ///
+ public $FIELD_TYPE$ $FIELD_ID$ { get; set; }
\ No newline at end of file
diff --git a/Doc/Example/CSharp.texportvd b/Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportvd
similarity index 100%
rename from Doc/Example/CSharp.texportvd
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventClasses.texportvd
diff --git a/Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texport b/Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texport
new file mode 100644
index 00000000..da038cea
--- /dev/null
+++ b/Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texport
@@ -0,0 +1,15 @@
+
+
+ Event Deserializer (CSharp)
+ .cs
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Doc/Example/CSharp.texportld b/Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportc
similarity index 100%
rename from Doc/Example/CSharp.texportld
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportc
diff --git a/Doc/Example/CSharp.texportli b/Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportcd
similarity index 100%
rename from Doc/Example/CSharp.texportli
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportcd
diff --git a/Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportf b/Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportf
new file mode 100644
index 00000000..146330f3
--- /dev/null
+++ b/Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportf
@@ -0,0 +1,20 @@
+namespace TomeExampleProject.Events
+{
+ using System;
+ using System.IO;
+
+ public class BinaryEventDeserializer
+ {
+ public Event Deserialize(BinaryReader reader)
+ {
+ EventType type = (EventType)Enum.Parse(typeof(EventType), reader.ReadString());
+
+ switch (type)
+ {
+$RECORDS$
+ }
+
+ throw new ArgumentException(string.Format("Unknown event type: {0}", type), "reader");
+ }
+ }
+}
diff --git a/Doc/Example/CSharp.texportmd b/Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportl
similarity index 100%
rename from Doc/Example/CSharp.texportmd
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportl
diff --git a/Doc/Example/HTML.texportvd b/Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportld
similarity index 100%
rename from Doc/Example/HTML.texportvd
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportld
diff --git a/Doc/Example/CSharp.texportmi b/Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportli
similarity index 100%
rename from Doc/Example/CSharp.texportmi
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportli
diff --git a/Doc/Example/HTML.texportc b/Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportm
similarity index 100%
rename from Doc/Example/HTML.texportc
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportm
diff --git a/Doc/Example/INI.texportrd b/Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportmd
similarity index 100%
rename from Doc/Example/INI.texportrd
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportmd
diff --git a/Doc/Example/HTML.texportcd b/Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportmi
similarity index 100%
rename from Doc/Example/HTML.texportcd
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportmi
diff --git a/Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportr b/Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportr
new file mode 100644
index 00000000..5f6909cc
--- /dev/null
+++ b/Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportr
@@ -0,0 +1,4 @@
+ case EventType.$RECORD_ID$:
+ $RECORD_ID$ $RECORD_ID$ = new $RECORD_ID$();
+$RECORD_FIELDS$
+ return new Event { Type = EventType.$RECORD_ID$, Data = $RECORD_ID$ };
\ No newline at end of file
diff --git a/Doc/Example/INI.texportvd b/Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportrd
similarity index 100%
rename from Doc/Example/INI.texportvd
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportrd
diff --git a/Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportv b/Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportv
new file mode 100644
index 00000000..c90ba74d
--- /dev/null
+++ b/Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportv
@@ -0,0 +1 @@
+ $RECORD_ID$.$FIELD_ID$ = reader.Read$FIELD_TYPE$();
\ No newline at end of file
diff --git a/Doc/Example/Slash XML.texportcd b/Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportvd
similarity index 100%
rename from Doc/Example/Slash XML.texportcd
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventDeserializer.texportvd
diff --git a/Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texport b/Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texport
new file mode 100644
index 00000000..889a655d
--- /dev/null
+++ b/Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texport
@@ -0,0 +1,15 @@
+
+
+ Event Serializer (CSharp)
+ .cs
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Doc/Example/INI.texportc b/Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportc
similarity index 100%
rename from Doc/Example/INI.texportc
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportc
diff --git a/Doc/Example/INI.texportcd b/Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportcd
similarity index 100%
rename from Doc/Example/INI.texportcd
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportcd
diff --git a/Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportf b/Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportf
new file mode 100644
index 00000000..51bcd093
--- /dev/null
+++ b/Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportf
@@ -0,0 +1,20 @@
+namespace TomeExampleProject.Events
+{
+ using System;
+ using System.IO;
+
+ public class BinaryEventSerializer
+ {
+ public void Serialize(BinaryWriter writer, Event e)
+ {
+ writer.Write(e.Type.ToString());
+
+ switch (e.Type)
+ {
+$RECORDS$
+ }
+
+ throw new ArgumentException(string.Format("Unknown event type: {0}", e.Type), "e");
+ }
+ }
+}
diff --git a/Doc/Example/INI.texportl b/Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportl
similarity index 100%
rename from Doc/Example/INI.texportl
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportl
diff --git a/Doc/Example/Slash XML.texportld b/Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportld
similarity index 100%
rename from Doc/Example/Slash XML.texportld
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportld
diff --git a/Doc/Example/INI.texportld b/Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportli
similarity index 100%
rename from Doc/Example/INI.texportld
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportli
diff --git a/Doc/Example/INI.texportli b/Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportm
similarity index 100%
rename from Doc/Example/INI.texportli
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportm
diff --git a/Doc/Example/Slash XML.texportmd b/Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportmd
similarity index 100%
rename from Doc/Example/Slash XML.texportmd
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportmd
diff --git a/Doc/Example/INI.texportm b/Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportmi
similarity index 100%
rename from Doc/Example/INI.texportm
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportmi
diff --git a/Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportr b/Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportr
new file mode 100644
index 00000000..a1f900f7
--- /dev/null
+++ b/Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportr
@@ -0,0 +1,4 @@
+ case EventType.$RECORD_ID$:
+ $RECORD_ID$ $RECORD_ID$ = ($RECORD_ID$)e.Data;
+$RECORD_FIELDS$
+ return;
\ No newline at end of file
diff --git a/Doc/Example/Slash XML.texportrd b/Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportrd
similarity index 100%
rename from Doc/Example/Slash XML.texportrd
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportrd
diff --git a/Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportv b/Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportv
new file mode 100644
index 00000000..f995f751
--- /dev/null
+++ b/Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportv
@@ -0,0 +1 @@
+ writer.Write($RECORD_ID$.$FIELD_ID$);
\ No newline at end of file
diff --git a/Doc/Example/Slash XML.texportvd b/Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportvd
similarity index 100%
rename from Doc/Example/Slash XML.texportvd
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventSerializer.texportvd
diff --git a/Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texport b/Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texport
new file mode 100644
index 00000000..0bdac335
--- /dev/null
+++ b/Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texport
@@ -0,0 +1,15 @@
+
+
+ Event Types (CSharp)
+ .cs
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Doc/Example/INI.texportmd b/Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportc
similarity index 100%
rename from Doc/Example/INI.texportmd
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportc
diff --git a/Doc/Example/INI.texportmi b/Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportcd
similarity index 100%
rename from Doc/Example/INI.texportmi
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportcd
diff --git a/Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportf b/Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportf
new file mode 100644
index 00000000..49502644
--- /dev/null
+++ b/Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportf
@@ -0,0 +1,7 @@
+namespace TomeExampleProject.Events
+{
+ public enum EventType
+ {
+$RECORDS$
+ }
+}
diff --git a/Doc/Example/StarCraft II XML.texportc b/Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportl
similarity index 100%
rename from Doc/Example/StarCraft II XML.texportc
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportl
diff --git a/Doc/Example/StarCraft II XML.texportcd b/Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportld
similarity index 100%
rename from Doc/Example/StarCraft II XML.texportcd
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportld
diff --git a/Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportli b/Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportli
new file mode 100644
index 00000000..e69de29b
diff --git a/Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportm b/Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportm
new file mode 100644
index 00000000..e69de29b
diff --git a/Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportmd b/Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportmd
new file mode 100644
index 00000000..e69de29b
diff --git a/Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportmi b/Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportmi
new file mode 100644
index 00000000..e69de29b
diff --git a/Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportr b/Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportr
new file mode 100644
index 00000000..a4b520b6
--- /dev/null
+++ b/Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportr
@@ -0,0 +1 @@
+ $RECORD_ID$
\ No newline at end of file
diff --git a/Doc/Example/JSON.texportcd b/Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportrd
similarity index 100%
rename from Doc/Example/JSON.texportcd
rename to Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportrd
diff --git a/Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportv b/Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportv
new file mode 100644
index 00000000..e69de29b
diff --git a/Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportvd b/Doc/Examples/Tome Example Code Generation Project/CSharpEventTypes.texportvd
new file mode 100644
index 00000000..e69de29b
diff --git a/Doc/Examples/Tome Example Code Generation Project/Tome Example Code Generation Project.tcomp b/Doc/Examples/Tome Example Code Generation Project/Tome Example Code Generation Project.tcomp
new file mode 100644
index 00000000..d4268c89
--- /dev/null
+++ b/Doc/Examples/Tome Example Code Generation Project/Tome Example Code Generation Project.tcomp
@@ -0,0 +1,2 @@
+
+
diff --git a/Doc/Examples/Tome Example Code Generation Project/Tome Example Code Generation Project.tdata b/Doc/Examples/Tome Example Code Generation Project/Tome Example Code Generation Project.tdata
new file mode 100644
index 00000000..78c9d74e
--- /dev/null
+++ b/Doc/Examples/Tome Example Code Generation Project/Tome Example Code Generation Project.tdata
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Doc/Examples/Tome Example Code Generation Project/Tome Example Code Generation Project.tfields b/Doc/Examples/Tome Example Code Generation Project/Tome Example Code Generation Project.tfields
new file mode 100644
index 00000000..d3e5c612
--- /dev/null
+++ b/Doc/Examples/Tome Example Code Generation Project/Tome Example Code Generation Project.tfields
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Doc/Examples/Tome Example Code Generation Project/Tome Example Code Generation Project.tproj b/Doc/Examples/Tome Example Code Generation Project/Tome Example Code Generation Project.tproj
new file mode 100644
index 00000000..f0acdee9
--- /dev/null
+++ b/Doc/Examples/Tome Example Code Generation Project/Tome Example Code Generation Project.tproj
@@ -0,0 +1,23 @@
+
+
+ Tome Example Code Generation Project
+ en_US
+
+ Tome Example Code Generation Project
+
+
+ Tome Example Code Generation Project
+
+
+ Tome Example Code Generation Project
+
+
+ CSharpEventTypes.texport
+ CSharpEventClasses.texport
+ CSharpEventSerializer.texport
+ CSharpEventDeserializer.texport
+
+
+ Tome Example Code Generation Project
+
+
diff --git a/Doc/Examples/Tome Example Code Generation Project/Tome Example Code Generation Project.ttypes b/Doc/Examples/Tome Example Code Generation Project/Tome Example Code Generation Project.ttypes
new file mode 100644
index 00000000..806afdcd
--- /dev/null
+++ b/Doc/Examples/Tome Example Code Generation Project/Tome Example Code Generation Project.ttypes
@@ -0,0 +1,2 @@
+
+
diff --git a/Doc/Example/CSV.texport b/Doc/Examples/Tome Example Data Project/CSV.texport
similarity index 100%
rename from Doc/Example/CSV.texport
rename to Doc/Examples/Tome Example Data Project/CSV.texport
diff --git a/Doc/Examples/Tome Example Data Project/CSV.texportc b/Doc/Examples/Tome Example Data Project/CSV.texportc
new file mode 100644
index 00000000..e69de29b
diff --git a/Doc/Examples/Tome Example Data Project/CSV.texportcd b/Doc/Examples/Tome Example Data Project/CSV.texportcd
new file mode 100644
index 00000000..e69de29b
diff --git a/Doc/Example/CSV.texportf b/Doc/Examples/Tome Example Data Project/CSV.texportf
similarity index 100%
rename from Doc/Example/CSV.texportf
rename to Doc/Examples/Tome Example Data Project/CSV.texportf
diff --git a/Doc/Example/CSV.texportl b/Doc/Examples/Tome Example Data Project/CSV.texportl
similarity index 100%
rename from Doc/Example/CSV.texportl
rename to Doc/Examples/Tome Example Data Project/CSV.texportl
diff --git a/Doc/Example/CSV.texportld b/Doc/Examples/Tome Example Data Project/CSV.texportld
similarity index 100%
rename from Doc/Example/CSV.texportld
rename to Doc/Examples/Tome Example Data Project/CSV.texportld
diff --git a/Doc/Example/CSV.texportli b/Doc/Examples/Tome Example Data Project/CSV.texportli
similarity index 100%
rename from Doc/Example/CSV.texportli
rename to Doc/Examples/Tome Example Data Project/CSV.texportli
diff --git a/Doc/Example/CSV.texportm b/Doc/Examples/Tome Example Data Project/CSV.texportm
similarity index 100%
rename from Doc/Example/CSV.texportm
rename to Doc/Examples/Tome Example Data Project/CSV.texportm
diff --git a/Doc/Example/CSV.texportmd b/Doc/Examples/Tome Example Data Project/CSV.texportmd
similarity index 100%
rename from Doc/Example/CSV.texportmd
rename to Doc/Examples/Tome Example Data Project/CSV.texportmd
diff --git a/Doc/Example/CSV.texportmi b/Doc/Examples/Tome Example Data Project/CSV.texportmi
similarity index 100%
rename from Doc/Example/CSV.texportmi
rename to Doc/Examples/Tome Example Data Project/CSV.texportmi
diff --git a/Doc/Example/CSV.texportr b/Doc/Examples/Tome Example Data Project/CSV.texportr
similarity index 100%
rename from Doc/Example/CSV.texportr
rename to Doc/Examples/Tome Example Data Project/CSV.texportr
diff --git a/Doc/Example/StarCraft II XML.texportld b/Doc/Examples/Tome Example Data Project/CSV.texportrd
similarity index 100%
rename from Doc/Example/StarCraft II XML.texportld
rename to Doc/Examples/Tome Example Data Project/CSV.texportrd
diff --git a/Doc/Example/CSV.texportv b/Doc/Examples/Tome Example Data Project/CSV.texportv
similarity index 100%
rename from Doc/Example/CSV.texportv
rename to Doc/Examples/Tome Example Data Project/CSV.texportv
diff --git a/Doc/Example/CSV.texportvd b/Doc/Examples/Tome Example Data Project/CSV.texportvd
similarity index 100%
rename from Doc/Example/CSV.texportvd
rename to Doc/Examples/Tome Example Data Project/CSV.texportvd
diff --git a/Doc/Example/HTML.texport b/Doc/Examples/Tome Example Data Project/HTML.texport
similarity index 100%
rename from Doc/Example/HTML.texport
rename to Doc/Examples/Tome Example Data Project/HTML.texport
diff --git a/Doc/Examples/Tome Example Data Project/HTML.texportc b/Doc/Examples/Tome Example Data Project/HTML.texportc
new file mode 100644
index 00000000..e69de29b
diff --git a/Doc/Examples/Tome Example Data Project/HTML.texportcd b/Doc/Examples/Tome Example Data Project/HTML.texportcd
new file mode 100644
index 00000000..e69de29b
diff --git a/Doc/Example/HTML.texportf b/Doc/Examples/Tome Example Data Project/HTML.texportf
similarity index 100%
rename from Doc/Example/HTML.texportf
rename to Doc/Examples/Tome Example Data Project/HTML.texportf
diff --git a/Doc/Example/HTML.texportl b/Doc/Examples/Tome Example Data Project/HTML.texportl
similarity index 100%
rename from Doc/Example/HTML.texportl
rename to Doc/Examples/Tome Example Data Project/HTML.texportl
diff --git a/Doc/Example/HTML.texportld b/Doc/Examples/Tome Example Data Project/HTML.texportld
similarity index 100%
rename from Doc/Example/HTML.texportld
rename to Doc/Examples/Tome Example Data Project/HTML.texportld
diff --git a/Doc/Example/HTML.texportli b/Doc/Examples/Tome Example Data Project/HTML.texportli
similarity index 100%
rename from Doc/Example/HTML.texportli
rename to Doc/Examples/Tome Example Data Project/HTML.texportli
diff --git a/Doc/Example/HTML.texportm b/Doc/Examples/Tome Example Data Project/HTML.texportm
similarity index 100%
rename from Doc/Example/HTML.texportm
rename to Doc/Examples/Tome Example Data Project/HTML.texportm
diff --git a/Doc/Example/HTML.texportmd b/Doc/Examples/Tome Example Data Project/HTML.texportmd
similarity index 100%
rename from Doc/Example/HTML.texportmd
rename to Doc/Examples/Tome Example Data Project/HTML.texportmd
diff --git a/Doc/Example/HTML.texportmi b/Doc/Examples/Tome Example Data Project/HTML.texportmi
similarity index 100%
rename from Doc/Example/HTML.texportmi
rename to Doc/Examples/Tome Example Data Project/HTML.texportmi
diff --git a/Doc/Example/HTML.texportr b/Doc/Examples/Tome Example Data Project/HTML.texportr
similarity index 100%
rename from Doc/Example/HTML.texportr
rename to Doc/Examples/Tome Example Data Project/HTML.texportr
diff --git a/Doc/Example/StarCraft II XML.texportmd b/Doc/Examples/Tome Example Data Project/HTML.texportrd
similarity index 100%
rename from Doc/Example/StarCraft II XML.texportmd
rename to Doc/Examples/Tome Example Data Project/HTML.texportrd
diff --git a/Doc/Example/HTML.texportv b/Doc/Examples/Tome Example Data Project/HTML.texportv
similarity index 100%
rename from Doc/Example/HTML.texportv
rename to Doc/Examples/Tome Example Data Project/HTML.texportv
diff --git a/Doc/Example/StarCraft II XML.texportrd b/Doc/Examples/Tome Example Data Project/HTML.texportvd
similarity index 100%
rename from Doc/Example/StarCraft II XML.texportrd
rename to Doc/Examples/Tome Example Data Project/HTML.texportvd
diff --git a/Doc/Example/INI.texport b/Doc/Examples/Tome Example Data Project/INI.texport
similarity index 100%
rename from Doc/Example/INI.texport
rename to Doc/Examples/Tome Example Data Project/INI.texport
diff --git a/Doc/Examples/Tome Example Data Project/INI.texportc b/Doc/Examples/Tome Example Data Project/INI.texportc
new file mode 100644
index 00000000..e69de29b
diff --git a/Doc/Examples/Tome Example Data Project/INI.texportcd b/Doc/Examples/Tome Example Data Project/INI.texportcd
new file mode 100644
index 00000000..e69de29b
diff --git a/Doc/Example/INI.texportf b/Doc/Examples/Tome Example Data Project/INI.texportf
similarity index 100%
rename from Doc/Example/INI.texportf
rename to Doc/Examples/Tome Example Data Project/INI.texportf
diff --git a/Doc/Examples/Tome Example Data Project/INI.texportl b/Doc/Examples/Tome Example Data Project/INI.texportl
new file mode 100644
index 00000000..e69de29b
diff --git a/Doc/Examples/Tome Example Data Project/INI.texportld b/Doc/Examples/Tome Example Data Project/INI.texportld
new file mode 100644
index 00000000..e69de29b
diff --git a/Doc/Examples/Tome Example Data Project/INI.texportli b/Doc/Examples/Tome Example Data Project/INI.texportli
new file mode 100644
index 00000000..e69de29b
diff --git a/Doc/Examples/Tome Example Data Project/INI.texportm b/Doc/Examples/Tome Example Data Project/INI.texportm
new file mode 100644
index 00000000..e69de29b
diff --git a/Doc/Examples/Tome Example Data Project/INI.texportmd b/Doc/Examples/Tome Example Data Project/INI.texportmd
new file mode 100644
index 00000000..e69de29b
diff --git a/Doc/Examples/Tome Example Data Project/INI.texportmi b/Doc/Examples/Tome Example Data Project/INI.texportmi
new file mode 100644
index 00000000..e69de29b
diff --git a/Doc/Example/INI.texportr b/Doc/Examples/Tome Example Data Project/INI.texportr
similarity index 100%
rename from Doc/Example/INI.texportr
rename to Doc/Examples/Tome Example Data Project/INI.texportr
diff --git a/Doc/Example/StarCraft II XML.texportvd b/Doc/Examples/Tome Example Data Project/INI.texportrd
similarity index 100%
rename from Doc/Example/StarCraft II XML.texportvd
rename to Doc/Examples/Tome Example Data Project/INI.texportrd
diff --git a/Doc/Example/INI.texportv b/Doc/Examples/Tome Example Data Project/INI.texportv
similarity index 100%
rename from Doc/Example/INI.texportv
rename to Doc/Examples/Tome Example Data Project/INI.texportv
diff --git a/Doc/Examples/Tome Example Data Project/INI.texportvd b/Doc/Examples/Tome Example Data Project/INI.texportvd
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/Doc/Examples/Tome Example Data Project/INI.texportvd
@@ -0,0 +1 @@
+
diff --git a/Doc/Example/JSON.texport b/Doc/Examples/Tome Example Data Project/JSON.texport
similarity index 100%
rename from Doc/Example/JSON.texport
rename to Doc/Examples/Tome Example Data Project/JSON.texport
diff --git a/Doc/Example/JSON.texportc b/Doc/Examples/Tome Example Data Project/JSON.texportc
similarity index 100%
rename from Doc/Example/JSON.texportc
rename to Doc/Examples/Tome Example Data Project/JSON.texportc
diff --git a/Doc/Example/JSON.texportmd b/Doc/Examples/Tome Example Data Project/JSON.texportcd
similarity index 100%
rename from Doc/Example/JSON.texportmd
rename to Doc/Examples/Tome Example Data Project/JSON.texportcd
diff --git a/Doc/Example/JSON.texportf b/Doc/Examples/Tome Example Data Project/JSON.texportf
similarity index 100%
rename from Doc/Example/JSON.texportf
rename to Doc/Examples/Tome Example Data Project/JSON.texportf
diff --git a/Doc/Example/JSON.texportl b/Doc/Examples/Tome Example Data Project/JSON.texportl
similarity index 100%
rename from Doc/Example/JSON.texportl
rename to Doc/Examples/Tome Example Data Project/JSON.texportl
diff --git a/Doc/Example/JSON.texportld b/Doc/Examples/Tome Example Data Project/JSON.texportld
similarity index 100%
rename from Doc/Example/JSON.texportld
rename to Doc/Examples/Tome Example Data Project/JSON.texportld
diff --git a/Doc/Example/JSON.texportli b/Doc/Examples/Tome Example Data Project/JSON.texportli
similarity index 100%
rename from Doc/Example/JSON.texportli
rename to Doc/Examples/Tome Example Data Project/JSON.texportli
diff --git a/Doc/Example/JSON.texportm b/Doc/Examples/Tome Example Data Project/JSON.texportm
similarity index 100%
rename from Doc/Example/JSON.texportm
rename to Doc/Examples/Tome Example Data Project/JSON.texportm
diff --git a/Doc/Example/JSON.texportrd b/Doc/Examples/Tome Example Data Project/JSON.texportmd
similarity index 100%
rename from Doc/Example/JSON.texportrd
rename to Doc/Examples/Tome Example Data Project/JSON.texportmd
diff --git a/Doc/Example/JSON.texportmi b/Doc/Examples/Tome Example Data Project/JSON.texportmi
similarity index 100%
rename from Doc/Example/JSON.texportmi
rename to Doc/Examples/Tome Example Data Project/JSON.texportmi
diff --git a/Doc/Example/JSON.texportr b/Doc/Examples/Tome Example Data Project/JSON.texportr
similarity index 100%
rename from Doc/Example/JSON.texportr
rename to Doc/Examples/Tome Example Data Project/JSON.texportr
diff --git a/Doc/Example/JSON.texportvd b/Doc/Examples/Tome Example Data Project/JSON.texportrd
similarity index 100%
rename from Doc/Example/JSON.texportvd
rename to Doc/Examples/Tome Example Data Project/JSON.texportrd
diff --git a/Doc/Example/JSON.texportv b/Doc/Examples/Tome Example Data Project/JSON.texportv
similarity index 100%
rename from Doc/Example/JSON.texportv
rename to Doc/Examples/Tome Example Data Project/JSON.texportv
diff --git a/Doc/Examples/Tome Example Data Project/JSON.texportvd b/Doc/Examples/Tome Example Data Project/JSON.texportvd
new file mode 100644
index 00000000..7edb2fa5
--- /dev/null
+++ b/Doc/Examples/Tome Example Data Project/JSON.texportvd
@@ -0,0 +1 @@
+,
diff --git a/Doc/Example/Slash XML.texport b/Doc/Examples/Tome Example Data Project/Slash XML.texport
similarity index 100%
rename from Doc/Example/Slash XML.texport
rename to Doc/Examples/Tome Example Data Project/Slash XML.texport
diff --git a/Doc/Example/Slash XML.texportc b/Doc/Examples/Tome Example Data Project/Slash XML.texportc
similarity index 100%
rename from Doc/Example/Slash XML.texportc
rename to Doc/Examples/Tome Example Data Project/Slash XML.texportc
diff --git a/Doc/Examples/Tome Example Data Project/Slash XML.texportcd b/Doc/Examples/Tome Example Data Project/Slash XML.texportcd
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/Doc/Examples/Tome Example Data Project/Slash XML.texportcd
@@ -0,0 +1 @@
+
diff --git a/Doc/Example/Slash XML.texportf b/Doc/Examples/Tome Example Data Project/Slash XML.texportf
similarity index 100%
rename from Doc/Example/Slash XML.texportf
rename to Doc/Examples/Tome Example Data Project/Slash XML.texportf
diff --git a/Doc/Example/Slash XML.texportl b/Doc/Examples/Tome Example Data Project/Slash XML.texportl
similarity index 100%
rename from Doc/Example/Slash XML.texportl
rename to Doc/Examples/Tome Example Data Project/Slash XML.texportl
diff --git a/Doc/Examples/Tome Example Data Project/Slash XML.texportld b/Doc/Examples/Tome Example Data Project/Slash XML.texportld
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/Doc/Examples/Tome Example Data Project/Slash XML.texportld
@@ -0,0 +1 @@
+
diff --git a/Doc/Example/Slash XML.texportli b/Doc/Examples/Tome Example Data Project/Slash XML.texportli
similarity index 100%
rename from Doc/Example/Slash XML.texportli
rename to Doc/Examples/Tome Example Data Project/Slash XML.texportli
diff --git a/Doc/Example/Slash XML.texportm b/Doc/Examples/Tome Example Data Project/Slash XML.texportm
similarity index 100%
rename from Doc/Example/Slash XML.texportm
rename to Doc/Examples/Tome Example Data Project/Slash XML.texportm
diff --git a/Doc/Examples/Tome Example Data Project/Slash XML.texportmd b/Doc/Examples/Tome Example Data Project/Slash XML.texportmd
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/Doc/Examples/Tome Example Data Project/Slash XML.texportmd
@@ -0,0 +1 @@
+
diff --git a/Doc/Example/Slash XML.texportmi b/Doc/Examples/Tome Example Data Project/Slash XML.texportmi
similarity index 100%
rename from Doc/Example/Slash XML.texportmi
rename to Doc/Examples/Tome Example Data Project/Slash XML.texportmi
diff --git a/Doc/Example/Slash XML.texportr b/Doc/Examples/Tome Example Data Project/Slash XML.texportr
similarity index 100%
rename from Doc/Example/Slash XML.texportr
rename to Doc/Examples/Tome Example Data Project/Slash XML.texportr
diff --git a/Doc/Examples/Tome Example Data Project/Slash XML.texportrd b/Doc/Examples/Tome Example Data Project/Slash XML.texportrd
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/Doc/Examples/Tome Example Data Project/Slash XML.texportrd
@@ -0,0 +1 @@
+
diff --git a/Doc/Example/Slash XML.texportv b/Doc/Examples/Tome Example Data Project/Slash XML.texportv
similarity index 100%
rename from Doc/Example/Slash XML.texportv
rename to Doc/Examples/Tome Example Data Project/Slash XML.texportv
diff --git a/Doc/Examples/Tome Example Data Project/Slash XML.texportvd b/Doc/Examples/Tome Example Data Project/Slash XML.texportvd
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/Doc/Examples/Tome Example Data Project/Slash XML.texportvd
@@ -0,0 +1 @@
+
diff --git a/Doc/Example/StarCraft II XML.texport b/Doc/Examples/Tome Example Data Project/StarCraft II XML.texport
similarity index 100%
rename from Doc/Example/StarCraft II XML.texport
rename to Doc/Examples/Tome Example Data Project/StarCraft II XML.texport
diff --git a/Doc/Examples/Tome Example Data Project/StarCraft II XML.texportc b/Doc/Examples/Tome Example Data Project/StarCraft II XML.texportc
new file mode 100644
index 00000000..e69de29b
diff --git a/Doc/Examples/Tome Example Data Project/StarCraft II XML.texportcd b/Doc/Examples/Tome Example Data Project/StarCraft II XML.texportcd
new file mode 100644
index 00000000..e69de29b
diff --git a/Doc/Example/StarCraft II XML.texportf b/Doc/Examples/Tome Example Data Project/StarCraft II XML.texportf
similarity index 100%
rename from Doc/Example/StarCraft II XML.texportf
rename to Doc/Examples/Tome Example Data Project/StarCraft II XML.texportf
diff --git a/Doc/Example/StarCraft II XML.texportl b/Doc/Examples/Tome Example Data Project/StarCraft II XML.texportl
similarity index 100%
rename from Doc/Example/StarCraft II XML.texportl
rename to Doc/Examples/Tome Example Data Project/StarCraft II XML.texportl
diff --git a/Doc/Examples/Tome Example Data Project/StarCraft II XML.texportld b/Doc/Examples/Tome Example Data Project/StarCraft II XML.texportld
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/Doc/Examples/Tome Example Data Project/StarCraft II XML.texportld
@@ -0,0 +1 @@
+
diff --git a/Doc/Example/StarCraft II XML.texportli b/Doc/Examples/Tome Example Data Project/StarCraft II XML.texportli
similarity index 100%
rename from Doc/Example/StarCraft II XML.texportli
rename to Doc/Examples/Tome Example Data Project/StarCraft II XML.texportli
diff --git a/Doc/Example/StarCraft II XML.texportm b/Doc/Examples/Tome Example Data Project/StarCraft II XML.texportm
similarity index 100%
rename from Doc/Example/StarCraft II XML.texportm
rename to Doc/Examples/Tome Example Data Project/StarCraft II XML.texportm
diff --git a/Doc/Examples/Tome Example Data Project/StarCraft II XML.texportmd b/Doc/Examples/Tome Example Data Project/StarCraft II XML.texportmd
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/Doc/Examples/Tome Example Data Project/StarCraft II XML.texportmd
@@ -0,0 +1 @@
+
diff --git a/Doc/Example/StarCraft II XML.texportmi b/Doc/Examples/Tome Example Data Project/StarCraft II XML.texportmi
similarity index 100%
rename from Doc/Example/StarCraft II XML.texportmi
rename to Doc/Examples/Tome Example Data Project/StarCraft II XML.texportmi
diff --git a/Doc/Example/StarCraft II XML.texportr b/Doc/Examples/Tome Example Data Project/StarCraft II XML.texportr
similarity index 100%
rename from Doc/Example/StarCraft II XML.texportr
rename to Doc/Examples/Tome Example Data Project/StarCraft II XML.texportr
diff --git a/Doc/Examples/Tome Example Data Project/StarCraft II XML.texportrd b/Doc/Examples/Tome Example Data Project/StarCraft II XML.texportrd
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/Doc/Examples/Tome Example Data Project/StarCraft II XML.texportrd
@@ -0,0 +1 @@
+
diff --git a/Doc/Example/StarCraft II XML.texportv b/Doc/Examples/Tome Example Data Project/StarCraft II XML.texportv
similarity index 100%
rename from Doc/Example/StarCraft II XML.texportv
rename to Doc/Examples/Tome Example Data Project/StarCraft II XML.texportv
diff --git a/Doc/Examples/Tome Example Data Project/StarCraft II XML.texportvd b/Doc/Examples/Tome Example Data Project/StarCraft II XML.texportvd
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/Doc/Examples/Tome Example Data Project/StarCraft II XML.texportvd
@@ -0,0 +1 @@
+
diff --git a/Doc/Example/Tome Example Project.tcomp b/Doc/Examples/Tome Example Data Project/Tome Example Data Project.tcomp
similarity index 100%
rename from Doc/Example/Tome Example Project.tcomp
rename to Doc/Examples/Tome Example Data Project/Tome Example Data Project.tcomp
diff --git a/Doc/Example/Tome Example Project.tdata b/Doc/Examples/Tome Example Data Project/Tome Example Data Project.tdata
similarity index 100%
rename from Doc/Example/Tome Example Project.tdata
rename to Doc/Examples/Tome Example Data Project/Tome Example Data Project.tdata
diff --git a/Doc/Example/Tome Example Project.tfields b/Doc/Examples/Tome Example Data Project/Tome Example Data Project.tfields
similarity index 73%
rename from Doc/Example/Tome Example Project.tfields
rename to Doc/Examples/Tome Example Data Project/Tome Example Data Project.tfields
index 38417f56..578b58ed 100644
--- a/Doc/Example/Tome Example Project.tfields
+++ b/Doc/Examples/Tome Example Data Project/Tome Example Data Project.tfields
@@ -9,45 +9,23 @@
-
-
-
-
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
+
+
diff --git a/Doc/Examples/Tome Example Data Project/Tome Example Data Project.tproj b/Doc/Examples/Tome Example Data Project/Tome Example Data Project.tproj
new file mode 100644
index 00000000..f143cc98
--- /dev/null
+++ b/Doc/Examples/Tome Example Data Project/Tome Example Data Project.tproj
@@ -0,0 +1,26 @@
+
+
+ Tome Example Data Project
+ en_US
+
+ Tome Example Data Project.tcomp
+
+
+ Tome Example Data Project.tfields
+
+
+ Tome Example Data Project.tdata
+
+
+ CSV.texport
+ HTML.texport
+ INI.texport
+ JSON.texport
+ Slash XML.texport
+ StarCraft II XML.texport
+ YAML.texport
+
+
+ Tome Example Data Project.ttypes
+
+
diff --git a/Doc/Examples/Tome Example Data Project/Tome Example Data Project.ttypes b/Doc/Examples/Tome Example Data Project/Tome Example Data Project.ttypes
new file mode 100644
index 00000000..10b52ee6
--- /dev/null
+++ b/Doc/Examples/Tome Example Data Project/Tome Example Data Project.ttypes
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Doc/Examples/Tome Example Data Project/YAML.texport b/Doc/Examples/Tome Example Data Project/YAML.texport
new file mode 100644
index 00000000..08a10bbb
--- /dev/null
+++ b/Doc/Examples/Tome Example Data Project/YAML.texport
@@ -0,0 +1,8 @@
+
+
+ YAML
+ .yaml
+
+
+
+
diff --git a/Doc/Examples/Tome Example Data Project/YAML.texportc b/Doc/Examples/Tome Example Data Project/YAML.texportc
new file mode 100644
index 00000000..f0d84ae4
--- /dev/null
+++ b/Doc/Examples/Tome Example Data Project/YAML.texportc
@@ -0,0 +1 @@
+ - $COMPONENT_NAME$
\ No newline at end of file
diff --git a/Doc/Examples/Tome Example Data Project/YAML.texportcd b/Doc/Examples/Tome Example Data Project/YAML.texportcd
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/Doc/Examples/Tome Example Data Project/YAML.texportcd
@@ -0,0 +1 @@
+
diff --git a/Doc/Examples/Tome Example Data Project/YAML.texportf b/Doc/Examples/Tome Example Data Project/YAML.texportf
new file mode 100644
index 00000000..7f6e9da1
--- /dev/null
+++ b/Doc/Examples/Tome Example Data Project/YAML.texportf
@@ -0,0 +1 @@
+$RECORDS$
diff --git a/Doc/Examples/Tome Example Data Project/YAML.texportl b/Doc/Examples/Tome Example Data Project/YAML.texportl
new file mode 100644
index 00000000..b70bf5e9
--- /dev/null
+++ b/Doc/Examples/Tome Example Data Project/YAML.texportl
@@ -0,0 +1,2 @@
+ - $FIELD_ID$:
+$FIELD_VALUE$
\ No newline at end of file
diff --git a/Doc/Examples/Tome Example Data Project/YAML.texportld b/Doc/Examples/Tome Example Data Project/YAML.texportld
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/Doc/Examples/Tome Example Data Project/YAML.texportld
@@ -0,0 +1 @@
+
diff --git a/Doc/Examples/Tome Example Data Project/YAML.texportli b/Doc/Examples/Tome Example Data Project/YAML.texportli
new file mode 100644
index 00000000..4ce629f4
--- /dev/null
+++ b/Doc/Examples/Tome Example Data Project/YAML.texportli
@@ -0,0 +1 @@
+ - $LIST_ITEM$
\ No newline at end of file
diff --git a/Doc/Examples/Tome Example Data Project/YAML.texportm b/Doc/Examples/Tome Example Data Project/YAML.texportm
new file mode 100644
index 00000000..b70bf5e9
--- /dev/null
+++ b/Doc/Examples/Tome Example Data Project/YAML.texportm
@@ -0,0 +1,2 @@
+ - $FIELD_ID$:
+$FIELD_VALUE$
\ No newline at end of file
diff --git a/Doc/Examples/Tome Example Data Project/YAML.texportmd b/Doc/Examples/Tome Example Data Project/YAML.texportmd
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/Doc/Examples/Tome Example Data Project/YAML.texportmd
@@ -0,0 +1 @@
+
diff --git a/Doc/Examples/Tome Example Data Project/YAML.texportmi b/Doc/Examples/Tome Example Data Project/YAML.texportmi
new file mode 100644
index 00000000..98138363
--- /dev/null
+++ b/Doc/Examples/Tome Example Data Project/YAML.texportmi
@@ -0,0 +1 @@
+ $FIELD_KEY$: $FIELD_VALUE$
\ No newline at end of file
diff --git a/Doc/Examples/Tome Example Data Project/YAML.texportr b/Doc/Examples/Tome Example Data Project/YAML.texportr
new file mode 100644
index 00000000..90cb1afb
--- /dev/null
+++ b/Doc/Examples/Tome Example Data Project/YAML.texportr
@@ -0,0 +1,7 @@
+---
+id: $RECORD_ID$
+parent: $RECORD_PARENT$
+attributes:
+$RECORD_FIELDS$
+components:
+$RECORD_COMPONENTS$
\ No newline at end of file
diff --git a/Doc/Examples/Tome Example Data Project/YAML.texportrd b/Doc/Examples/Tome Example Data Project/YAML.texportrd
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/Doc/Examples/Tome Example Data Project/YAML.texportrd
@@ -0,0 +1 @@
+
diff --git a/Doc/Examples/Tome Example Data Project/YAML.texportv b/Doc/Examples/Tome Example Data Project/YAML.texportv
new file mode 100644
index 00000000..b9247f59
--- /dev/null
+++ b/Doc/Examples/Tome Example Data Project/YAML.texportv
@@ -0,0 +1 @@
+ - $FIELD_ID$: $FIELD_VALUE$
\ No newline at end of file
diff --git a/Doc/Examples/Tome Example Data Project/YAML.texportvd b/Doc/Examples/Tome Example Data Project/YAML.texportvd
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/Doc/Examples/Tome Example Data Project/YAML.texportvd
@@ -0,0 +1 @@
+
diff --git a/Doc/Manual/Images/EditFieldValue.png b/Doc/Manual/Images/EditFieldValue.png
index 3c5b878f..6b7bf175 100644
Binary files a/Doc/Manual/Images/EditFieldValue.png and b/Doc/Manual/Images/EditFieldValue.png differ
diff --git a/Doc/Manual/Images/RecordWithComponents.png b/Doc/Manual/Images/RecordWithComponents.png
deleted file mode 100644
index c9fbaf1c..00000000
Binary files a/Doc/Manual/Images/RecordWithComponents.png and /dev/null differ
diff --git a/Media/Icons/Output_16xLG.png b/Media/Icons/Output_16xLG.png
new file mode 100644
index 00000000..43f00d3d
Binary files /dev/null and b/Media/Icons/Output_16xLG.png differ
diff --git a/Project/Tome.pro b/Project/Tome.pro
index 2729b22d..33604f42 100644
--- a/Project/Tome.pro
+++ b/Project/Tome.pro
@@ -13,25 +13,25 @@ TEMPLATE = app
# Expose application version in Windows property window and in application code.
# http://www.openguru.com/2009/11/qt-best-way-to-set-application-version.html
-VERSION = 0.6
-VERSION_NAME = Hydra
+VERSION = 0.7
+VERSION_NAME = Manticore
DEFINES += APP_VERSION=\\\"$$VERSION\\\"
DEFINES += APP_VERSION_NAME=\\\"$$VERSION_NAME\\\"
RC_ICONS = ../Media/Icons/Tome.ico
ICON = ../Media/Icons/Tome.icns
-Debug:DESTDIR = ../../Bin/debug
-Debug:OBJECTS_DIR = ../../Obj/debug
-Debug:MOC_DIR = ../../Obj/debug/.moc
-Debug:RCC_DIR = ../../Obj/debug/.rcc
-Debug:UI_DIR = ../../Obj/debug/.ui
+Debug:DESTDIR = ../../Bin/debug/Tome
+Debug:OBJECTS_DIR = ../../Obj/debug/Tome
+Debug:MOC_DIR = ../../Obj/debug/Tome/.moc
+Debug:RCC_DIR = ../../Obj/debug/Tome/.rcc
+Debug:UI_DIR = ../../Obj/debug/Tome/.ui
-Release:DESTDIR = ../../Bin/release
-Release:OBJECTS_DIR = ../../Obj/release
-Release:MOC_DIR = ../../Obj/release/.moc
-Release:RCC_DIR = ../../Obj/release/.rcc
-Release:UI_DIR = ../../Obj/release/.ui
+Release:DESTDIR = ../../Bin/release/Tome
+Release:OBJECTS_DIR = ../../Obj/release/Tome
+Release:MOC_DIR = ../../Obj/release/Tome/.moc
+Release:RCC_DIR = ../../Obj/release/Tome/.rcc
+Release:UI_DIR = ../../Obj/release/Tome/.ui
SOURCES += ../Source/Tome/main.cpp \
../Source/Tome/Core/mainwindow.cpp \
@@ -112,7 +112,13 @@ SOURCES += ../Source/Tome/main.cpp \
../Source/Tome/Features/Facets/Controller/minimumrealvaluefacet.cpp \
../Source/Tome/Features/Facets/Controller/maximumstringlengthfacet.cpp \
../Source/Tome/Features/Facets/Controller/requiredreferenceancestorfacet.cpp \
- ../Source/Tome/Features/Facets/Model/facetcontext.cpp
+ ../Source/Tome/Features/Facets/Model/facetcontext.cpp \
+ ../Source/Tome/Features/Records/Controller/recordnamevalidator.cpp \
+ ../Source/Tome/Features/Diagnostics/Controller/messagehandlers.cpp \
+ ../Source/Tome/Features/Diagnostics/Controller/filemessagehandler.cpp \
+ ../Source/Tome/Features/Diagnostics/View/outputdockwidget.cpp \
+ ../Source/Tome/Features/Types/View/derivedtypewindow.cpp \
+ ../Source/Tome/Features/Integrity/Controller/typefacetviolatedtask.cpp
HEADERS += ../Source/Tome/Core/mainwindow.h \
../Source/Tome/Features/Types/Model/builtintype.h \
@@ -215,7 +221,13 @@ HEADERS += ../Source/Tome/Core/mainwindow.h \
../Source/Tome/Features/Facets/Controller/minimumrealvaluefacet.h \
../Source/Tome/Features/Facets/Controller/maximumstringlengthfacet.h \
../Source/Tome/Features/Facets/Controller/requiredreferenceancestorfacet.h \
- ../Source/Tome/Features/Facets/Model/facetcontext.h
+ ../Source/Tome/Features/Facets/Model/facetcontext.h \
+ ../Source/Tome/Features/Records/Controller/recordnamevalidator.h \
+ ../Source/Tome/Features/Diagnostics/Controller/messagehandlers.h \
+ ../Source/Tome/Features/Diagnostics/Controller/filemessagehandler.h \
+ ../Source/Tome/Features/Diagnostics/View/outputdockwidget.h \
+ ../Source/Tome/Features/Types/View/derivedtypewindow.h \
+ ../Source/Tome/Features/Integrity/Controller/typefacetviolatedtask.h
FORMS += ../Source/Tome/Core/mainwindow.ui \
../Source/Tome/Features/Help/View/aboutwindow.ui \
@@ -236,7 +248,8 @@ FORMS += ../Source/Tome/Core/mainwindow.ui \
../Source/Tome/Features/Fields/View/mapitemwindow.ui \
../Source/Tome/Features/Search/View/findrecordwindow.ui \
../Source/Tome/Features/Projects/View/projectoverviewwindow.ui \
- ../Source/Tome/Features/Settings/View/usersettingswindow.ui
+ ../Source/Tome/Features/Settings/View/usersettingswindow.ui \
+ ../Source/Tome/Features/Types/View/derivedtypewindow.ui
RESOURCES += \
tome.qrc
diff --git a/Project/TomeIssueReporter.pro b/Project/TomeIssueReporter.pro
new file mode 100644
index 00000000..0a80a269
--- /dev/null
+++ b/Project/TomeIssueReporter.pro
@@ -0,0 +1,36 @@
+#-------------------------------------------------
+#
+# Project created by QtCreator 2016-11-12T01:17:57
+#
+#-------------------------------------------------
+
+QT += core gui
+QT += network
+
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
+
+TARGET = TomeIssueReporter
+TEMPLATE = app
+
+RC_ICONS = ../Media/Icons/Tome.ico
+ICON = ../Media/Icons/Tome.icns
+
+Debug:DESTDIR = ../../Bin/debug/TomeIssueReporter
+Debug:OBJECTS_DIR = ../../Obj/debug/TomeIssueReporter
+Debug:MOC_DIR = ../../Obj/debug/TomeIssueReporter/.moc
+Debug:RCC_DIR = ../../Obj/debug/TomeIssueReporter/.rcc
+Debug:UI_DIR = ../../Obj/debug/TomeIssueReporter/.ui
+
+Release:DESTDIR = ../../Bin/release/TomeIssueReporter
+Release:OBJECTS_DIR = ../../Obj/release/TomeIssueReporter
+Release:MOC_DIR = ../../Obj/release/TomeIssueReporter/.moc
+Release:RCC_DIR = ../../Obj/release/TomeIssueReporter/.rcc
+Release:UI_DIR = ../../Obj/release/TomeIssueReporter/.ui
+
+SOURCES += ../Source/TomeIssueReporter/main.cpp\
+ ../Source/TomeIssueReporter/mainwindow.cpp
+
+HEADERS += ../Source/TomeIssueReporter/mainwindow.h \
+ ../Source/TomeIssueReporter/config.h
+
+FORMS += ../Source/TomeIssueReporter/mainwindow.ui
diff --git a/Project/tome.qrc b/Project/tome.qrc
index 72e623a6..e8385009 100644
--- a/Project/tome.qrc
+++ b/Project/tome.qrc
@@ -19,5 +19,6 @@
../Media/Icons/Tome.png
../Media/Icons/lock_16xLG.png
../Media/Icons/Find_5650.png
+ ../Media/Icons/Output_16xLG.png
diff --git a/README.md b/README.md
index 6d14b65b..fe4b2b28 100644
--- a/README.md
+++ b/README.md
@@ -64,7 +64,7 @@ Tome is developed using the [GitFlow branching model](http://nvie.com/posts/a-su
### Step 3: Implement your feature or bugfix
-Tome is based on [Qt 5.4](http://www.qt.io/).
+Tome is based on [Qt 5.7](http://www.qt.io/).
You might also take a look at our [development wiki](https://github.com/npruehs/game-data-editor/wiki) in order to get a better understanding of how everything's tied together.
diff --git a/Source/Tome/Core/commandlineoptions.cpp b/Source/Tome/Core/commandlineoptions.cpp
index 631fb559..cee8c081 100644
--- a/Source/Tome/Core/commandlineoptions.cpp
+++ b/Source/Tome/Core/commandlineoptions.cpp
@@ -34,6 +34,13 @@ void CommandLineOptions::parse(int argc, char* argv[])
i = i + 2;
continue;
}
+
+ // Parse project path.
+ if (argv[i][0] != '-')
+ {
+ this->projectPath = QString(argv[i]);
+ continue;
+ }
}
this->argc = argc;
this->argv = argv;
diff --git a/Source/Tome/Core/controller.cpp b/Source/Tome/Core/controller.cpp
index dce77dea..4aea3086 100644
--- a/Source/Tome/Core/controller.cpp
+++ b/Source/Tome/Core/controller.cpp
@@ -2,15 +2,19 @@
#include
+#include
#include
#include
#include
+#include
#include
#include "commandlineoptions.h"
#include "mainwindow.h"
#include "../Features/Components/Controller/componentscontroller.h"
#include "../Features/Components/Controller/componentsetserializer.h"
+#include "../Features/Diagnostics/Controller/filemessagehandler.h"
+#include "../Features/Diagnostics/Controller/messagehandlers.h"
#include "../Features/Export/Controller/exportcontroller.h"
#include "../Features/Export/Controller/exporttemplateserializer.h"
#include "../Features/Facets/Controller/facetscontroller.h"
@@ -29,6 +33,7 @@
#include "../Features/Integrity/Controller/mapkeytypenotsupportedtask.h"
#include "../Features/Integrity/Controller/mapvaluetypedoesnotexisttask.h"
#include "../Features/Integrity/Controller/mapvaluetypenotsupportedtask.h"
+#include "../Features/Integrity/Controller/typefacetviolatedtask.h"
#include "../Features/Projects/Controller/projectserializer.h"
#include "../Features/Projects/Model/project.h"
#include "../Features/Records/Controller/recordscontroller.h"
@@ -69,13 +74,14 @@ Controller::Controller(CommandLineOptions* options) :
componentsController(new ComponentsController()),
fieldDefinitionsController(new FieldDefinitionsController()),
typesController(new TypesController()),
- recordsController(new RecordsController(*this->fieldDefinitionsController)),
+ recordsController(new RecordsController(*this->fieldDefinitionsController, *this->typesController)),
exportController(new ExportController(*this->fieldDefinitionsController, *this->recordsController, *this->typesController)),
settingsController(new SettingsController()),
- tasksController(new TasksController(*this->componentsController, *this->fieldDefinitionsController, *this->recordsController, *this->typesController)),
+ facetsController(new FacetsController(*this->recordsController, *this->typesController)),
+ tasksController(new TasksController(*this->componentsController, *this->facetsController, *this->fieldDefinitionsController, *this->recordsController, *this->typesController)),
findUsagesController(new FindUsagesController(*this->fieldDefinitionsController, *this->recordsController, *this->typesController)),
findRecordController(new FindRecordController(*this->recordsController)),
- facetsController(new FacetsController()),
+ recordSetSerializer(new RecordSetSerializer()),
mainWindow(0)
{
// Setup tasks.
@@ -86,6 +92,7 @@ Controller::Controller(CommandLineOptions* options) :
this->tasksController->addTask(new MapKeyTypeNotSupportedTask());
this->tasksController->addTask(new MapValueTypeDoesNotExistTask());
this->tasksController->addTask(new MapValueTypeNotSupportedTask());
+ this->tasksController->addTask(new TypeFacetViolatedTask());
// Register facets.
this->facetsController->registerFacet(new MinimumIntegerValueFacet());
@@ -94,6 +101,13 @@ Controller::Controller(CommandLineOptions* options) :
this->facetsController->registerFacet(new MaximumRealValueFacet());
this->facetsController->registerFacet(new MaximumStringLengthFacet());
this->facetsController->registerFacet(new RequiredReferenceAncestorFacet());
+
+ // Connect signals.
+ connect(
+ this->recordSetSerializer,
+ SIGNAL(progressChanged(QString, QString, int, int)),
+ SLOT(onProgressChanged(QString, QString, int, int))
+ );
}
Controller::~Controller()
@@ -113,6 +127,7 @@ Controller::~Controller()
delete this->findUsagesController;
delete this->findRecordController;
delete this->facetsController;
+ delete this->recordSetSerializer;
delete this->options;
}
@@ -169,12 +184,31 @@ FacetsController&Controller::getFacetsController()
int Controller::start()
{
+ // Install message handlers.
+ if (FileMessageHandler::init())
+ {
+ MessageHandlers::addMessageHandler(FileMessageHandler::handleMessage);
+ }
+
+ // Log system information.
+ qInfo(QString("Tome Version: %1").arg(QApplication::instance()->applicationVersion()).toUtf8().constData());
+ qInfo(QString("Qt Build Architecture: %1").arg(QSysInfo::buildAbi()).toUtf8().constData());
+ qInfo(QString("CPU Architecture: %1").arg(QSysInfo::currentCpuArchitecture()).toUtf8().constData());
+ qInfo(QString("OS: %1 %2").arg(QSysInfo::prettyProductName(), QSysInfo::kernelVersion()).toUtf8().constData());
+ qInfo(QString("Machine Host Name: %1").arg(QSysInfo::machineHostName()).toUtf8().constData());
+
if (!this->options->noGui)
{
+ qInfo("Setting up main window.");
+
// Setup view.
this->mainWindow = new MainWindow(this);
this->mainWindow->show();
}
+ else
+ {
+ qInfo("Running without main window.");
+ }
if (!this->options->projectPath.isEmpty())
{
@@ -182,9 +216,9 @@ int Controller::start()
{
this->openProject(this->options->projectPath);
}
- catch (std::runtime_error&)
+ catch (std::runtime_error& e)
{
- // TODO(np): Write error log.
+ qCritical(e.what());
return 1;
}
}
@@ -195,7 +229,7 @@ int Controller::start()
{
if (!this->exportController->hasRecordExportTemplate(this->options->exportTemplateName))
{
- // TODO(np): Write error log.
+ qCritical(QString("Export template not found: %1").arg(this->options->exportTemplateName).toUtf8().constData());
return 1;
}
@@ -211,9 +245,9 @@ int Controller::start()
{
this->exportController->exportRecords(exportTemplate, filePath);
}
- catch (std::runtime_error&)
+ catch (std::runtime_error& e)
{
- // TODO(np): Write error log.
+ qCritical(e.what());
return 1;
}
}
@@ -223,6 +257,8 @@ int Controller::start()
void Controller::createProject(const QString& projectName, const QString& projectPath)
{
+ qInfo(QString("Creating new project %1 at %2.").arg(projectName, projectPath).toUtf8().constData());
+
// Create new project.
QSharedPointer newProject = QSharedPointer::create();
newProject->name = projectName;
@@ -290,15 +326,21 @@ void Controller::loadComponentSet(const QString& projectPath, ComponentSet& comp
buildFullFilePath(componentSet.name, projectPath, ComponentFileExtension);
QFile componentFile(fullComponentSetPath);
+
+ qInfo(QString("Opening components file %1.").arg(fullComponentSetPath).toUtf8().constData());
+
if (componentFile.open(QIODevice::ReadOnly))
{
try
{
componentSerializer.deserialize(componentFile, componentSet);
+ qInfo(QString("Opened components file %1 with %2 components.")
+ .arg(fullComponentSetPath, QString::number(componentSet.components.count())).toUtf8().constData());
}
catch (const std::runtime_error& e)
{
QString errorMessage = QObject::tr("File could not be read: ") + fullComponentSetPath + "\r\n" + e.what();
+ qCritical(errorMessage.toUtf8().constData());
throw std::runtime_error(errorMessage.toStdString());
}
}
@@ -324,15 +366,21 @@ void Controller::loadCustomTypeSet(const QString& projectPath, CustomTypeSet& ty
buildFullFilePath(typeSet.name, projectPath, TypeFileExtension);
QFile typeFile(fullTypeSetPath);
+
+ qInfo(QString("Opening types file %1.").arg(fullTypeSetPath).toUtf8().constData());
+
if (typeFile.open(QIODevice::ReadOnly))
{
try
{
typesSerializer.deserialize(typeFile, typeSet);
+ qInfo(QString("Opened types file %1 with %2 custom types.")
+ .arg(fullTypeSetPath, QString::number(typeSet.types.count())).toUtf8().constData());
}
catch (const std::runtime_error& e)
{
QString errorMessage = QObject::tr("File could not be read: ") + fullTypeSetPath + "\r\n" + e.what();
+ qCritical(errorMessage.toUtf8().constData());
throw std::runtime_error(errorMessage.toStdString());
}
}
@@ -355,6 +403,9 @@ void Controller::loadExportTemplate(const QString& projectPath, RecordExportTemp
buildFullFilePath(exportTemplate.path, projectPath, RecordExportTemplateFileExtension);
QFile exportTemplateFile(fullExportTemplatePath);
+
+ qInfo(QString("Opening export template file %1.").arg(fullExportTemplatePath).toUtf8().constData());
+
if (exportTemplateFile.open(QIODevice::ReadOnly))
{
try
@@ -364,6 +415,7 @@ void Controller::loadExportTemplate(const QString& projectPath, RecordExportTemp
catch (const std::runtime_error& e)
{
QString errorMessage = QObject::tr("File could not be read: ") + fullExportTemplatePath + "\r\n" + e.what();
+ qCritical(errorMessage.toUtf8().constData());
throw std::runtime_error(errorMessage.toStdString());
}
}
@@ -419,6 +471,7 @@ void Controller::loadExportTemplate(const QString& projectPath, RecordExportTemp
catch (const std::runtime_error& e)
{
QString errorMessage = QObject::tr("File could not be read:\r\n") + e.what();
+ qCritical(errorMessage.toUtf8().constData());
throw std::runtime_error(errorMessage.toStdString());
}
}
@@ -432,15 +485,21 @@ void Controller::loadFieldDefinitionSet(const QString& projectPath, FieldDefinit
buildFullFilePath(fieldDefinitionSet.name, projectPath, FieldDefinitionFileExtension);
QFile fieldDefinitionFile(fullFieldDefinitionSetPath);
+
+ qInfo(QString("Opening field definitions file %1.").arg(fullFieldDefinitionSetPath).toUtf8().constData());
+
if (fieldDefinitionFile.open(QIODevice::ReadOnly))
{
try
{
fieldDefinitionSerializer.deserialize(fieldDefinitionFile, fieldDefinitionSet);
+ qInfo(QString("Opened field definitions file %1 with %2 fields.")
+ .arg(fullFieldDefinitionSetPath, QString::number(fieldDefinitionSet.fieldDefinitions.count())).toUtf8().constData());
}
catch (const std::runtime_error& e)
{
QString errorMessage = QObject::tr("File could not be read: ") + fullFieldDefinitionSetPath + "\r\n" + e.what();
+ qCritical(errorMessage.toUtf8().constData());
throw std::runtime_error(errorMessage.toStdString());
}
}
@@ -453,22 +512,26 @@ void Controller::loadFieldDefinitionSet(const QString& projectPath, FieldDefinit
void Controller::loadRecordSet(const QString& projectPath, RecordSet& recordSet)
{
- RecordSetSerializer recordSetSerializer = RecordSetSerializer();
-
// Open record file.
QString fullRecordSetPath =
buildFullFilePath(recordSet.name, projectPath, RecordFileExtension);
QFile recordFile(fullRecordSetPath);
+
+ qInfo(QString("Opening records file %1.").arg(fullRecordSetPath).toUtf8().constData());
+
if (recordFile.open(QIODevice::ReadOnly))
{
try
{
- recordSetSerializer.deserialize(recordFile, recordSet);
+ this->recordSetSerializer->deserialize(recordFile, recordSet);
+ qInfo(QString("Opened records file %1 with %2 records.")
+ .arg(fullRecordSetPath, QString::number(recordSet.records.count())).toUtf8().constData());
}
catch (const std::runtime_error& e)
{
QString errorMessage = QObject::tr("File could not be read: ") + fullRecordSetPath + "\r\n" + e.what();
+ qCritical(errorMessage.toUtf8().constData());
throw std::runtime_error(errorMessage.toStdString());
}
}
@@ -492,6 +555,8 @@ void Controller::openProject(const QString& projectFileName)
const QString projectPath = projectFileInfo.path();
+ qInfo(QString("Opening project %1.").arg(projectFileName).toUtf8().constData());
+
if (projectFile.open(QIODevice::ReadOnly))
{
// Load project from file.
@@ -506,6 +571,7 @@ void Controller::openProject(const QString& projectFileName)
catch (const std::runtime_error& e)
{
QString errorMessage = QObject::tr("File could not be read: ") + projectFileName + "\r\n" + e.what();
+ qCritical(errorMessage.toUtf8().constData());
throw std::runtime_error(errorMessage.toStdString());
}
@@ -560,6 +626,11 @@ void Controller::saveProject()
this->saveProject(this->project);
}
+void Controller::onProgressChanged(const QString title, const QString text, const int currentValue, const int maximumValue)
+{
+ emit this->progressChanged(title, text, currentValue, maximumValue);
+}
+
QString Controller::buildFullFilePath(QString filePath, QString projectPath, QString desiredExtension) const
{
if (!filePath.endsWith(desiredExtension))
@@ -586,6 +657,8 @@ void Controller::saveProject(QSharedPointer project)
// Write project file.
QFile projectFile(fullProjectPath);
+ qInfo(QString("Saving project %1.").arg(fullProjectPath).toUtf8().constData());
+
if (projectFile.open(QIODevice::ReadWrite | QIODevice::Truncate))
{
projectSerializer.serialize(projectFile, project);
@@ -610,6 +683,8 @@ void Controller::saveProject(QSharedPointer project)
// Write file.
QFile componentSetFile(fullComponentSetPath);
+ qInfo(QString("Saving components file %1.").arg(fullComponentSetPath).toUtf8().constData());
+
if (componentSetFile.open(QIODevice::ReadWrite | QIODevice::Truncate))
{
componentSetSerializer.serialize(componentSetFile, componentSet);
@@ -635,6 +710,8 @@ void Controller::saveProject(QSharedPointer project)
// Write file.
QFile fieldDefinitionSetFile(fullFieldDefinitionSetPath);
+ qInfo(QString("Saving field definitions file %1.").arg(fullFieldDefinitionSetPath).toUtf8().constData());
+
if (fieldDefinitionSetFile.open(QIODevice::ReadWrite | QIODevice::Truncate))
{
fieldDefinitionSetSerializer.serialize(fieldDefinitionSetFile, fieldDefinitionSet);
@@ -647,8 +724,6 @@ void Controller::saveProject(QSharedPointer project)
}
// Write record sets.
- Tome::RecordSetSerializer recordSetSerializer = RecordSetSerializer();
-
for (int i = 0; i < project->recordSets.size(); ++i)
{
const RecordSet& recordSet = project->recordSets[i];
@@ -660,9 +735,11 @@ void Controller::saveProject(QSharedPointer project)
// Write file.
QFile recordSetFile(fullRecordSetPath);
+ qInfo(QString("Saving records file %1.").arg(fullRecordSetPath).toUtf8().constData());
+
if (recordSetFile.open(QIODevice::ReadWrite | QIODevice::Truncate))
{
- recordSetSerializer.serialize(recordSetFile, recordSet);
+ this->recordSetSerializer->serialize(recordSetFile, recordSet);
}
else
{
@@ -687,6 +764,8 @@ void Controller::saveProject(QSharedPointer project)
// Write file.
QFile exportTemplateFile(fullExportTemplatePath);
+ qInfo(QString("Saving export template file %1.").arg(fullExportTemplatePath).toUtf8().constData());
+
if (exportTemplateFile.open(QIODevice::ReadWrite | QIODevice::Truncate))
{
exportTemplateSerializer.serialize(exportTemplateFile, exportTemplate);
@@ -712,6 +791,8 @@ void Controller::saveProject(QSharedPointer project)
// Write file.
QFile typeSetFile(fullTypeSetPath);
+ qInfo(QString("Saving types file %1.").arg(fullTypeSetPath).toUtf8().constData());
+
if (typeSetFile.open(QIODevice::ReadWrite | QIODevice::Truncate))
{
typeSetSerializer.serialize(typeSetFile, typeSet);
diff --git a/Source/Tome/Core/controller.h b/Source/Tome/Core/controller.h
index 2b5bbebe..10b1449c 100644
--- a/Source/Tome/Core/controller.h
+++ b/Source/Tome/Core/controller.h
@@ -20,6 +20,7 @@ namespace Tome
class Project;
class RecordExportTemplate;
class RecordSet;
+ class RecordSetSerializer;
class RecordsController;
class SettingsController;
class TasksController;
@@ -82,8 +83,12 @@ namespace Tome
static const QString TypeFileExtension;
signals:
+ void progressChanged(const QString title, const QString text, const int currentValue, const int maximumValue);
void projectChanged(QSharedPointer project);
+ private slots:
+ void onProgressChanged(const QString title, const QString text, const int currentValue, const int maximumValue);
+
private:
CommandLineOptions* options;
@@ -95,10 +100,12 @@ namespace Tome
RecordsController* recordsController;
ExportController* exportController;
SettingsController* settingsController;
+ FacetsController* facetsController;
TasksController* tasksController;
FindUsagesController* findUsagesController;
FindRecordController* findRecordController;
- FacetsController* facetsController;
+
+ RecordSetSerializer* recordSetSerializer;
MainWindow* mainWindow;
diff --git a/Source/Tome/Core/mainwindow.cpp b/Source/Tome/Core/mainwindow.cpp
index 0e201523..4c8049db 100644
--- a/Source/Tome/Core/mainwindow.cpp
+++ b/Source/Tome/Core/mainwindow.cpp
@@ -9,6 +9,8 @@
#include
#include
#include
+#include
+#include
#include
#include
#include
@@ -16,6 +18,7 @@
#include "controller.h"
#include "../Features/Components/View/componentswindow.h"
#include "../Features/Components/Controller/componentscontroller.h"
+#include "../Features/Diagnostics/View/outputdockwidget.h"
#include "../Features/Export/Controller/exportcontroller.h"
#include "../Features/Facets/Controller/facet.h"
#include "../Features/Facets/Controller/facetscontroller.h"
@@ -71,12 +74,13 @@ MainWindow::MainWindow(Controller* controller, QWidget *parent) :
duplicateRecordWindow(0),
findRecordWindow(0),
projectOverviewWindow(0),
- userSettingsWindow(0)
+ userSettingsWindow(0),
+ progressDialog(0)
{
ui->setupUi(this);
// Add record tree.
- this->recordTreeWidget = new RecordTreeWidget(this->controller->getRecordsController());
+ this->recordTreeWidget = new RecordTreeWidget(this->controller->getRecordsController(), this->controller->getSettingsController());
this->ui->splitter->addWidget(this->recordTreeWidget);
// Add record table.
@@ -90,12 +94,20 @@ MainWindow::MainWindow(Controller* controller, QWidget *parent) :
// Add search results.
this->searchResultsDockWidget = new SearchResultsDockWidget(this);
this->addDockWidget(Qt::BottomDockWidgetArea, this->searchResultsDockWidget, Qt::Vertical);
+ this->dockWidgets.push_back(this->searchResultsDockWidget);
// Add error list.
this->errorListDockWidget = new ErrorListDockWidget(this);
this->addDockWidget(Qt::BottomDockWidgetArea, this->errorListDockWidget, Qt::Vertical);
+ this->dockWidgets.push_back(this->errorListDockWidget);
- // Hide all dock widgets until required.
+ // Add log window.
+ this->outputDockWidget = new OutputDockWidget(this);
+ this->outputDockWidget->init();
+ this->addDockWidget(Qt::BottomDockWidgetArea, this->outputDockWidget, Qt::Vertical);
+ this->dockWidgets.push_back(this->outputDockWidget);
+
+ // Hide most dock widgets until required.
this->searchResultsDockWidget->close();
this->errorListDockWidget->close();
@@ -118,12 +130,24 @@ MainWindow::MainWindow(Controller* controller, QWidget *parent) :
SLOT(onRecordSetsChanged())
);
+ connect(
+ &this->controller->getRecordsController(),
+ SIGNAL(progressChanged(QString,QString,int,int)),
+ SLOT(onProgressChanged(QString,QString,int,int))
+ );
+
connect(
&this->controller->getExportController(),
SIGNAL(exportTemplatesChanged()),
SLOT(onExportTemplatesChanged())
);
+ connect(
+ &this->controller->getExportController(),
+ SIGNAL(progressChanged(QString,QString,int,int)),
+ SLOT(onProgressChanged(QString,QString,int,int))
+ );
+
connect(
this->ui->menuExport,
SIGNAL(triggered(QAction*)),
@@ -172,18 +196,54 @@ MainWindow::MainWindow(Controller* controller, QWidget *parent) :
SLOT(searchResultChanged(const QString&, const Tome::SearchResultList))
);
+ connect(
+ &this->controller->getFindUsagesController(),
+ SIGNAL(progressChanged(QString,QString,int,int)),
+ SLOT(onProgressChanged(QString,QString,int,int))
+ );
+
connect(
&this->controller->getFindRecordController(),
SIGNAL(searchResultChanged(const QString&, const Tome::SearchResultList)),
SLOT(searchResultChanged(const QString&, const Tome::SearchResultList))
);
+ connect(
+ &this->controller->getFindRecordController(),
+ SIGNAL(progressChanged(QString,QString,int,int)),
+ SLOT(onProgressChanged(QString,QString,int,int))
+ );
+
connect(
this->searchResultsDockWidget,
SIGNAL(recordLinkActivated(const QString&)),
SLOT(onRecordLinkActivated(const QString&))
);
+ connect(
+ this->searchResultsDockWidget,
+ SIGNAL(progressChanged(QString,QString,int,int)),
+ SLOT(onProgressChanged(QString,QString,int,int))
+ );
+
+ connect(
+ this->controller,
+ SIGNAL(progressChanged(QString, QString, int, int)),
+ SLOT(onProgressChanged(QString, QString, int, int))
+ );
+
+ connect(
+ this->recordTreeWidget,
+ SIGNAL(progressChanged(QString,QString,int,int)),
+ SLOT(onProgressChanged(QString,QString,int,int))
+ );
+
+ connect(
+ this->errorListDockWidget,
+ SIGNAL(recordLinkActivated(const QString&)),
+ SLOT(onRecordLinkActivated(const QString&))
+ );
+
// Maximize window.
this->showMaximized();
@@ -193,6 +253,14 @@ MainWindow::MainWindow(Controller* controller, QWidget *parent) :
// Can't access some functionality until project created or loaded.
this->updateMenus();
this->updateRecentProjects();
+
+ // Setup progress dialog.
+ this->progressDialog = new QProgressDialog(this);
+ this->progressDialog->setMinimumDuration(500);
+ this->progressDialog->setModal(true);
+ this->progressDialog->setCancelButton(0);
+ this->progressDialog->setMaximum(1);
+ this->progressDialog->setValue(1);
}
MainWindow::~MainWindow()
@@ -201,6 +269,7 @@ MainWindow::~MainWindow()
delete this->recordTreeWidget;
delete this->recordFieldTableWidget;
+ delete this->outputDockWidget;
delete this->errorListDockWidget;
delete this->aboutWindow;
@@ -274,8 +343,10 @@ void MainWindow::on_actionManage_Custom_Types_triggered()
{
this->customTypesWindow = new CustomTypesWindow(
this->controller->getTypesController(),
+ this->controller->getFacetsController(),
this->controller->getFieldDefinitionsController(),
this->controller->getFindUsagesController(),
+ this->controller->getRecordsController(),
this);
}
@@ -580,7 +651,7 @@ void MainWindow::on_actionRevert_Record_triggered()
}
// Show question.
- const QString& question = QString(tr("Are you sure you want to revert %1 to its original state?")).arg(recordId);
+ const QString& question = tr("Are you sure you want to revert %1 to its original state?").arg(recordId);
int answer = QMessageBox::question(
this,
@@ -697,8 +768,10 @@ void MainWindow::on_actionUser_Settings_triggered()
// Save settings.
SettingsController& settingsController = this->controller->getSettingsController();
+ settingsController.setRunIntegrityChecksOnLoad(this->userSettingsWindow->getRunIntegrityChecksOnLoad());
settingsController.setRunIntegrityChecksOnSave(this->userSettingsWindow->getRunIntegrityChecksOnSave());
settingsController.setShowDescriptionColumnInsteadOfFieldTooltips(this->userSettingsWindow->getShowDescriptionColumnInsteadOfFieldTooltips());
+ settingsController.setExpandRecordTreeOnRefresh(this->userSettingsWindow->getExpandRecordTreeOnRefresh());
// Refresh view with updated settings.
this->refreshRecordTable();
@@ -721,7 +794,8 @@ void MainWindow::on_actionManual_triggered()
void MainWindow::on_actionReport_a_Bug_triggered()
{
- QDesktopServices::openUrl(QUrl("https://github.com/npruehs/tome-editor/issues/new"));
+ QProcess *process = new QProcess();
+ process->start("TomeIssueReporter.exe");
}
void MainWindow::on_actionReleases_triggered()
@@ -734,6 +808,11 @@ void MainWindow::on_actionRoadmap_triggered()
QDesktopServices::openUrl(QUrl("https://github.com/npruehs/tome-editor/milestones"));
}
+void MainWindow::on_actionOutput_triggered()
+{
+ this->showWindow(this->outputDockWidget);
+}
+
void MainWindow::on_actionError_List_triggered()
{
this->showWindow(this->errorListDockWidget);
@@ -845,28 +924,12 @@ void MainWindow::tableWidgetDoubleClicked(const QModelIndex &index)
const FieldDefinition& field =
this->controller->getFieldDefinitionsController().getFieldDefinition(fieldId);
- // Get field facet data.
- QString facetsDescription;
- QList facets = this->controller->getFacetsController().getFacets(field.fieldType);
-
- for (int i = 0; i < facets.count(); ++i)
- {
- Facet* facet = facets[i];
- QString facetKey = facet->getKey();
-
- if (field.facets.contains(facetKey))
- {
- QVariant facetValue = field.facets[facetKey];
- facetsDescription += facet->getDescriptionForValue(facetValue);
- facetsDescription += " ";
- }
- }
-
// Prepare window.
if (!this->fieldValueWindow)
{
this->fieldValueWindow = new FieldValueWindow
- (this->controller->getRecordsController(),
+ (this->controller->getFacetsController(),
+ this->controller->getRecordsController(),
this->controller->getTypesController(),
this);
@@ -879,10 +942,38 @@ void MainWindow::tableWidgetDoubleClicked(const QModelIndex &index)
// Update view.
this->fieldValueWindow->setFieldDisplayName(field.displayName);
- this->fieldValueWindow->setFieldDescription(field.description + " " + facetsDescription);
+ this->fieldValueWindow->setFieldDescription(field.description);
this->fieldValueWindow->setFieldType(field.fieldType);
this->fieldValueWindow->setFieldValue(fieldValue);
- this->fieldValueWindow->setFieldFacets(facets, field.facets);
+
+ // Apply facets.
+ QString facetsDescription;
+
+ if (this->controller->getTypesController().isCustomType(field.fieldType))
+ {
+ const CustomType& customType = this->controller->getTypesController().getCustomType(field.fieldType);
+
+ if (customType.isDerivedType())
+ {
+ QString baseType = customType.getBaseType();
+ QList facets = this->controller->getFacetsController().getFacets(baseType);
+
+ for (int i = 0; i < facets.count(); ++i)
+ {
+ Facet* facet = facets[i];
+ QString facetKey = facet->getKey();
+
+ if (customType.constrainingFacets.contains(facetKey))
+ {
+ QVariant facetValue = customType.constrainingFacets[facetKey];
+ facetsDescription += facet->getDescriptionForValue(facetValue);
+ facetsDescription += " ";
+ }
+ }
+
+ this->fieldValueWindow->setFieldDescription(field.description + " " + facetsDescription);
+ }
+ }
// Show window.
int result = this->fieldValueWindow->exec();
@@ -929,6 +1020,7 @@ void MainWindow::treeWidgetRecordReparented(const QString& recordId, const QStri
// Update view.
this->recordTreeWidget->clear();
this->refreshRecordTree();
+ this->recordTreeWidget->selectRecord(recordId);
}
void MainWindow::treeWidgetSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
@@ -1003,6 +1095,19 @@ void MainWindow::onFieldChanged()
this->refreshRecordTable();
}
+void MainWindow::onProgressChanged(const QString title, const QString text, const int currentValue, const int maximumValue)
+{
+ this->progressDialog->setWindowTitle(title);
+ this->progressDialog->setLabelText(text);
+ this->progressDialog->setMaximum(maximumValue);
+ this->progressDialog->setValue(currentValue);
+
+ if (currentValue >= maximumValue)
+ {
+ this->progressDialog->reset();
+ }
+}
+
void MainWindow::onProjectChanged(QSharedPointer project)
{
Q_UNUSED(project);
@@ -1024,6 +1129,12 @@ void MainWindow::onProjectChanged(QSharedPointer project)
// Update recent projects.
this->updateRecentProjects();
+
+ // Run integrity checks.
+ if (this->controller->getSettingsController().getRunIntegrityChecksOnLoad())
+ {
+ this->on_actionRun_Integrity_Checks_triggered();
+ }
}
void MainWindow::onRecordFieldsChanged(const QString& recordId)
@@ -1053,14 +1164,14 @@ void MainWindow::refreshExportMenu()
{
this->ui->menuExport->clear();
- const RecordExportTemplateMap& recordExportTemplateMap =
+ const RecordExportTemplateList& recordExportTemplateList =
this->controller->getExportController().getRecordExportTemplates();
- for (RecordExportTemplateMap::const_iterator it = recordExportTemplateMap.begin();
- it != recordExportTemplateMap.end();
+ for (RecordExportTemplateList::const_iterator it = recordExportTemplateList.begin();
+ it != recordExportTemplateList.end();
++it)
{
- QAction* exportAction = new QAction(it.key(), this);
+ QAction* exportAction = new QAction(it->name, this);
this->ui->menuExport->addAction(exportAction);
}
}
@@ -1125,6 +1236,22 @@ void MainWindow::showWindow(QWidget* widget)
widget->show();
widget->raise();
widget->activateWindow();
+
+ // Check if dock widget.
+ QDockWidget* dockWidget = dynamic_cast(widget);
+ if (dockWidget != NULL)
+ {
+ // Tabify dock widget.
+ for (int i = 0; i < this->dockWidgets.count(); ++i)
+ {
+ QDockWidget* existingDockWidget = this->dockWidgets[i];
+
+ if (dockWidget != existingDockWidget && existingDockWidget->isVisible())
+ {
+ this->tabifyDockWidget(existingDockWidget, dockWidget);
+ }
+ }
+ }
}
void MainWindow::updateMenus()
diff --git a/Source/Tome/Core/mainwindow.h b/Source/Tome/Core/mainwindow.h
index 56f5509a..e2997291 100644
--- a/Source/Tome/Core/mainwindow.h
+++ b/Source/Tome/Core/mainwindow.h
@@ -2,6 +2,7 @@
#define MAINWINDOW_H
#include
+#include
#include
#include
@@ -28,6 +29,7 @@ namespace Tome
{
class Controller;
class ErrorListDockWidget;
+ class OutputDockWidget;
class Project;
class RecordFieldsTableWidget;
class RecordTreeWidget;
@@ -75,11 +77,13 @@ class MainWindow : public QMainWindow
void on_actionReleases_triggered();
void on_actionRoadmap_triggered();
+ void on_actionOutput_triggered();
void on_actionError_List_triggered();
void exportRecords(QAction* exportAction);
void onExportTemplatesChanged();
void onFieldChanged();
+ void onProgressChanged(const QString title, const QString text, const int currentValue, const int maximumValue);
void onProjectChanged(QSharedPointer project);
void onRecordFieldsChanged(const QString& recordId);
void onRecordSetsChanged();
@@ -98,6 +102,7 @@ class MainWindow : public QMainWindow
Tome::RecordTreeWidget* recordTreeWidget;
Tome::RecordFieldsTableWidget* recordFieldTableWidget;
Tome::ErrorListDockWidget* errorListDockWidget;
+ Tome::OutputDockWidget* outputDockWidget;
Tome::SearchResultsDockWidget* searchResultsDockWidget;
Tome::Controller* controller;
@@ -114,6 +119,10 @@ class MainWindow : public QMainWindow
ProjectOverviewWindow* projectOverviewWindow;
UserSettingsWindow* userSettingsWindow;
+ QProgressDialog* progressDialog;
+
+ QList dockWidgets;
+
Tome::MessageList messages;
void addRecordField(const QString& fieldId);
diff --git a/Source/Tome/Core/mainwindow.ui b/Source/Tome/Core/mainwindow.ui
index a09ecf62..958174c0 100644
--- a/Source/Tome/Core/mainwindow.ui
+++ b/Source/Tome/Core/mainwindow.ui
@@ -100,6 +100,7 @@
Window
+
diff --git a/Source/Tome/Features/Components/Controller/componentscontroller.cpp b/Source/Tome/Features/Components/Controller/componentscontroller.cpp
index 26cb49e6..9855494b 100644
--- a/Source/Tome/Features/Components/Controller/componentscontroller.cpp
+++ b/Source/Tome/Features/Components/Controller/componentscontroller.cpp
@@ -13,6 +13,7 @@ ComponentsController::ComponentsController()
const Component ComponentsController::addComponent(const QString& componentName, const QString& componentSetName)
{
+ qInfo(QString("Adding component %1.").arg(componentName).toUtf8().constData());
Component component = Component(componentName);
for (ComponentSetList::iterator it = this->model->begin();
@@ -31,6 +32,7 @@ const Component ComponentsController::addComponent(const QString& componentName,
}
const QString errorMessage = "Component set not found: " + componentSetName;
+ qCritical(errorMessage.toUtf8().constData());
throw std::out_of_range(errorMessage.toStdString());
}
@@ -81,6 +83,8 @@ int ComponentsController::indexOf(const Component& component) const
void ComponentsController::removeComponent(const Component component)
{
+ qInfo(QString("Removing component %1.").arg(component).toUtf8().constData());
+
for (ComponentSetList::iterator itSets = this->model->begin();
itSets != this->model->end();
++itSets)
diff --git a/Source/Tome/Features/Components/View/componentswindow.cpp b/Source/Tome/Features/Components/View/componentswindow.cpp
index df01ef67..4240d405 100644
--- a/Source/Tome/Features/Components/View/componentswindow.cpp
+++ b/Source/Tome/Features/Components/View/componentswindow.cpp
@@ -46,8 +46,10 @@ void ComponentsWindow::on_actionNew_Component_triggered()
// Set component sets.
const QStringList componentSetNames = this->componentsController.getComponentSetNames();
+ const QStringList componentIds = this->componentsController.getComponents();
this->componentWindow->setComponentSetNames(componentSetNames);
this->componentWindow->setComponentSetName(componentSetNames.first());
+ this->componentWindow->setDisallowedComponentIds(componentIds);
int result = this->componentWindow->exec();
diff --git a/Source/Tome/Features/Components/View/componentwindow.cpp b/Source/Tome/Features/Components/View/componentwindow.cpp
index a173a397..28be85e4 100644
--- a/Source/Tome/Features/Components/View/componentwindow.cpp
+++ b/Source/Tome/Features/Components/View/componentwindow.cpp
@@ -26,6 +26,11 @@ QString ComponentWindow::getComponentSetName() const
return this->ui->comboBox->currentText();
}
+void ComponentWindow::setDisallowedComponentIds(const QStringList disallowedComponentIds)
+{
+ this->disallowedComponentIds = disallowedComponentIds;
+}
+
void ComponentWindow::setComponentSetName(const QString& componentSetName)
{
this->ui->comboBox->setCurrentText(componentSetName);
@@ -60,5 +65,17 @@ bool ComponentWindow::validate()
return false;
}
+ // Component ids must be unique.
+ if (this->disallowedComponentIds.contains(this->getComponentName()))
+ {
+ QMessageBox::information(
+ this,
+ tr("Duplicate component id"),
+ tr("Please specify another id for the component."),
+ QMessageBox::Close,
+ QMessageBox::Close);
+ return false;
+ }
+
return true;
}
diff --git a/Source/Tome/Features/Components/View/componentwindow.h b/Source/Tome/Features/Components/View/componentwindow.h
index e1f8fd93..52f6ca5d 100644
--- a/Source/Tome/Features/Components/View/componentwindow.h
+++ b/Source/Tome/Features/Components/View/componentwindow.h
@@ -18,6 +18,7 @@ class ComponentWindow : public QDialog
QString getComponentName() const;
QString getComponentSetName() const;
+ void setDisallowedComponentIds(const QStringList disallowedComponentIds);
void setComponentSetName(const QString& componentSetName);
void setComponentSetNames(const QStringList& componentSetNames);
@@ -27,6 +28,8 @@ class ComponentWindow : public QDialog
private:
Ui::ComponentWindow *ui;
+ QStringList disallowedComponentIds;
+
bool validate();
};
diff --git a/Source/Tome/Features/Diagnostics/Controller/filemessagehandler.cpp b/Source/Tome/Features/Diagnostics/Controller/filemessagehandler.cpp
new file mode 100644
index 00000000..ca63b07e
--- /dev/null
+++ b/Source/Tome/Features/Diagnostics/Controller/filemessagehandler.cpp
@@ -0,0 +1,47 @@
+#include "filemessagehandler.h"
+
+#include
+
+using namespace Tome;
+
+
+bool FileMessageHandler::initialized = false;
+QFile FileMessageHandler::logFile("output.log");
+
+
+bool FileMessageHandler::init()
+{
+ initialized = logFile.open(QIODevice::ReadWrite | QIODevice::Truncate);
+ return initialized;
+}
+
+void FileMessageHandler::handleMessage(QtMsgType type, const QMessageLogContext& context, const QString& msg)
+{
+ if (!initialized)
+ {
+ return;
+ }
+
+ QTextStream stream(&logFile);
+
+ switch (type)
+ {
+ case QtDebugMsg:
+ stream << "[DEBUG] ";
+ break;
+ case QtInfoMsg:
+ stream << "[INFO] ";
+ break;
+ case QtWarningMsg:
+ stream << "[WARNING] ";
+ break;
+ case QtCriticalMsg:
+ stream << "[CRITICAL] ";
+ break;
+ case QtFatalMsg:
+ stream << "[FATAL] ";
+ break;
+ }
+
+ stream << context.file << ":" << context.line << " - " << msg << endl;
+}
diff --git a/Source/Tome/Features/Diagnostics/Controller/filemessagehandler.h b/Source/Tome/Features/Diagnostics/Controller/filemessagehandler.h
new file mode 100644
index 00000000..ffb35510
--- /dev/null
+++ b/Source/Tome/Features/Diagnostics/Controller/filemessagehandler.h
@@ -0,0 +1,21 @@
+#ifndef FILEMESSAGEHANDLER_H
+#define FILEMESSAGEHANDLER_H
+
+#include
+#include
+
+namespace Tome
+{
+ class FileMessageHandler
+ {
+ public:
+ static bool init();
+ static void handleMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg);
+
+ private:
+ static bool initialized;
+ static QFile logFile;
+ };
+}
+
+#endif // FILEMESSAGEHANDLER_H
diff --git a/Source/Tome/Features/Diagnostics/Controller/messagehandlers.cpp b/Source/Tome/Features/Diagnostics/Controller/messagehandlers.cpp
new file mode 100644
index 00000000..2078dd16
--- /dev/null
+++ b/Source/Tome/Features/Diagnostics/Controller/messagehandlers.cpp
@@ -0,0 +1,28 @@
+#include "messagehandlers.h"
+
+using namespace Tome;
+
+
+QList MessageHandlers::handlers = QList();
+
+
+void MessageHandlers::addMessageHandler(QtMessageHandler handler)
+{
+ if (handlers.empty())
+ {
+ // Replace default Qt message handler with this one.
+ QtMessageHandler defaultHandler = qInstallMessageHandler(handleMessage);
+ handlers.append(defaultHandler);
+ }
+
+ handlers.append(handler);
+}
+
+void MessageHandlers::handleMessage(QtMsgType type, const QMessageLogContext& context, const QString& msg)
+{
+ // Relay message to all handlers.
+ for (int i = 0; i < handlers.count(); ++i)
+ {
+ handlers[i](type, context, msg);
+ }
+}
diff --git a/Source/Tome/Features/Diagnostics/Controller/messagehandlers.h b/Source/Tome/Features/Diagnostics/Controller/messagehandlers.h
new file mode 100644
index 00000000..de64b392
--- /dev/null
+++ b/Source/Tome/Features/Diagnostics/Controller/messagehandlers.h
@@ -0,0 +1,21 @@
+#ifndef MESSAGEHANDLERS_H
+#define MESSAGEHANDLERS_H
+
+#include
+#include
+
+namespace Tome
+{
+ class MessageHandlers
+ {
+ public:
+ static void addMessageHandler(QtMessageHandler handler);
+
+ private:
+ static QList handlers;
+
+ static void handleMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg);
+ };
+}
+
+#endif // MESSAGEHANDLERS_H
diff --git a/Source/Tome/Features/Diagnostics/View/outputdockwidget.cpp b/Source/Tome/Features/Diagnostics/View/outputdockwidget.cpp
new file mode 100644
index 00000000..9e8d3f28
--- /dev/null
+++ b/Source/Tome/Features/Diagnostics/View/outputdockwidget.cpp
@@ -0,0 +1,73 @@
+#include "outputdockwidget.h"
+
+#include "../Controller/messagehandlers.h"
+
+using namespace Tome;
+
+
+OutputDockWidget* OutputDockWidget::logDockWidget = nullptr;
+
+
+OutputDockWidget::OutputDockWidget(QWidget* parent) :
+ QDockWidget(tr("Output"), parent)
+{
+ // Create layout and widgets.
+ this->widget = new QWidget(this);
+ this->verticalLayout = new QVBoxLayout();
+ this->textBrowser = new QTextBrowser();
+
+ this->verticalLayout->addWidget(this->textBrowser);
+ this->widget->setLayout(this->verticalLayout);
+ this->setWidget(this->widget);
+
+ // Install message handler.
+ MessageHandlers::addMessageHandler(handleMessage);
+}
+
+OutputDockWidget::~OutputDockWidget()
+{
+ delete this->textBrowser;
+ delete this->verticalLayout;
+ delete this->widget;
+}
+
+void OutputDockWidget::init()
+{
+ // Set static reference to current window.
+ // This is required because QtMessageHandlers must be static functions.
+ logDockWidget = this;
+}
+
+void OutputDockWidget::handleMessage(QtMsgType type, const QMessageLogContext& context, const QString& msg)
+{
+ Q_UNUSED(context)
+
+ if (logDockWidget == nullptr)
+ {
+ return;
+ }
+
+ QString colorString;
+
+ switch (type)
+ {
+ case QtDebugMsg:
+ colorString = "";
+ break;
+ case QtInfoMsg:
+ colorString = "";
+ break;
+ case QtWarningMsg:
+ colorString = "";
+ break;
+ case QtCriticalMsg:
+ colorString = "";
+ break;
+ case QtFatalMsg:
+ colorString = "";
+ break;
+ }
+
+ QString s = QString("%0 %1 ").arg(colorString, msg);
+ logDockWidget->textBrowser->append(s);
+}
diff --git a/Source/Tome/Features/Diagnostics/View/outputdockwidget.h b/Source/Tome/Features/Diagnostics/View/outputdockwidget.h
new file mode 100644
index 00000000..f4582e4b
--- /dev/null
+++ b/Source/Tome/Features/Diagnostics/View/outputdockwidget.h
@@ -0,0 +1,29 @@
+#ifndef LOGWINDOW_H
+#define LOGWINDOW_H
+
+#include
+#include
+#include
+
+namespace Tome
+{
+ class OutputDockWidget : public QDockWidget
+ {
+ public:
+ OutputDockWidget(QWidget* parent);
+ ~OutputDockWidget();
+
+ void init();
+
+ private:
+ static OutputDockWidget* logDockWidget;
+
+ QWidget* widget;
+ QVBoxLayout* verticalLayout;
+ QTextBrowser* textBrowser;
+
+ static void handleMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg);
+ };
+}
+
+#endif // LOGWINDOW_H
diff --git a/Source/Tome/Features/Export/Controller/exportcontroller.cpp b/Source/Tome/Features/Export/Controller/exportcontroller.cpp
index c429a18b..e18087cd 100644
--- a/Source/Tome/Features/Export/Controller/exportcontroller.cpp
+++ b/Source/Tome/Features/Export/Controller/exportcontroller.cpp
@@ -2,6 +2,7 @@
#include
+#include
#include
#include
#include
@@ -15,15 +16,22 @@
using namespace Tome;
+const QString ExportController::PlaceholderAppVersion = "$APP_VERSION$";
+const QString ExportController::PlaceholderAppVersionName = "$APP_VERSION_NAME$";
const QString ExportController::PlaceholderComponents = "$RECORD_COMPONENTS$";
const QString ExportController::PlaceholderComponentName = "$COMPONENT_NAME$";
-const QString ExportController::PlaceholderItemType = "$ITEM_TYPE$";
+const QString ExportController::PlaceholderExportTime = "$EXPORT_TIME$";
+const QString ExportController::PlaceholderFieldComponent = "$FIELD_COMPONENT$";
+const QString ExportController::PlaceholderFieldDescription = "$FIELD_DESCRIPTION$";
+const QString ExportController::PlaceholderFieldDisplayName = "$FIELD_DISPLAY_NAME$";
const QString ExportController::PlaceholderFieldId = "$FIELD_ID$";
const QString ExportController::PlaceholderFieldKey = "$FIELD_KEY$";
const QString ExportController::PlaceholderFieldType = "$FIELD_TYPE$";
const QString ExportController::PlaceholderFieldValue = "$FIELD_VALUE$";
+const QString ExportController::PlaceholderItemType = "$ITEM_TYPE$";
const QString ExportController::PlaceholderKeyType = "$KEY_TYPE$";
const QString ExportController::PlaceholderListItem = "$LIST_ITEM$";
+const QString ExportController::PlaceholderRecordDisplayName = "$RECORD_DISPLAY_NAME$";
const QString ExportController::PlaceholderRecordFields = "$RECORD_FIELDS$";
const QString ExportController::PlaceholderRecordId = "$RECORD_ID$";
const QString ExportController::PlaceholderRecordParentId = "$RECORD_PARENT$";
@@ -40,8 +48,10 @@ ExportController::ExportController(const FieldDefinitionsController& fieldDefini
void ExportController::addRecordExportTemplate(const RecordExportTemplate& exportTemplate)
{
+ qInfo(QString("Adding export template %1.").arg(exportTemplate.name).toUtf8().constData());
+
// Update model.
- this->model[exportTemplate.name] = exportTemplate;
+ this->model->push_back(exportTemplate);
// Notify listeners.
emit this->exportTemplatesChanged();
@@ -49,17 +59,39 @@ void ExportController::addRecordExportTemplate(const RecordExportTemplate& expor
const RecordExportTemplate ExportController::getRecordExportTemplate(const QString& name) const
{
- return this->model.value(name);
+ for (RecordExportTemplateList::iterator it = this->model->begin();
+ it != this->model->end();
+ ++it)
+ {
+ if (it->name == name)
+ {
+ return *it;
+ }
+ }
+
+ QString errorMessage = QObject::tr("Export template not found: ") + name;
+ qCritical(errorMessage.toUtf8().constData());
+ throw std::runtime_error(errorMessage.toStdString());
}
-const RecordExportTemplateMap& ExportController::getRecordExportTemplates() const
+const RecordExportTemplateList ExportController::getRecordExportTemplates() const
{
- return this->model;
+ return *this->model;
}
bool ExportController::hasRecordExportTemplate(const QString& name) const
{
- return this->model.contains(name);
+ for (RecordExportTemplateList::iterator it = this->model->begin();
+ it != this->model->end();
+ ++it)
+ {
+ if (it->name == name)
+ {
+ return true;
+ }
+ }
+
+ return false;
}
void ExportController::exportRecords(const RecordExportTemplate& exportTemplate, const QString& filePath)
@@ -73,12 +105,15 @@ void ExportController::exportRecords(const RecordExportTemplate& exportTemplate,
else
{
QString errorMessage = QObject::tr("Destination file could not be written:\r\n") + filePath;
+ qCritical(errorMessage.toUtf8().constData());
throw std::runtime_error(errorMessage.toStdString());
}
}
void ExportController::exportRecords(const RecordExportTemplate& exportTemplate, QIODevice& device)
{
+ qInfo(QString("Exporting records with template %1.").arg(exportTemplate.name).toUtf8().constData());
+
// Build record file string.
QString recordsString;
@@ -93,6 +128,9 @@ void ExportController::exportRecords(const RecordExportTemplate& exportTemplate,
{
const Record& record = recordSet.records[j];
+ // Report progress.
+ emit this->progressChanged(tr("Exporting Data"), record.id, j, recordSet.records.size());
+
// Check if should export.
if (record.parentId.isEmpty())
{
@@ -331,6 +369,9 @@ void ExportController::exportRecords(const RecordExportTemplate& exportTemplate,
// Get field type name.
const QString fieldType = fieldDefinition.fieldType;
const QString exportedFieldType = exportTemplate.typeMap.value(fieldType, fieldType);
+ const QString fieldComponent = fieldDefinition.component;
+ const QString fieldDescription = fieldDefinition.description;
+ const QString fieldDisplayName = fieldDefinition.displayName;
// Apply field value template.
QString fieldValueString = exportTemplate.fieldValueTemplate;
@@ -391,6 +432,11 @@ void ExportController::exportRecords(const RecordExportTemplate& exportTemplate,
fieldValueString = fieldValueString.replace(PlaceholderFieldId, fieldId);
fieldValueString = fieldValueString.replace(PlaceholderFieldType, exportedFieldType);
fieldValueString = fieldValueString.replace(PlaceholderFieldValue, fieldValueText);
+ fieldValueString = fieldValueString.replace(PlaceholderFieldComponent, fieldComponent);
+ fieldValueString = fieldValueString.replace(PlaceholderFieldDisplayName, fieldDisplayName);
+ fieldValueString = fieldValueString.replace(PlaceholderFieldDescription, fieldDescription);
+ fieldValueString = fieldValueString.replace(PlaceholderRecordId, record.id);
+ fieldValueString = fieldValueString.replace(PlaceholderRecordDisplayName, record.displayName);
fieldValuesString.append(fieldValueString);
@@ -457,6 +503,7 @@ void ExportController::exportRecords(const RecordExportTemplate& exportTemplate,
recordString = recordString.replace(PlaceholderRecordParentId, recordParent);
recordString = recordString.replace(PlaceholderRecordFields, fieldValuesString);
recordString = recordString.replace(PlaceholderComponents, componentsString);
+ recordString = recordString.replace(PlaceholderRecordDisplayName, record.displayName);
if (!recordsString.isEmpty())
{
@@ -471,29 +518,40 @@ void ExportController::exportRecords(const RecordExportTemplate& exportTemplate,
// Apply record file template.
QString recordFileString = exportTemplate.recordFileTemplate;
recordFileString = recordFileString.replace(PlaceholderRecords, recordsString);
+ recordFileString = recordFileString.replace(PlaceholderAppVersion, APP_VERSION);
+ recordFileString = recordFileString.replace(PlaceholderAppVersionName, APP_VERSION_NAME);
+ recordFileString = recordFileString.replace(PlaceholderExportTime, QDateTime::currentDateTimeUtc().toString(Qt::ISODate));
// Write record file.
QTextStream textStream(&device);
textStream.setCodec("UTF-8");
textStream << recordFileString;
+
+ // Report finish.
+ emit this->progressChanged(tr("Exporting Data"), QString(), 1, 1);
}
void ExportController::removeExportTemplate(const QString& name)
{
+ qInfo(QString("Removing export template %1.").arg(name).toUtf8().constData());
+
// Update model.
- this->model.remove(name);
+ for (RecordExportTemplateList::iterator it = this->model->begin();
+ it != this->model->end();
+ ++it)
+ {
+ if (it->name == name)
+ {
+ this->model->erase(it);
- // Notify listeners.
- emit this->exportTemplatesChanged();
+ // Notify listeners.
+ emit this->exportTemplatesChanged();
+ return;
+ }
+ }
}
-void ExportController::setRecordExportTemplates(const RecordExportTemplateList& exportTemplates)
+void ExportController::setRecordExportTemplates(RecordExportTemplateList& exportTemplates)
{
- this->model.clear();
-
- for (int i = 0; i < exportTemplates.size(); ++i)
- {
- const RecordExportTemplate& exportTemplate = exportTemplates[i];
- this->model.insert(exportTemplate.name, exportTemplate);
- }
+ this->model = &exportTemplates;
}
diff --git a/Source/Tome/Features/Export/Controller/exportcontroller.h b/Source/Tome/Features/Export/Controller/exportcontroller.h
index 2767a742..598f48f9 100644
--- a/Source/Tome/Features/Export/Controller/exportcontroller.h
+++ b/Source/Tome/Features/Export/Controller/exportcontroller.h
@@ -23,30 +23,38 @@ namespace Tome
void addRecordExportTemplate(const RecordExportTemplate& exportTemplate);
const RecordExportTemplate getRecordExportTemplate(const QString& name) const;
- const RecordExportTemplateMap& getRecordExportTemplates() const;
+ const RecordExportTemplateList getRecordExportTemplates() const;
bool hasRecordExportTemplate(const QString& name) const;
void exportRecords(const RecordExportTemplate& exportTemplate, const QString& filePath);
void exportRecords(const RecordExportTemplate& exportTemplate, QIODevice& device);
void removeExportTemplate(const QString& name);
- void setRecordExportTemplates(const RecordExportTemplateList& exportTemplates);
+ void setRecordExportTemplates(RecordExportTemplateList& exportTemplates);
signals:
void exportTemplatesChanged();
+ void progressChanged(const QString title, const QString text, const int currentValue, const int maximumValue);
private:
- RecordExportTemplateMap model;
+ RecordExportTemplateList* model;
+ static const QString PlaceholderAppVersion;
+ static const QString PlaceholderAppVersionName;
static const QString PlaceholderComponents;
static const QString PlaceholderComponentName;
- static const QString PlaceholderItemType;
+ static const QString PlaceholderExportTime;
+ static const QString PlaceholderFieldComponent;
+ static const QString PlaceholderFieldDescription;
+ static const QString PlaceholderFieldDisplayName;
static const QString PlaceholderFieldId;
static const QString PlaceholderFieldKey;
static const QString PlaceholderFieldType;
static const QString PlaceholderFieldValue;
+ static const QString PlaceholderItemType;
static const QString PlaceholderKeyType;
static const QString PlaceholderListItem;
+ static const QString PlaceholderRecordDisplayName;
static const QString PlaceholderRecordFields;
static const QString PlaceholderRecordId;
static const QString PlaceholderRecordParentId;
diff --git a/Source/Tome/Features/Facets/Controller/facetscontroller.cpp b/Source/Tome/Features/Facets/Controller/facetscontroller.cpp
index a47d544d..fe42d9e7 100644
--- a/Source/Tome/Features/Facets/Controller/facetscontroller.cpp
+++ b/Source/Tome/Features/Facets/Controller/facetscontroller.cpp
@@ -2,11 +2,18 @@
#include "facet.h"
+#include "../Model/facetcontext.h"
+
+#include "../../Records/Controller/recordscontroller.h"
+#include "../../Types/Controller/typescontroller.h"
+
using namespace Tome;
-FacetsController::FacetsController()
-{
+FacetsController::FacetsController(const RecordsController& recordsController, const TypesController& typesController) :
+ recordsController(recordsController),
+ typesController(typesController)
+{
}
FacetsController::~FacetsController()
@@ -19,8 +26,21 @@ FacetsController::~FacetsController()
this->facets.clear();
}
-QList FacetsController::getFacets(const QString& targetType)
+QList FacetsController::getFacets(const QString& targetType) const
{
+ // Check if derived type.
+ if (this->typesController.isCustomType(targetType))
+ {
+ const CustomType& customType = this->typesController.getCustomType(targetType);
+
+ if (customType.isDerivedType())
+ {
+ QString baseType = customType.getBaseType();
+ return getFacets(baseType);
+ }
+ }
+
+ // Get facets for built-in type.
QList typeFacets;
for (int i = 0; i < this->facets.count(); ++i)
@@ -36,5 +56,53 @@ QList FacetsController::getFacets(const QString& targetType)
void FacetsController::registerFacet(Tome::Facet* facet)
{
+ qInfo(QString("Registering facet %1.").arg(facet->getKey()).toUtf8().constData());
this->facets.push_back(facet);
}
+
+QString FacetsController::validateFieldValue(const QString& fieldType, const QVariant& fieldValue) const
+{
+ // Check if custom type.
+ if (!this->typesController.isCustomType(fieldType))
+ {
+ return QString();
+ }
+
+ // Check if derived type.
+ const CustomType& customType = this->typesController.getCustomType(fieldType);
+
+ if (!customType.isDerivedType())
+ {
+ return QString();
+ }
+
+ FacetContext context = FacetContext(this->recordsController);
+
+ // Validate all facets.
+ for (int i = 0; i < this->facets.count(); ++i)
+ {
+ Facet* facet = this->facets[i];
+ QString facetKey = facet->getKey();
+
+ if (facet->getTargetType() != customType.getBaseType())
+ {
+ continue;
+ }
+
+ if (!customType.constrainingFacets.contains(facetKey))
+ {
+ continue;
+ }
+
+ QVariant facetValue = customType.constrainingFacets[facetKey];
+
+ QString validationError = facet->validateValue(context, fieldValue, facetValue);
+
+ if (!validationError.isEmpty())
+ {
+ return validationError;
+ }
+ }
+
+ return QString();
+}
diff --git a/Source/Tome/Features/Facets/Controller/facetscontroller.h b/Source/Tome/Features/Facets/Controller/facetscontroller.h
index 77a895a8..87c9d028 100644
--- a/Source/Tome/Features/Facets/Controller/facetscontroller.h
+++ b/Source/Tome/Features/Facets/Controller/facetscontroller.h
@@ -2,21 +2,28 @@
#define FACETSCONTROLLER_H
#include
+#include
namespace Tome
{
class Facet;
+ class RecordsController;
+ class TypesController;
class FacetsController
{
public:
- FacetsController();
+ FacetsController(const RecordsController& recordsController, const TypesController& typesController);
~FacetsController();
- QList getFacets(const QString& targetType);
+ QList getFacets(const QString& targetType) const;
void registerFacet(Facet* facet);
+ QString validateFieldValue(const QString& fieldType, const QVariant& fieldValue) const;
private:
+ const RecordsController& recordsController;
+ const TypesController& typesController;
+
QList facets;
};
}
diff --git a/Source/Tome/Features/Facets/Controller/requiredreferenceancestorfacet.cpp b/Source/Tome/Features/Facets/Controller/requiredreferenceancestorfacet.cpp
index efe71f09..49dad9c1 100644
--- a/Source/Tome/Features/Facets/Controller/requiredreferenceancestorfacet.cpp
+++ b/Source/Tome/Features/Facets/Controller/requiredreferenceancestorfacet.cpp
@@ -16,12 +16,12 @@ RequiredReferenceAncestorFacet::RequiredReferenceAncestorFacet()
QWidget* RequiredReferenceAncestorFacet::createWidget(const FacetContext& context) const
{
QComboBox* comboBox = new QComboBox();
- QStringList recordNames = context.recordsController.getRecordNames();
+ QStringList recordIds = context.recordsController.getRecordIds();
// Allow clearing the field.
- recordNames.push_front(QString());
+ recordIds.push_front(QString());
- comboBox->addItems(recordNames);
+ comboBox->addItems(recordIds);
return comboBox;
}
@@ -68,8 +68,9 @@ void RequiredReferenceAncestorFacet::setWidgetValue(QWidget* widget, const QVari
QString RequiredReferenceAncestorFacet::validateValue(const FacetContext& context, const QVariant value, const QVariant facetValue) const
{
QString requiredAncestor = facetValue.toString();
+ QString valueAsString = value.toString();
- if (!requiredAncestor.isEmpty() && !context.recordsController.isAncestorOf(requiredAncestor, value.toString()))
+ if ( !valueAsString.isEmpty() && !requiredAncestor.isEmpty() && !context.recordsController.isAncestorOf(requiredAncestor, valueAsString))
{
return tr("Value must be any %1.").arg(requiredAncestor);
}
diff --git a/Source/Tome/Features/Fields/Controller/fielddefinitionscontroller.cpp b/Source/Tome/Features/Fields/Controller/fielddefinitionscontroller.cpp
index 599f8294..867391ca 100644
--- a/Source/Tome/Features/Fields/Controller/fielddefinitionscontroller.cpp
+++ b/Source/Tome/Features/Fields/Controller/fielddefinitionscontroller.cpp
@@ -18,13 +18,15 @@ const FieldDefinition FieldDefinitionsController::addFieldDefinition(const QStri
const QVariant& defaultValue,
const QString& component,
const QString& description,
- const QString& fieldDefinitionSetName,
- const QVariantMap& facets)
+ const QString& fieldDefinitionSetName)
{
+ qInfo(QString("Adding field definition %1.").arg(id).toUtf8().constData());
+
// Check if already exists.
if (this->hasFieldDefinition(id))
{
const QString errorMessage = "Field with the specified id already exists: " + id;
+ qCritical(errorMessage.toUtf8().constData());
throw std::out_of_range(errorMessage.toStdString());
}
@@ -37,7 +39,6 @@ const FieldDefinition FieldDefinitionsController::addFieldDefinition(const QStri
fieldDefinition.component = component;
fieldDefinition.description = description;
fieldDefinition.fieldDefinitionSetName = fieldDefinitionSetName;
- fieldDefinition.facets = facets;
for (FieldDefinitionSetList::iterator it = this->model->begin();
it != this->model->end();
@@ -56,6 +57,7 @@ const FieldDefinition FieldDefinitionsController::addFieldDefinition(const QStri
}
const QString errorMessage = "Field definition set not found: " + fieldDefinitionSetName;
+ qCritical(errorMessage.toUtf8().constData());
throw std::out_of_range(errorMessage.toStdString());
}
@@ -132,6 +134,9 @@ int FieldDefinitionsController::indexOf(const FieldDefinition& fieldDefinition)
void FieldDefinitionsController::moveFieldDefinitionToSet(const QString& fieldDefinitionId, const QString& fieldDefinitionSetName)
{
+ qInfo(QString("Moving field definition %1 to set %2.").arg(fieldDefinitionId, fieldDefinitionSetName)
+ .toUtf8().constData());
+
FieldDefinition fieldDefinition = this->getFieldDefinition(fieldDefinitionId);
for (FieldDefinitionSetList::iterator itSets = this->model->begin();
@@ -186,6 +191,8 @@ void FieldDefinitionsController::removeFieldComponent(const QString componentNam
void FieldDefinitionsController::removeFieldDefinition(const QString& fieldId)
{
+ qInfo(QString("Removing field definition %1.").arg(fieldId).toUtf8().constData());
+
for (int i = 0; i < this->model->size(); ++i)
{
FieldDefinitionSet& fieldDefinitionSet = (*this->model)[i];
@@ -248,12 +255,13 @@ void FieldDefinitionsController::updateFieldDefinition(const QString oldId,
const QVariant& defaultValue,
const QString& component,
const QString& description,
- const QString& fieldDefinitionSetName, const QVariantMap& facets)
+ const QString& fieldDefinitionSetName)
{
// Check if already exists.
if (oldId != newId && this->hasFieldDefinition(newId))
{
const QString errorMessage = "Field with the specified id already exists: " + newId;
+ qCritical(errorMessage.toUtf8().constData());
throw std::out_of_range(errorMessage.toStdString());
}
@@ -268,7 +276,6 @@ void FieldDefinitionsController::updateFieldDefinition(const QString oldId,
fieldDefinition.defaultValue = defaultValue;
fieldDefinition.description = description;
fieldDefinition.component = component;
- fieldDefinition.facets = facets;
// Move field definition, if necessary.
if (fieldDefinition.fieldDefinitionSetName != fieldDefinitionSetName)
@@ -306,5 +313,6 @@ FieldDefinition* FieldDefinitionsController::getFieldDefinitionById(const QStrin
}
const QString errorMessage = "Field not found: " + id;
+ qCritical(errorMessage.toUtf8().constData());
throw std::out_of_range(errorMessage.toStdString());
}
diff --git a/Source/Tome/Features/Fields/Controller/fielddefinitionscontroller.h b/Source/Tome/Features/Fields/Controller/fielddefinitionscontroller.h
index 845cb27e..ac9dfa9c 100644
--- a/Source/Tome/Features/Fields/Controller/fielddefinitionscontroller.h
+++ b/Source/Tome/Features/Fields/Controller/fielddefinitionscontroller.h
@@ -18,8 +18,7 @@ namespace Tome
const QVariant& defaultValue,
const QString& component,
const QString& description,
- const QString& fieldDefinitionSetName,
- const QVariantMap& facets);
+ const QString& fieldDefinitionSetName);
void addFieldDefinitionSet(const FieldDefinitionSet& fieldDefinitionSet);
const FieldDefinition& getFieldDefinition(const QString& id) const;
const FieldDefinitionList getFieldDefinitions() const;
@@ -33,16 +32,14 @@ namespace Tome
void removeFieldDefinitionSet(const QString& name);
void renameFieldType(const QString oldTypeName, const QString newTypeName);
void setFieldDefinitionSets(FieldDefinitionSetList& model);
- void updateFieldDefinition(
- const QString oldId,
+ void updateFieldDefinition(const QString oldId,
const QString newId,
const QString& displayName,
const QString& fieldType,
const QVariant& defaultValue,
const QString& component,
const QString& description,
- const QString& fieldDefinitionSetName,
- const QVariantMap& facets);
+ const QString& fieldDefinitionSetName);
private:
FieldDefinitionSetList* model;
diff --git a/Source/Tome/Features/Fields/Controller/fielddefinitionsetserializer.cpp b/Source/Tome/Features/Fields/Controller/fielddefinitionsetserializer.cpp
index 1156803e..576128dc 100644
--- a/Source/Tome/Features/Fields/Controller/fielddefinitionsetserializer.cpp
+++ b/Source/Tome/Features/Fields/Controller/fielddefinitionsetserializer.cpp
@@ -82,17 +82,6 @@ void FieldDefinitionSetSerializer::serialize(QIODevice& device, const FieldDefin
stream.writeAttribute(AttributeDefaultValue, fieldDefinition.defaultValue.toString());
}
- // Write facets.
- for (QVariantMap::const_iterator it = fieldDefinition.facets.begin();
- it != fieldDefinition.facets.end();
- ++it)
- {
- stream.writeStartElement(ElementFacet);
- stream.writeAttribute(AttributeKey, it.key());
- stream.writeAttribute(AttributeValue, it.value().toString());
- stream.writeEndElement();
- }
-
stream.writeEndElement();
}
}
@@ -169,13 +158,9 @@ void FieldDefinitionSetSerializer::deserialize(QIODevice& device, FieldDefinitio
}
// Read facets.
+ // TODO(np): Remove in next major release.
while (reader.isAtElement(ElementFacet))
{
- QString key = reader.readAttribute(AttributeKey);
- QVariant value = reader.readAttribute(AttributeValue);
-
- fieldDefinition.facets.insert(key, value);
-
reader.readEmptyElement(ElementFacet);
}
diff --git a/Source/Tome/Features/Fields/Controller/fielddefinitionsetserializer.h b/Source/Tome/Features/Fields/Controller/fielddefinitionsetserializer.h
index 05c7642c..ca867c8c 100644
--- a/Source/Tome/Features/Fields/Controller/fielddefinitionsetserializer.h
+++ b/Source/Tome/Features/Fields/Controller/fielddefinitionsetserializer.h
@@ -35,7 +35,6 @@ namespace Tome
static const QString AttributeKey;
static const QString AttributeType;
static const QString AttributeValue;
-
static const QString ElementFacet;
static const QString ElementField;
static const QString ElementFields;
diff --git a/Source/Tome/Features/Fields/Model/fielddefinition.h b/Source/Tome/Features/Fields/Model/fielddefinition.h
index a2a9e3b7..980c8c3d 100644
--- a/Source/Tome/Features/Fields/Model/fielddefinition.h
+++ b/Source/Tome/Features/Fields/Model/fielddefinition.h
@@ -21,7 +21,6 @@ namespace Tome
QString fieldDefinitionSetName;
QString fieldType;
QString id;
- QVariantMap facets;
};
inline bool operator==(const FieldDefinition& lhs, const FieldDefinition& rhs){ return lhs.id == rhs.id; }
diff --git a/Source/Tome/Features/Fields/View/fielddefinitionswindow.cpp b/Source/Tome/Features/Fields/View/fielddefinitionswindow.cpp
index 4965435a..fec0950c 100644
--- a/Source/Tome/Features/Fields/View/fielddefinitionswindow.cpp
+++ b/Source/Tome/Features/Fields/View/fielddefinitionswindow.cpp
@@ -94,9 +94,9 @@ void FieldDefinitionsWindow::on_actionNew_Field_triggered()
this->fieldDefinitionWindow = new FieldDefinitionWindow(
this->fieldDefinitionsController,
this->componentsController,
+ this->facetsController,
this->recordsController,
this->typesController,
- this->facetsController,
this);
}
@@ -121,8 +121,7 @@ void FieldDefinitionsWindow::on_actionNew_Field_triggered()
this->fieldDefinitionWindow->getDefaultValue(),
component,
this->fieldDefinitionWindow->getFieldDescription(),
- this->fieldDefinitionWindow->getFieldDefinitionSetName(),
- this->fieldDefinitionWindow->getFieldFacets());
+ this->fieldDefinitionWindow->getFieldDefinitionSetName());
this->recordsController.moveFieldToComponent(fieldId, QString(), component);
@@ -163,9 +162,9 @@ void FieldDefinitionsWindow::on_actionEdit_Field_triggered()
this->fieldDefinitionWindow = new FieldDefinitionWindow(
this->fieldDefinitionsController,
this->componentsController,
+ this->facetsController,
this->recordsController,
this->typesController,
- this->facetsController,
this);
}
@@ -180,7 +179,6 @@ void FieldDefinitionsWindow::on_actionEdit_Field_triggered()
this->fieldDefinitionWindow->setFieldComponent(fieldDefinition.component);
this->fieldDefinitionWindow->setFieldDefinitionSetNames(this->fieldDefinitionsController.getFieldDefinitionSetNames());
this->fieldDefinitionWindow->setFieldDefinitionSetName(fieldDefinition.fieldDefinitionSetName);
- this->fieldDefinitionWindow->setFieldFacets(fieldDefinition.facets);
int result = this->fieldDefinitionWindow->exec();
@@ -195,8 +193,7 @@ void FieldDefinitionsWindow::on_actionEdit_Field_triggered()
this->fieldDefinitionWindow->getDefaultValue(),
this->fieldDefinitionWindow->getFieldDescription(),
this->fieldDefinitionWindow->getFieldComponent(),
- this->fieldDefinitionWindow->getFieldDefinitionSetName(),
- this->fieldDefinitionWindow->getFieldFacets());
+ this->fieldDefinitionWindow->getFieldDefinitionSetName());
// Notify listeners.
emit fieldChanged();
@@ -275,6 +272,11 @@ void FieldDefinitionsWindow::updateTable()
this->ui->tableWidget->setSortingEnabled(false);
+ if (fieldDefinitions.count() != this->ui->tableWidget->rowCount())
+ {
+ this->ui->tableWidget->setRowCount(fieldDefinitions.size());
+ }
+
for (int i = 0; i < fieldDefinitions.size(); ++i)
{
this->updateRow(i, fieldDefinitions[i], false);
@@ -299,8 +301,7 @@ void FieldDefinitionsWindow::updateFieldDefinition(const QString oldId,
const QVariant& defaultValue,
const QString& description,
const Component& component,
- const QString& fieldDefinitionSetName,
- const QVariantMap& facets)
+ const QString& fieldDefinitionSetName)
{
const FieldDefinition& fieldDefinition = this->fieldDefinitionsController.getFieldDefinition(oldId);
@@ -317,8 +318,7 @@ void FieldDefinitionsWindow::updateFieldDefinition(const QString oldId,
defaultValue,
component,
description,
- fieldDefinitionSetName,
- facets);
+ fieldDefinitionSetName);
this->recordsController.renameRecordField(oldId, newId);
this->recordsController.moveFieldToComponent(newId, oldComponent, component);
diff --git a/Source/Tome/Features/Fields/View/fielddefinitionswindow.h b/Source/Tome/Features/Fields/View/fielddefinitionswindow.h
index 75677791..a551d267 100644
--- a/Source/Tome/Features/Fields/View/fielddefinitionswindow.h
+++ b/Source/Tome/Features/Fields/View/fielddefinitionswindow.h
@@ -81,8 +81,7 @@ class FieldDefinitionsWindow : public QMainWindow
const QVariant& defaultValue,
const QString& description,
const Tome::Component& component,
- const QString& fieldDefinitionSetName,
- const QVariantMap& facets);
+ const QString& fieldDefinitionSetName);
void updateRow(const int index, const Tome::FieldDefinition& fieldDefinition, bool disableSorting);
};
diff --git a/Source/Tome/Features/Fields/View/fielddefinitionwindow.cpp b/Source/Tome/Features/Fields/View/fielddefinitionwindow.cpp
index 1aa97608..10056029 100644
--- a/Source/Tome/Features/Fields/View/fielddefinitionwindow.cpp
+++ b/Source/Tome/Features/Fields/View/fielddefinitionwindow.cpp
@@ -17,29 +17,28 @@
using namespace Tome;
const int FieldDefinitionWindow::DefaultFormRows = 7;
-const int FieldDefinitionWindow::FacetFormRow = 6;
const int FieldDefinitionWindow::ValueFormRow = 3;
FieldDefinitionWindow::FieldDefinitionWindow(
FieldDefinitionsController& fieldDefinitionsController,
ComponentsController& componentsController,
+ FacetsController& facetsController,
RecordsController& recordsController,
TypesController& typesController,
- FacetsController& facetsController,
QWidget *parent) :
QDialog(parent),
ui(new Ui::FieldDefinitionWindow),
fieldDefinitionsController(fieldDefinitionsController),
componentsController(componentsController),
+ facetsController(facetsController),
recordsController(recordsController),
- typesController(typesController),
- facetsController(facetsController)
+ typesController(typesController)
{
ui->setupUi(this);
// Add widget for specifying the default field value.
- this->fieldValueWidget = new FieldValueWidget(this->recordsController, this->typesController, this);
+ this->fieldValueWidget = new FieldValueWidget(this->facetsController, this->recordsController, this->typesController, this);
QFormLayout* layout = static_cast(this->layout());
layout->insertRow(ValueFormRow, tr("Default Value:"), this->fieldValueWidget);
}
@@ -91,29 +90,6 @@ QString FieldDefinitionWindow::getFieldDisplayName() const
return this->ui->lineEditDisplayName->text();
}
-QVariantMap FieldDefinitionWindow::getFieldFacets() const
-{
- QVariantMap facetMap;
-
- // Get all facets registered for the current type.
- QList typeFacets = this->facetsController.getFacets(this->getFieldType());
-
- for (int i = 0; i < typeFacets.count(); ++i)
- {
- // Get facet.
- Facet* facet = typeFacets[i];
-
- // Get current facet value.
- QString key = facet->getKey();
- QVariant value = facet->getWidgetValue(this->facetWidgets[i]);
-
- // Insert into facets map.
- facetMap.insert(key, value);
- }
-
- return facetMap;
-}
-
QString FieldDefinitionWindow::getFieldId() const
{
return this->ui->lineEditId->text();
@@ -178,43 +154,6 @@ void FieldDefinitionWindow::setFieldDisplayName(const QString& displayName)
this->ui->lineEditDisplayName->setText(displayName);
}
-void FieldDefinitionWindow::setFieldFacets(const QVariantMap& facets)
-{
- // Remove all existing facet widget from previous times this window was shown.
- QFormLayout* layout = static_cast(this->layout());
-
- while (!this->facetWidgets.empty())
- {
- QWidget* facetWidget = this->facetWidgets.takeAt(0);
- QWidget* facetLabel = layout->labelForField(facetWidget);
- layout->removeWidget(facetLabel);
- layout->removeWidget(facetWidget);
- delete facetLabel;
- delete facetWidget;
- }
-
- // Add new facet widgets.
- QList typeFacets = this->facetsController.getFacets(this->getFieldType());
- FacetContext context = FacetContext(this->recordsController);
-
- for (int i = 0; i < typeFacets.count(); ++i)
- {
- // Create facet widget and label.
- Facet* facet = typeFacets[i];
- QString facetDisplayName = facet->getDisplayName();
- QWidget* facetWidget = facet->createWidget(context);
-
- // Get current facet value from field definition.
- QString facetKey = facet->getKey();
- QVariant facetValue = facets.contains(facetKey) ? facets[facetKey] : facet->getDefaultValue();
- facet->setWidgetValue(facetWidget, facetValue);
-
- // Insert into layout and remember for later removal.
- layout->insertRow(FacetFormRow + i, facetDisplayName + ":", facetWidget);
- this->facetWidgets.push_back(facetWidget);
- }
-}
-
void FieldDefinitionWindow::setFieldId(const QString& fieldId)
{
this->ui->lineEditId->setText(fieldId);
@@ -233,7 +172,6 @@ void FieldDefinitionWindow::setFieldType(const QString& fieldType) const
void FieldDefinitionWindow::on_comboBoxType_currentIndexChanged(const QString &fieldType)
{
this->fieldValueWidget->setFieldType(fieldType);
- this->setFieldFacets(QVariantMap());
}
void FieldDefinitionWindow::on_lineEditDisplayName_textEdited(const QString &displayName)
@@ -291,5 +229,19 @@ bool FieldDefinitionWindow::validate()
return false;
}
+ // Default value must be valid.
+ QString validationError = this->fieldValueWidget->validate();
+
+ if (!validationError.isEmpty())
+ {
+ QMessageBox::information(
+ this,
+ tr("Invalid data"),
+ validationError,
+ QMessageBox::Close,
+ QMessageBox::Close);
+ return false;
+ }
+
return true;
}
diff --git a/Source/Tome/Features/Fields/View/fielddefinitionwindow.h b/Source/Tome/Features/Fields/View/fielddefinitionwindow.h
index 1964392d..f885b4b6 100644
--- a/Source/Tome/Features/Fields/View/fielddefinitionwindow.h
+++ b/Source/Tome/Features/Fields/View/fielddefinitionwindow.h
@@ -26,12 +26,11 @@ class FieldDefinitionWindow : public QDialog
Q_OBJECT
public:
- explicit FieldDefinitionWindow(
- Tome::FieldDefinitionsController& fieldDefinitionsController,
+ explicit FieldDefinitionWindow(Tome::FieldDefinitionsController& fieldDefinitionsController,
Tome::ComponentsController& componentsController,
+ Tome::FacetsController& facetsController,
Tome::RecordsController& recordsController,
Tome::TypesController& typesController,
- Tome::FacetsController& facetsController,
QWidget *parent = 0);
~FieldDefinitionWindow();
@@ -39,7 +38,6 @@ class FieldDefinitionWindow : public QDialog
QString getFieldDefinitionSetName() const;
QString getFieldDescription() const;
QString getFieldDisplayName() const;
- QVariantMap getFieldFacets() const;
QString getFieldId() const;
QVariant getDefaultValue() const;
QString getFieldType() const;
@@ -51,7 +49,6 @@ class FieldDefinitionWindow : public QDialog
void setFieldDefinitionSetNames(const QStringList& fieldDefinitionSetNames);
void setFieldDescription(const QString& description);
void setFieldDisplayName(const QString& displayName);
- void setFieldFacets(const QVariantMap& facets);
void setFieldId(const QString& fieldId);
void setDefaultValue(const QVariant& defaultValue);
void setFieldType(const QString& fieldType) const;
@@ -68,20 +65,17 @@ class FieldDefinitionWindow : public QDialog
private:
static const int DefaultFormRows;
- static const int FacetFormRow;
static const int ValueFormRow;
Ui::FieldDefinitionWindow *ui;
Tome::FieldDefinitionsController& fieldDefinitionsController;
Tome::ComponentsController& componentsController;
+ Tome::FacetsController& facetsController;
Tome::RecordsController& recordsController;
Tome::TypesController& typesController;
- Tome::FacetsController& facetsController;
Tome::FieldValueWidget* fieldValueWidget;
- QList facetWidgets;
-
bool validate();
};
diff --git a/Source/Tome/Features/Fields/View/fieldvaluewidget.cpp b/Source/Tome/Features/Fields/View/fieldvaluewidget.cpp
index bd7769f4..b7adba4e 100644
--- a/Source/Tome/Features/Fields/View/fieldvaluewidget.cpp
+++ b/Source/Tome/Features/Fields/View/fieldvaluewidget.cpp
@@ -3,14 +3,19 @@
#include
#include
+#include
+
#include "listwidget.h"
#include "mapwidget.h"
#include "vector2iwidget.h"
#include "vector2rwidget.h"
#include "vector3iwidget.h"
#include "vector3rwidget.h"
+#include "../../Facets/Controller/facet.h"
+#include "../../Facets/Controller/facetscontroller.h"
#include "../../Records/Controller/recordscontroller.h"
#include "../../Types/Controller/typescontroller.h"
+#include "../../Facets/Model/facetcontext.h"
#include "../../Types/Model/customtype.h"
#include "../../Types/Model/builtintype.h"
#include "../../../Util/memoryutils.h"
@@ -18,9 +23,10 @@
using namespace Tome;
-FieldValueWidget::FieldValueWidget(RecordsController& recordsController, TypesController& typesController, QWidget *parent) :
+FieldValueWidget::FieldValueWidget(FacetsController& facetsController, RecordsController& recordsController, TypesController& typesController, QWidget *parent) :
QWidget(parent),
currentWidget(0),
+ facetsController(facetsController),
recordsController(recordsController),
typesController(typesController)
{
@@ -53,7 +59,7 @@ FieldValueWidget::FieldValueWidget(RecordsController& recordsController, TypesCo
this->comboBox = new QComboBox();
this->addWidget(this->comboBox);
- this->listWidget = new ListWidget(this->recordsController, this->typesController);
+ this->listWidget = new ListWidget(this->facetsController, this->recordsController, this->typesController);
this->addWidget(this->listWidget);
this->vector2IWidget = new Vector2IWidget();
@@ -68,9 +74,44 @@ FieldValueWidget::FieldValueWidget(RecordsController& recordsController, TypesCo
this->vector3RWidget = new Vector3RWidget();
this->addWidget(this->vector3RWidget);
- this->mapWidget = new MapWidget(this->recordsController, this->typesController);
+ this->mapWidget = new MapWidget(this->facetsController, this->recordsController, this->typesController);
this->addWidget(this->mapWidget);
+ // Add error label.
+ this->errorLabel = new QLabel();
+ this->addWidget(this->errorLabel);
+
+ // Connect to signals.
+ connect(
+ this->colorDialog,
+ SIGNAL(currentColorChanged(QColor)),
+ SLOT(onColorDialogCurrentColorChanged(QColor))
+ );
+
+ connect(
+ this->doubleSpinBox,
+ SIGNAL(valueChanged(double)),
+ SLOT(onDoubleSpinBoxValueChanged(double))
+ );
+
+ connect(
+ this->lineEdit,
+ SIGNAL(textChanged(QString)),
+ SLOT(onLineEditTextChanged(QString))
+ );
+
+ connect(
+ this->comboBox,
+ SIGNAL(currentIndexChanged(QString)),
+ SLOT(onComboBoxCurrentIndexChanged(QString))
+ );
+
+ connect(
+ this->spinBox,
+ SIGNAL(valueChanged(int)),
+ SLOT(onSpinBoxValueChanged(int))
+ );
+
// Set layout.
this->setLayout(this->layout);
this->layout->setContentsMargins(0, 0, 0, 0);
@@ -90,67 +131,174 @@ QString FieldValueWidget::getFieldType() const
}
QVariant FieldValueWidget::getFieldValue() const
+{
+ return this->getFieldValueForType(this->fieldType);
+}
+
+void FieldValueWidget::setFieldType(const QString& fieldType)
+{
+ if (fieldType.isEmpty())
+ {
+ return;
+ }
+
+ // Set new field type.
+ this->fieldType = fieldType;
+
+ // Update view.
+ this->selectWidgetForType(fieldType);
+}
+
+void FieldValueWidget::setFieldValue(const QVariant& fieldValue)
+{
+ this->setFieldValueForType(fieldValue, this->fieldType);
+}
+
+QString FieldValueWidget::validate()
+{
+ const QString fieldType = this->getFieldType();
+ const QVariant fieldValue = this->getFieldValue();
+
+ return this->facetsController.validateFieldValue(fieldType, fieldValue);
+}
+
+void FieldValueWidget::focusInEvent(QFocusEvent* event)
+{
+ Q_UNUSED(event);
+
+ if (this->currentWidget != 0)
+ {
+ this->currentWidget->setFocus();
+
+ // Select content for more convenient editing.
+ CustomType customType;
+
+ if (this->typesController.isCustomType(this->fieldType))
+ {
+ customType = this->typesController.getCustomType(this->fieldType);
+ }
+
+ if (this->fieldType == BuiltInType::Integer || customType.getBaseType() == BuiltInType::Integer)
+ {
+ this->spinBox->selectAll();
+ }
+ else if (this->fieldType == BuiltInType::Real || customType.getBaseType() == BuiltInType::Real)
+ {
+ this->doubleSpinBox->selectAll();
+ }
+ else if (this->fieldType == BuiltInType::String || customType.getBaseType() == BuiltInType::String)
+ {
+ this->lineEdit->selectAll();
+ }
+ }
+}
+
+void FieldValueWidget::onColorDialogCurrentColorChanged(const QColor &color)
+{
+ Q_UNUSED(color)
+ this->updateErrorLabel();
+}
+
+void FieldValueWidget::onDoubleSpinBoxValueChanged(double d)
+{
+ Q_UNUSED(d)
+ this->updateErrorLabel();
+}
+
+void FieldValueWidget::onLineEditTextChanged(const QString &text)
+{
+ Q_UNUSED(text)
+ this->updateErrorLabel();
+}
+
+void FieldValueWidget::onComboBoxCurrentIndexChanged(const QString &text)
+{
+ Q_UNUSED(text)
+ this->updateErrorLabel();
+}
+
+void FieldValueWidget::onSpinBoxValueChanged(int i)
+{
+ Q_UNUSED(i)
+ this->updateErrorLabel();
+}
+
+void FieldValueWidget::addWidget(QWidget* widget)
+{
+ if (widget != 0)
+ {
+ widget->hide();
+ this->layout->addWidget(widget);
+ }
+}
+
+QVariant FieldValueWidget::getFieldValueForType(const QString& typeName) const
{
// Check built-in types.
- if (this->fieldType == BuiltInType::Boolean)
+ if (typeName == BuiltInType::Boolean)
{
return this->checkBox->isChecked();
}
- if (this->fieldType == BuiltInType::Color)
+ if (typeName == BuiltInType::Color)
{
return this->colorDialog->currentColor();
}
- if (this->fieldType == BuiltInType::Integer)
+ if (typeName == BuiltInType::Integer)
{
return this->spinBox->text();
}
- if (this->fieldType == BuiltInType::Real)
+ if (typeName == BuiltInType::Real)
{
return this->doubleSpinBox->text();
}
- if (this->fieldType == BuiltInType::String)
+ if (typeName == BuiltInType::String)
{
return this->lineEdit->text();
}
- if (this->fieldType == BuiltInType::Reference)
+ if (typeName == BuiltInType::Reference)
{
return this->comboBox->currentText();
}
- if (this->fieldType == BuiltInType::Vector2I)
+ if (typeName == BuiltInType::Vector2I)
{
return this->vector2IWidget->getValue();
}
- if (this->fieldType == BuiltInType::Vector2R)
+ if (typeName == BuiltInType::Vector2R)
{
return this->vector2RWidget->getValue();
}
- if (this->fieldType == BuiltInType::Vector3I)
+ if (typeName == BuiltInType::Vector3I)
{
return this->vector3IWidget->getValue();
}
- if (this->fieldType == BuiltInType::Vector3R)
+ if (typeName == BuiltInType::Vector3R)
{
return this->vector3RWidget->getValue();
}
// Custom type - or is it?
- if (!this->typesController.isCustomType(this->fieldType))
+ if (!this->typesController.isCustomType(typeName))
{
// Fall back to string.
return this->lineEdit->text();
}
// Check custom data types.
- const CustomType& customType = this->typesController.getCustomType(this->fieldType);
+ const CustomType& customType = this->typesController.getCustomType(typeName);
+
+ if (customType.isDerivedType())
+ {
+ return this->getFieldValueForType(customType.getBaseType());
+ }
if (customType.isEnumeration())
{
@@ -167,89 +315,95 @@ QVariant FieldValueWidget::getFieldValue() const
return this->mapWidget->getMap();
}
- const QString errorMessage = "Unknown field type: " + this->fieldType;
+ const QString errorMessage = "Unknown field type: " + typeName;
throw std::runtime_error(errorMessage.toStdString());
}
-void FieldValueWidget::setFieldType(const QString& fieldType)
+void FieldValueWidget::selectWidgetForType(const QString& typeName)
{
- if (fieldType.isEmpty())
- {
- return;
- }
-
- // Set new field type.
- this->fieldType = fieldType;
-
- // Update view - check built-in types.
- if (this->fieldType == BuiltInType::Boolean)
+ // Check built-in types.
+ if (typeName == BuiltInType::Boolean)
{
this->setCurrentWidget(this->checkBox);
return;
}
- if (this->fieldType == BuiltInType::Color)
+ if (typeName == BuiltInType::Color)
{
this->setCurrentWidget(this->colorDialog);
return;
}
- if (this->fieldType == BuiltInType::Integer)
+ if (typeName == BuiltInType::Integer)
{
this->setCurrentWidget(this->spinBox);
return;
}
- if (this->fieldType == BuiltInType::Real)
+ if (typeName == BuiltInType::Real)
{
this->setCurrentWidget(this->doubleSpinBox);
return;
}
- if (this->fieldType == BuiltInType::String)
+ if (typeName == BuiltInType::String)
{
this->setCurrentWidget(this->lineEdit);
return;
}
- if (this->fieldType == BuiltInType::Reference)
+ if (typeName == BuiltInType::Reference)
{
QStringList recordNames = this->recordsController.getRecordNames();
+ // Only show allowed record references.
+ QStringList references;
+
+ for (const QString recordName : recordNames)
+ {
+ const QString& validationError =
+ this->facetsController.validateFieldValue(this->getFieldType(), recordName);
+
+ if (validationError.isEmpty())
+ {
+ references.push_back(recordName);
+ }
+ }
+
// Allow clearing the field.
- recordNames.push_front(QString());
+ references.push_front(QString());
- this->setEnumeration(recordNames);
+ this->setEnumeration(references);
this->setCurrentWidget(this->comboBox);
return;
}
- if (this->fieldType == BuiltInType::Vector2I)
+ if (typeName == BuiltInType::Vector2I)
{
this->setCurrentWidget(this->vector2IWidget);
return;
}
- if (this->fieldType == BuiltInType::Vector2R)
+ if (typeName == BuiltInType::Vector2R)
{
this->setCurrentWidget(this->vector2RWidget);
return;
}
- if (this->fieldType == BuiltInType::Vector3I)
+ if (typeName == BuiltInType::Vector3I)
{
this->setCurrentWidget(this->vector3IWidget);
return;
}
- if (this->fieldType == BuiltInType::Vector3R)
+ if (typeName == BuiltInType::Vector3R)
{
this->setCurrentWidget(this->vector3RWidget);
return;
}
// Custom type - or is it?
- if (!this->typesController.isCustomType(this->fieldType))
+ if (!this->typesController.isCustomType(typeName))
{
// Fall back to string.
this->setCurrentWidget(this->lineEdit);
@@ -257,7 +411,13 @@ void FieldValueWidget::setFieldType(const QString& fieldType)
}
// Update view - check custom types.
- const CustomType& customType = this->typesController.getCustomType(this->fieldType);
+ const CustomType& customType = this->typesController.getCustomType(typeName);
+
+ if (customType.isDerivedType())
+ {
+ this->selectWidgetForType(customType.getBaseType());
+ return;
+ }
if (customType.isEnumeration())
{
@@ -281,80 +441,102 @@ void FieldValueWidget::setFieldType(const QString& fieldType)
return;
}
- const QString errorMessage = "Unknown field type: " + this->fieldType;
+ const QString errorMessage = "Unknown field type: " + typeName;
throw std::runtime_error(errorMessage.toStdString());
}
-void FieldValueWidget::setFieldValue(const QVariant& fieldValue)
+void FieldValueWidget::setCurrentWidget(QWidget* widget)
+{
+ if (this->currentWidget != 0)
+ {
+ this->currentWidget->hide();
+ }
+
+ this->currentWidget = widget;
+
+ if (this->currentWidget != 0)
+ {
+ this->currentWidget->show();
+ this->currentWidget->setFocus();
+ }
+}
+
+void FieldValueWidget::setEnumeration(const QStringList& enumeration)
+{
+ this->comboBox->clear();
+ this->comboBox->addItems(enumeration);
+}
+
+void FieldValueWidget::setFieldValueForType(const QVariant& fieldValue, const QString& typeName)
{
// Check built-in types.
- if (this->fieldType == BuiltInType::Boolean)
+ if (typeName == BuiltInType::Boolean)
{
bool value = fieldValue.toBool();
this->checkBox->setChecked(value);
return;
}
- if (this->fieldType == BuiltInType::Color)
+ if (typeName == BuiltInType::Color)
{
QColor color = fieldValue.value();
this->colorDialog->setCurrentColor(color);
return;
}
- if (this->fieldType == BuiltInType::Integer)
+ if (typeName == BuiltInType::Integer)
{
int value = fieldValue.toInt();
this->spinBox->setValue(value);
return;
}
- if (this->fieldType == BuiltInType::Real)
+ if (typeName == BuiltInType::Real)
{
double value = fieldValue.toDouble();
this->doubleSpinBox->setValue(value);
return;
}
- if (this->fieldType == BuiltInType::String)
+ if (typeName == BuiltInType::String)
{
QString value = fieldValue.toString();
this->lineEdit->setText(value);
return;
}
- if (this->fieldType == BuiltInType::Reference)
+ if (typeName == BuiltInType::Reference)
{
this->comboBox->setCurrentText(fieldValue.toString());
return;
}
- if (this->fieldType == BuiltInType::Vector2I)
+ if (typeName == BuiltInType::Vector2I)
{
this->vector2IWidget->setValue(fieldValue);
return;
}
- if (this->fieldType == BuiltInType::Vector2R)
+ if (typeName == BuiltInType::Vector2R)
{
this->vector2RWidget->setValue(fieldValue);
return;
}
- if (this->fieldType == BuiltInType::Vector3I)
+ if (typeName == BuiltInType::Vector3I)
{
this->vector3IWidget->setValue(fieldValue);
return;
}
- if (this->fieldType == BuiltInType::Vector3R)
+ if (typeName == BuiltInType::Vector3R)
{
this->vector3RWidget->setValue(fieldValue);
return;
}
// Custom type - or is it?
- if (!this->typesController.isCustomType(this->fieldType))
+ if (!this->typesController.isCustomType(typeName))
{
// Fall back to string.
QString value = fieldValue.toString();
@@ -363,7 +545,13 @@ void FieldValueWidget::setFieldValue(const QVariant& fieldValue)
}
// Check custom types.
- const CustomType& customType = this->typesController.getCustomType(this->fieldType);
+ const CustomType& customType = this->typesController.getCustomType(typeName);
+
+ if (customType.isDerivedType())
+ {
+ this->setFieldValueForType(fieldValue, customType.getBaseType());
+ return;
+ }
if (customType.isEnumeration())
{
@@ -383,61 +571,23 @@ void FieldValueWidget::setFieldValue(const QVariant& fieldValue)
return;
}
- const QString errorMessage = "Unknown field type: " + this->fieldType;
+ const QString errorMessage = "Unknown field type: " + typeName;
throw std::runtime_error(errorMessage.toStdString());
}
-void FieldValueWidget::setEnumeration(const QStringList& enumeration)
+void FieldValueWidget::updateErrorLabel()
{
- this->comboBox->clear();
- this->comboBox->addItems(enumeration);
-}
-
-void FieldValueWidget::focusInEvent(QFocusEvent* event)
-{
- Q_UNUSED(event);
+ QString validationError = this->validate();
- if (this->currentWidget != 0)
+ if (validationError.isEmpty())
{
- this->currentWidget->setFocus();
-
- // Select content for more convenient editing.
- if (this->fieldType == BuiltInType::Integer)
- {
- this->spinBox->selectAll();
- }
- else if (this->fieldType == BuiltInType::Real)
- {
- this->doubleSpinBox->selectAll();
- }
- else if (this->fieldType == BuiltInType::String)
- {
- this->lineEdit->selectAll();
- }
+ this->errorLabel->clear();
+ this->errorLabel->hide();
}
-}
-
-void FieldValueWidget::addWidget(QWidget* widget)
-{
- if (widget != 0)
- {
- widget->hide();
- this->layout->addWidget(widget);
- }
-}
-
-void FieldValueWidget::setCurrentWidget(QWidget* widget)
-{
- if (this->currentWidget != 0)
- {
- this->currentWidget->hide();
- }
-
- this->currentWidget = widget;
-
- if (this->currentWidget != 0)
+ else
{
- this->currentWidget->show();
- this->currentWidget->setFocus();
+ QString errorMessage = QString("%1").arg(validationError);
+ this->errorLabel->setText(errorMessage);
+ this->errorLabel->show();
}
}
diff --git a/Source/Tome/Features/Fields/View/fieldvaluewidget.h b/Source/Tome/Features/Fields/View/fieldvaluewidget.h
index 101c2670..040b89ed 100644
--- a/Source/Tome/Features/Fields/View/fieldvaluewidget.h
+++ b/Source/Tome/Features/Fields/View/fieldvaluewidget.h
@@ -5,6 +5,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -17,12 +18,14 @@ namespace Tome
class CustomType;
class ListWidget;
class MapWidget;
+ class FacetsController;
class RecordsController;
class TypesController;
class Vector2IWidget;
class Vector2RWidget;
class Vector3IWidget;
class Vector3RWidget;
+ class Facet;
/**
* @brief Changes its appearance depending on the specified type.
@@ -32,7 +35,7 @@ namespace Tome
Q_OBJECT
public:
- explicit FieldValueWidget(RecordsController& recordsController, TypesController& typesController, QWidget *parent = 0);
+ explicit FieldValueWidget(FacetsController& facetsController, RecordsController& recordsController, TypesController& typesController, QWidget *parent = 0);
~FieldValueWidget();
QString getFieldType() const;
@@ -41,9 +44,18 @@ namespace Tome
void setFieldType(const QString& fieldType);
void setFieldValue(const QVariant& fieldValue);
+ QString validate();
+
protected:
virtual void focusInEvent(QFocusEvent* event);
+ private slots:
+ void onColorDialogCurrentColorChanged(const QColor& color);
+ void onDoubleSpinBoxValueChanged(double d);
+ void onLineEditTextChanged(const QString& text);
+ void onComboBoxCurrentIndexChanged(const QString& text);
+ void onSpinBoxValueChanged(int i);
+
private:
QWidget* currentWidget;
QString fieldType;
@@ -62,12 +74,19 @@ namespace Tome
Vector2RWidget* vector2RWidget;
Vector3RWidget* vector3RWidget;
+ QLabel* errorLabel;
+
+ FacetsController& facetsController;
RecordsController& recordsController;
TypesController& typesController;
void addWidget(QWidget* widget);
+ QVariant getFieldValueForType(const QString& typeName) const;
+ void selectWidgetForType(const QString& typeName);
void setCurrentWidget(QWidget* widget);
void setEnumeration(const QStringList& enumeration);
+ void setFieldValueForType(const QVariant& fieldValue, const QString& typeName);
+ void updateErrorLabel();
};
}
diff --git a/Source/Tome/Features/Fields/View/fieldvaluewindow.cpp b/Source/Tome/Features/Fields/View/fieldvaluewindow.cpp
index da805053..15d1597b 100644
--- a/Source/Tome/Features/Fields/View/fieldvaluewindow.cpp
+++ b/Source/Tome/Features/Fields/View/fieldvaluewindow.cpp
@@ -6,6 +6,7 @@
#include "fieldvaluewidget.h"
#include "../../Facets/Controller/facet.h"
+#include "../../Facets/Controller/facetscontroller.h"
#include "../../Facets/Model/facetcontext.h"
#include "../../Records/Controller/recordscontroller.h"
#include "../../Types/Controller/typescontroller.h"
@@ -14,16 +15,17 @@
using namespace Tome;
-FieldValueWindow::FieldValueWindow(RecordsController& recordsController, TypesController& typesController, QWidget *parent) :
+FieldValueWindow::FieldValueWindow(FacetsController& facetsController, RecordsController& recordsController, TypesController& typesController, QWidget *parent) :
QDialog(parent),
ui(new Ui::FieldValueWindow),
+ facetsController(facetsController),
recordsController(recordsController),
typesController(typesController)
{
ui->setupUi(this);
// Add widget for specifying the field value.
- this->fieldValueWidget = new FieldValueWidget(this->recordsController, this->typesController, this);
+ this->fieldValueWidget = new FieldValueWidget(this->facetsController, this->recordsController, this->typesController, this);
QGridLayout* layout = static_cast(this->layout());
layout->addWidget(this->fieldValueWidget, 3, 1);
}
@@ -50,12 +52,6 @@ void FieldValueWindow::setFieldDisplayName(const QString& displayName)
this->ui->labelDisplayNameValue->setText(displayName);
}
-void FieldValueWindow::setFieldFacets(const QList facets, const QVariantMap& facetValues)
-{
- this->facets = facets;
- this->facetValues = facetValues;
-}
-
void FieldValueWindow::setFieldValue(const QVariant& fieldValue)
{
this->fieldValueWidget->setFieldValue(fieldValue);
@@ -70,10 +66,20 @@ void FieldValueWindow::setFieldType(const QString& fieldType) const
void FieldValueWindow::accept()
{
// Validate data.
- if (this->validate())
+ QString validationError = this->fieldValueWidget->validate();
+
+ if (!validationError.isEmpty())
{
- this->done(Accepted);
+ QMessageBox::information(
+ this,
+ tr("Invalid data"),
+ validationError,
+ QMessageBox::Close,
+ QMessageBox::Close);
+ return;
}
+
+ this->done(Accepted);
}
void FieldValueWindow::showEvent(QShowEvent* event)
@@ -98,40 +104,3 @@ void FieldValueWindow::on_toolButtonRevert_clicked()
{
emit revert();
}
-
-bool FieldValueWindow::validate()
-{
- FacetContext context = FacetContext(this->recordsController);
-
- // Validate all facets.
- for (int i = 0; i < this->facets.count(); ++i)
- {
- Facet* facet = this->facets[i];
- QString facetKey = facet->getKey();
-
- if (!this->facetValues.contains(facetKey))
- {
- continue;
- }
-
- QVariant facetValue = this->facetValues[facetKey];
- QVariant value = this->getFieldValue();
-
- QString validationError = facet->validateValue(context, value, facetValue);
-
- if (!validationError.isEmpty())
- {
- QString facetDisplayName = facet->getDisplayName();
-
- QMessageBox::information(
- this,
- facetDisplayName,
- validationError,
- QMessageBox::Close,
- QMessageBox::Close);
- return false;
- }
- }
-
- return true;
-}
diff --git a/Source/Tome/Features/Fields/View/fieldvaluewindow.h b/Source/Tome/Features/Fields/View/fieldvaluewindow.h
index 6b75e237..e2fa5f32 100644
--- a/Source/Tome/Features/Fields/View/fieldvaluewindow.h
+++ b/Source/Tome/Features/Fields/View/fieldvaluewindow.h
@@ -13,6 +13,7 @@ namespace Tome
class CustomType;
class Facet;
class FieldValueWidget;
+ class FacetsController;
class RecordsController;
class TypesController;
}
@@ -22,14 +23,13 @@ class FieldValueWindow : public QDialog
Q_OBJECT
public:
- explicit FieldValueWindow(Tome::RecordsController& recordsController, Tome::TypesController& typesController, QWidget *parent = 0);
+ explicit FieldValueWindow(Tome::FacetsController& facetsController, Tome::RecordsController& recordsController, Tome::TypesController& typesController, QWidget *parent = 0);
~FieldValueWindow();
QVariant getFieldValue() const;
void setFieldDescription(const QString& description);
void setFieldDisplayName(const QString& displayName);
- void setFieldFacets(const QList facets, const QVariantMap& facetValues);
void setFieldValue(const QVariant& fieldValue);
void setFieldType(const QString& fieldType) const;
@@ -49,13 +49,9 @@ class FieldValueWindow : public QDialog
Ui::FieldValueWindow *ui;
Tome::FieldValueWidget* fieldValueWidget;
+ Tome::FacetsController& facetsController;
Tome::RecordsController& recordsController;
Tome::TypesController& typesController;
-
- QList facets;
- QVariantMap facetValues;
-
- bool validate();
};
#endif // FIELDVALUEWINDOW_H
diff --git a/Source/Tome/Features/Fields/View/listitemwindow.cpp b/Source/Tome/Features/Fields/View/listitemwindow.cpp
index 946a3440..e5bfcd3e 100644
--- a/Source/Tome/Features/Fields/View/listitemwindow.cpp
+++ b/Source/Tome/Features/Fields/View/listitemwindow.cpp
@@ -2,8 +2,10 @@
#include "ui_listitemwindow.h"
#include
+#include
#include "fieldvaluewidget.h"
+#include "../../Facets/Controller/facetscontroller.h"
#include "../../Records/Controller/recordscontroller.h"
#include "../../Types/Controller/typescontroller.h"
@@ -11,16 +13,17 @@
using namespace Tome;
-ListItemWindow::ListItemWindow(Tome::RecordsController& recordsController, Tome::TypesController& typesController, QWidget *parent) :
+ListItemWindow::ListItemWindow(Tome::FacetsController& facetsController, Tome::RecordsController& recordsController, Tome::TypesController& typesController, QWidget *parent) :
QDialog(parent),
ui(new Ui::ListItemWindow),
+ facetsController(facetsController),
recordsController(recordsController),
typesController(typesController)
{
ui->setupUi(this);
// Add widget for specifying the list item value.
- this->fieldValueWidget = new FieldValueWidget(this->recordsController, this->typesController, this);
+ this->fieldValueWidget = new FieldValueWidget(this->facetsController, this->recordsController, this->typesController, this);
QFormLayout* layout = static_cast(this->layout());
layout->insertRow(0, tr("Value:"), this->fieldValueWidget);
}
@@ -47,6 +50,15 @@ void ListItemWindow::setFieldType(const QString& fieldType) const
this->fieldValueWidget->setFieldType(fieldType);
}
+void ListItemWindow::accept()
+{
+ // Validate data.
+ if (this->validate())
+ {
+ this->done(Accepted);
+ }
+}
+
void ListItemWindow::showEvent(QShowEvent* event)
{
QDialog::showEvent(event);
@@ -56,3 +68,22 @@ void ListItemWindow::showEvent(QShowEvent* event)
okButton->setAutoDefault(true);
okButton->setDefault(true);
}
+
+bool ListItemWindow::validate()
+{
+ // Value must be valid.
+ QString validationError = this->fieldValueWidget->validate();
+
+ if (!validationError.isEmpty())
+ {
+ QMessageBox::information(
+ this,
+ tr("Invalid data"),
+ validationError,
+ QMessageBox::Close,
+ QMessageBox::Close);
+ return false;
+ }
+
+ return true;
+}
diff --git a/Source/Tome/Features/Fields/View/listitemwindow.h b/Source/Tome/Features/Fields/View/listitemwindow.h
index 47b35b60..cf7ca5ef 100644
--- a/Source/Tome/Features/Fields/View/listitemwindow.h
+++ b/Source/Tome/Features/Fields/View/listitemwindow.h
@@ -11,6 +11,7 @@ namespace Tome
{
class CustomType;
class FieldValueWidget;
+ class FacetsController;
class RecordsController;
class TypesController;
}
@@ -20,7 +21,7 @@ class ListItemWindow : public QDialog
Q_OBJECT
public:
- explicit ListItemWindow(Tome::RecordsController& recordsController, Tome::TypesController& typesController, QWidget *parent = 0);
+ explicit ListItemWindow(Tome::FacetsController& facetsController, Tome::RecordsController& recordsController, Tome::TypesController& typesController, QWidget *parent = 0);
~ListItemWindow();
QVariant getValue() const;
@@ -28,6 +29,9 @@ class ListItemWindow : public QDialog
void setFieldType(const QString& fieldType) const;
+ public slots:
+ void accept();
+
protected:
virtual void showEvent(QShowEvent* event);
@@ -35,8 +39,11 @@ class ListItemWindow : public QDialog
Ui::ListItemWindow *ui;
Tome::FieldValueWidget* fieldValueWidget;
+ Tome::FacetsController& facetsController;
Tome::RecordsController& recordsController;
Tome::TypesController& typesController;
+
+ bool validate();
};
#endif // LISTITEMWINDOW_H
diff --git a/Source/Tome/Features/Fields/View/listwidget.cpp b/Source/Tome/Features/Fields/View/listwidget.cpp
index 17387aa2..bc900bc3 100644
--- a/Source/Tome/Features/Fields/View/listwidget.cpp
+++ b/Source/Tome/Features/Fields/View/listwidget.cpp
@@ -1,6 +1,7 @@
#include "listwidget.h"
#include "listitemwindow.h"
+#include "../../Facets/Controller/facetscontroller.h"
#include "../../Records/Controller/recordscontroller.h"
#include "../../Types/Controller/typescontroller.h"
#include "../../../Util/memoryutils.h"
@@ -9,9 +10,10 @@
using namespace Tome;
-ListWidget::ListWidget(RecordsController& recordsController, TypesController& typesController, QWidget *parent) :
+ListWidget::ListWidget(FacetsController& facetsController, RecordsController& recordsController, TypesController& typesController, QWidget *parent) :
QWidget(parent),
listItemWindow(0),
+ facetsController(facetsController),
recordsController(recordsController),
typesController(typesController)
{
@@ -113,7 +115,7 @@ void ListWidget::addItem()
// Prepare window.
if (!this->listItemWindow)
{
- this->listItemWindow = new ListItemWindow(this->recordsController, this->typesController, this);
+ this->listItemWindow = new ListItemWindow(this->facetsController, this->recordsController, this->typesController, this);
}
// Update view.
@@ -139,7 +141,7 @@ void ListWidget::editItem(QListWidgetItem* item)
// Prepare window.
if (!this->listItemWindow)
{
- this->listItemWindow = new ListItemWindow(this->recordsController, this->typesController, this);
+ this->listItemWindow = new ListItemWindow(this->facetsController, this->recordsController, this->typesController, this);
}
QVariant currentValue = item->text();
diff --git a/Source/Tome/Features/Fields/View/listwidget.h b/Source/Tome/Features/Fields/View/listwidget.h
index f244ad42..3e0fc85c 100644
--- a/Source/Tome/Features/Fields/View/listwidget.h
+++ b/Source/Tome/Features/Fields/View/listwidget.h
@@ -12,6 +12,7 @@ class ListItemWindow;
namespace Tome
{
+ class FacetsController;
class RecordsController;
class TypesController;
@@ -23,7 +24,7 @@ namespace Tome
Q_OBJECT
public:
- explicit ListWidget(RecordsController& recordsController, TypesController& typesController, QWidget *parent = 0);
+ explicit ListWidget(FacetsController& facetsController, RecordsController& recordsController, TypesController& typesController, QWidget *parent = 0);
~ListWidget();
QString getFieldType() const;
@@ -49,6 +50,7 @@ namespace Tome
QVBoxLayout* buttonLayout;
QListWidget* listWidget;
+ FacetsController& facetsController;
RecordsController& recordsController;
TypesController& typesController;
diff --git a/Source/Tome/Features/Fields/View/mapitemwindow.cpp b/Source/Tome/Features/Fields/View/mapitemwindow.cpp
index a3dcaa70..83fad75a 100644
--- a/Source/Tome/Features/Fields/View/mapitemwindow.cpp
+++ b/Source/Tome/Features/Fields/View/mapitemwindow.cpp
@@ -2,6 +2,7 @@
#include "ui_mapitemwindow.h"
#include
+#include
#include "fieldvaluewidget.h"
#include "../../Records/Controller/recordscontroller.h"
@@ -11,9 +12,10 @@
using namespace Tome;
-MapItemWindow::MapItemWindow(Tome::RecordsController& recordsController, Tome::TypesController& typesController, QWidget *parent) :
+MapItemWindow::MapItemWindow(Tome::FacetsController& facetsController, Tome::RecordsController& recordsController, Tome::TypesController& typesController, QWidget *parent) :
QDialog(parent),
ui(new Ui::MapItemWindow),
+ facetsController(facetsController),
recordsController(recordsController),
typesController(typesController)
{
@@ -22,10 +24,10 @@ MapItemWindow::MapItemWindow(Tome::RecordsController& recordsController, Tome::T
// Add widgets for specifying key and value.
QFormLayout* layout = static_cast(this->layout());
- this->keyWidget = new FieldValueWidget(this->recordsController, this->typesController, this);
+ this->keyWidget = new FieldValueWidget(this->facetsController, this->recordsController, this->typesController, this);
layout->insertRow(0, tr("Key:"), this->keyWidget);
- this->valueWidget = new FieldValueWidget(this->recordsController, this->typesController, this);
+ this->valueWidget = new FieldValueWidget(this->facetsController, this->recordsController, this->typesController, this);
layout->insertRow(1, tr("Value:"), this->valueWidget);
}
@@ -67,6 +69,15 @@ void MapItemWindow::setValueType(const QString& valueType) const
this->valueWidget->setFieldType(valueType);
}
+void MapItemWindow::accept()
+{
+ // Validate data.
+ if (this->validate())
+ {
+ this->done(Accepted);
+ }
+}
+
void MapItemWindow::showEvent(QShowEvent* event)
{
QDialog::showEvent(event);
@@ -76,3 +87,36 @@ void MapItemWindow::showEvent(QShowEvent* event)
okButton->setAutoDefault(true);
okButton->setDefault(true);
}
+
+bool MapItemWindow::validate()
+{
+ // Key must be valid.
+ QString validationError = this->keyWidget->validate();
+
+ if (!validationError.isEmpty())
+ {
+ QMessageBox::information(
+ this,
+ tr("Invalid key"),
+ validationError,
+ QMessageBox::Close,
+ QMessageBox::Close);
+ return false;
+ }
+
+ // Value must be valid.
+ validationError = this->valueWidget->validate();
+
+ if (!validationError.isEmpty())
+ {
+ QMessageBox::information(
+ this,
+ tr("Invalid value"),
+ validationError,
+ QMessageBox::Close,
+ QMessageBox::Close);
+ return false;
+ }
+
+ return true;
+}
diff --git a/Source/Tome/Features/Fields/View/mapitemwindow.h b/Source/Tome/Features/Fields/View/mapitemwindow.h
index b8d28bd5..d4173b2a 100644
--- a/Source/Tome/Features/Fields/View/mapitemwindow.h
+++ b/Source/Tome/Features/Fields/View/mapitemwindow.h
@@ -10,6 +10,7 @@ namespace Ui {
namespace Tome
{
class FieldValueWidget;
+ class FacetsController;
class RecordsController;
class TypesController;
}
@@ -19,7 +20,7 @@ class MapItemWindow : public QDialog
Q_OBJECT
public:
- explicit MapItemWindow(Tome::RecordsController& recordsController, Tome::TypesController& typesController, QWidget *parent = 0);
+ explicit MapItemWindow(Tome::FacetsController& facetsController, Tome::RecordsController& recordsController, Tome::TypesController& typesController, QWidget *parent = 0);
~MapItemWindow();
QVariant getKey() const;
@@ -29,6 +30,9 @@ class MapItemWindow : public QDialog
void setValue(const QVariant& value);
void setValueType(const QString& valueType) const;
+ public slots:
+ void accept();
+
protected:
virtual void showEvent(QShowEvent* event);
@@ -37,8 +41,11 @@ class MapItemWindow : public QDialog
Tome::FieldValueWidget* keyWidget;
Tome::FieldValueWidget* valueWidget;
+ Tome::FacetsController& facetsController;
Tome::RecordsController& recordsController;
Tome::TypesController& typesController;
+
+ bool validate();
};
#endif // MAPITEMWINDOW_H
diff --git a/Source/Tome/Features/Fields/View/mapwidget.cpp b/Source/Tome/Features/Fields/View/mapwidget.cpp
index f7b23239..aaf3bdd2 100644
--- a/Source/Tome/Features/Fields/View/mapwidget.cpp
+++ b/Source/Tome/Features/Fields/View/mapwidget.cpp
@@ -11,9 +11,10 @@
using namespace Tome;
-MapWidget::MapWidget(RecordsController& recordsController, TypesController& typesController, QWidget* parent) :
+MapWidget::MapWidget(FacetsController& facetsController, RecordsController& recordsController, TypesController& typesController, QWidget* parent) :
QWidget(parent),
mapItemWindow(0),
+ facetsController(facetsController),
recordsController(recordsController),
typesController(typesController)
{
@@ -131,7 +132,7 @@ void MapWidget::addItem()
// Prepare window.
if (!this->mapItemWindow)
{
- this->mapItemWindow = new MapItemWindow(this->recordsController, this->typesController, this);
+ this->mapItemWindow = new MapItemWindow(this->facetsController, this->recordsController, this->typesController, this);
}
// Update view.
@@ -163,7 +164,7 @@ void MapWidget::editItem(QTableWidgetItem* item)
// Prepare window.
if (!this->mapItemWindow)
{
- this->mapItemWindow = new MapItemWindow(this->recordsController, this->typesController, this);
+ this->mapItemWindow = new MapItemWindow(this->facetsController, this->recordsController, this->typesController, this);
}
QTableWidgetItem* keyTableWidgetItem = this->tableWidget->item(item->row(), 0);
@@ -177,7 +178,7 @@ void MapWidget::editItem(QTableWidgetItem* item)
this->mapItemWindow->setValueType(this->valueType);
this->mapItemWindow->setKey(currentKey);
- this->mapItemWindow->setKey(currentValue);
+ this->mapItemWindow->setValue(currentValue);
// Show window.
int result = this->mapItemWindow->exec();
diff --git a/Source/Tome/Features/Fields/View/mapwidget.h b/Source/Tome/Features/Fields/View/mapwidget.h
index f89f03f4..ae2c12cb 100644
--- a/Source/Tome/Features/Fields/View/mapwidget.h
+++ b/Source/Tome/Features/Fields/View/mapwidget.h
@@ -12,6 +12,7 @@ class MapItemWindow;
namespace Tome
{
+ class FacetsController;
class RecordsController;
class TypesController;
@@ -20,7 +21,7 @@ namespace Tome
Q_OBJECT
public:
- explicit MapWidget(RecordsController& recordsController, TypesController& typesController, QWidget *parent = 0);
+ explicit MapWidget(FacetsController& facetsController, RecordsController& recordsController, TypesController& typesController, QWidget *parent = 0);
~MapWidget();
QString getKeyType() const;
@@ -51,6 +52,7 @@ namespace Tome
QVBoxLayout* buttonLayout;
QTableWidget* tableWidget;
+ FacetsController& facetsController;
RecordsController& recordsController;
TypesController& typesController;
};
diff --git a/Source/Tome/Features/Integrity/Controller/fieldtypedoesnotexisttask.cpp b/Source/Tome/Features/Integrity/Controller/fieldtypedoesnotexisttask.cpp
index 2c79ddfb..27dca612 100644
--- a/Source/Tome/Features/Integrity/Controller/fieldtypedoesnotexisttask.cpp
+++ b/Source/Tome/Features/Integrity/Controller/fieldtypedoesnotexisttask.cpp
@@ -29,7 +29,7 @@ const MessageList FieldTypeDoesNotExistTask::execute(const TaskContext& context)
if (!context.typesController.isBuiltInType(field.fieldType) && !context.typesController.isCustomType(field.fieldType))
{
Message message;
- message.content = "Field type " + field.fieldType + " does not exist.";
+ message.content = tr("Field type %1 does not exist.").arg(field.fieldType);
message.messageCode = MessageCode;
message.severity = Severity::Error;
message.targetSiteId = field.id;
diff --git a/Source/Tome/Features/Integrity/Controller/fieldtypedoesnotexisttask.h b/Source/Tome/Features/Integrity/Controller/fieldtypedoesnotexisttask.h
index 118f7cfb..155a926a 100644
--- a/Source/Tome/Features/Integrity/Controller/fieldtypedoesnotexisttask.h
+++ b/Source/Tome/Features/Integrity/Controller/fieldtypedoesnotexisttask.h
@@ -1,13 +1,15 @@
#ifndef FIELDTYPEDOESNOTEXISTTASK_H
#define FIELDTYPEDOESNOTEXISTTASK_H
+#include
+
#include "../../Tasks/Controller/task.h"
namespace Tome
{
class TaskContext;
- class FieldTypeDoesNotExistTask : public Task
+ class FieldTypeDoesNotExistTask : public QObject, public Task
{
public:
FieldTypeDoesNotExistTask();
diff --git a/Source/Tome/Features/Integrity/Controller/listitemtypedoesnotexisttask.cpp b/Source/Tome/Features/Integrity/Controller/listitemtypedoesnotexisttask.cpp
index d9519622..5323c46d 100644
--- a/Source/Tome/Features/Integrity/Controller/listitemtypedoesnotexisttask.cpp
+++ b/Source/Tome/Features/Integrity/Controller/listitemtypedoesnotexisttask.cpp
@@ -32,7 +32,7 @@ const MessageList ListItemTypeDoesNotExistTask::execute(const TaskContext& conte
if (!context.typesController.isBuiltInType(itemType) && !context.typesController.isCustomType(itemType))
{
Message message;
- message.content = "List item type " + itemType + " does not exist.";
+ message.content = tr("List item type %1 does not exist.").arg(itemType);
message.messageCode = MessageCode;
message.severity = Severity::Error;
message.targetSiteId = type.name;
diff --git a/Source/Tome/Features/Integrity/Controller/listitemtypedoesnotexisttask.h b/Source/Tome/Features/Integrity/Controller/listitemtypedoesnotexisttask.h
index cdc9cf29..4246cd96 100644
--- a/Source/Tome/Features/Integrity/Controller/listitemtypedoesnotexisttask.h
+++ b/Source/Tome/Features/Integrity/Controller/listitemtypedoesnotexisttask.h
@@ -1,13 +1,15 @@
#ifndef LISTITEMTYPEDOESNOTEXISTTASK_H
#define LISTITEMTYPEDOESNOTEXISTTASK_H
+#include
+
#include "../../Tasks/Controller/task.h"
namespace Tome
{
class TaskContext;
- class ListItemTypeDoesNotExistTask : public Task
+ class ListItemTypeDoesNotExistTask : public QObject, public Task
{
public:
ListItemTypeDoesNotExistTask();
diff --git a/Source/Tome/Features/Integrity/Controller/listitemtypenotsupportedtask.cpp b/Source/Tome/Features/Integrity/Controller/listitemtypenotsupportedtask.cpp
index fb566623..10713715 100644
--- a/Source/Tome/Features/Integrity/Controller/listitemtypenotsupportedtask.cpp
+++ b/Source/Tome/Features/Integrity/Controller/listitemtypenotsupportedtask.cpp
@@ -34,7 +34,7 @@ const MessageList ListItemTypeNotSupportedTask::execute(const TaskContext& conte
itemType == BuiltInType::Vector3I || itemType == BuiltInType::Vector3R)
{
Message message;
- message.content = "List item type " + itemType + " is not supported.";
+ message.content = tr("List item type %1 is not supported.").arg(itemType);
message.messageCode = MessageCode;
message.severity = Severity::Error;
message.targetSiteId = type.name;
diff --git a/Source/Tome/Features/Integrity/Controller/listitemtypenotsupportedtask.h b/Source/Tome/Features/Integrity/Controller/listitemtypenotsupportedtask.h
index ede78d37..9e4a59cb 100644
--- a/Source/Tome/Features/Integrity/Controller/listitemtypenotsupportedtask.h
+++ b/Source/Tome/Features/Integrity/Controller/listitemtypenotsupportedtask.h
@@ -1,13 +1,15 @@
#ifndef LISTITEMTYPENOTSUPPORTEDTASK_H
#define LISTITEMTYPENOTSUPPORTEDTASK_H
+#include
+
#include "../../Tasks/Controller/task.h"
namespace Tome
{
class TaskContext;
- class ListItemTypeNotSupportedTask : public Task
+ class ListItemTypeNotSupportedTask : public QObject, public Task
{
public:
ListItemTypeNotSupportedTask();
diff --git a/Source/Tome/Features/Integrity/Controller/mapkeytypedoesnotexisttask.cpp b/Source/Tome/Features/Integrity/Controller/mapkeytypedoesnotexisttask.cpp
index 40ec5499..e36dd098 100644
--- a/Source/Tome/Features/Integrity/Controller/mapkeytypedoesnotexisttask.cpp
+++ b/Source/Tome/Features/Integrity/Controller/mapkeytypedoesnotexisttask.cpp
@@ -32,7 +32,7 @@ const MessageList MapKeyTypeDoesNotExistTask::execute(const TaskContext& context
if (!context.typesController.isBuiltInType(keyType) && !context.typesController.isCustomType(keyType))
{
Message message;
- message.content = "Map key type " + keyType + " does not exist.";
+ message.content = tr("Map key type %1 does not exist.").arg(keyType);
message.messageCode = MessageCode;
message.severity = Severity::Error;
message.targetSiteId = type.name;
diff --git a/Source/Tome/Features/Integrity/Controller/mapkeytypedoesnotexisttask.h b/Source/Tome/Features/Integrity/Controller/mapkeytypedoesnotexisttask.h
index ad3230f7..efeffcdc 100644
--- a/Source/Tome/Features/Integrity/Controller/mapkeytypedoesnotexisttask.h
+++ b/Source/Tome/Features/Integrity/Controller/mapkeytypedoesnotexisttask.h
@@ -1,13 +1,15 @@
#ifndef MAPKEYTYPEDOESNOTEXISTTASK_H
#define MAPKEYTYPEDOESNOTEXISTTASK_H
+#include
+
#include "../../Tasks/Controller/task.h"
namespace Tome
{
class TaskContext;
- class MapKeyTypeDoesNotExistTask : public Task
+ class MapKeyTypeDoesNotExistTask : public QObject, public Task
{
public:
MapKeyTypeDoesNotExistTask();
diff --git a/Source/Tome/Features/Integrity/Controller/mapkeytypenotsupportedtask.cpp b/Source/Tome/Features/Integrity/Controller/mapkeytypenotsupportedtask.cpp
index 7a56c0e0..e661f7c0 100644
--- a/Source/Tome/Features/Integrity/Controller/mapkeytypenotsupportedtask.cpp
+++ b/Source/Tome/Features/Integrity/Controller/mapkeytypenotsupportedtask.cpp
@@ -34,7 +34,7 @@ const MessageList MapKeyTypeNotSupportedTask::execute(const TaskContext& context
keyType == BuiltInType::Vector3I || keyType == BuiltInType::Vector3R)
{
Message message;
- message.content = "Map key type " + keyType + " is not supported.";
+ message.content = tr("Map key type %1 is not supported.").arg(keyType);
message.messageCode = MessageCode;
message.severity = Severity::Error;
message.targetSiteId = type.name;
diff --git a/Source/Tome/Features/Integrity/Controller/mapkeytypenotsupportedtask.h b/Source/Tome/Features/Integrity/Controller/mapkeytypenotsupportedtask.h
index a30167a1..6d62d0cb 100644
--- a/Source/Tome/Features/Integrity/Controller/mapkeytypenotsupportedtask.h
+++ b/Source/Tome/Features/Integrity/Controller/mapkeytypenotsupportedtask.h
@@ -1,13 +1,15 @@
#ifndef MAPKEYTYPENOTSUPPORTEDTASK_H
#define MAPKEYTYPENOTSUPPORTEDTASK_H
+#include
+
#include "../../Tasks/Controller/task.h"
namespace Tome
{
class TaskContext;
- class MapKeyTypeNotSupportedTask : public Task
+ class MapKeyTypeNotSupportedTask : public QObject, public Task
{
public:
MapKeyTypeNotSupportedTask();
diff --git a/Source/Tome/Features/Integrity/Controller/mapvaluetypedoesnotexisttask.cpp b/Source/Tome/Features/Integrity/Controller/mapvaluetypedoesnotexisttask.cpp
index 17385579..24da256f 100644
--- a/Source/Tome/Features/Integrity/Controller/mapvaluetypedoesnotexisttask.cpp
+++ b/Source/Tome/Features/Integrity/Controller/mapvaluetypedoesnotexisttask.cpp
@@ -32,7 +32,7 @@ const MessageList MapValueTypeDoesNotExistTask::execute(const TaskContext& conte
if (!context.typesController.isBuiltInType(valueType) && !context.typesController.isCustomType(valueType))
{
Message message;
- message.content = "Map value type " + valueType + " does not exist.";
+ message.content = tr("Map value type %1 does not exist.").arg(valueType);
message.messageCode = MessageCode;
message.severity = Severity::Error;
message.targetSiteId = type.name;
diff --git a/Source/Tome/Features/Integrity/Controller/mapvaluetypedoesnotexisttask.h b/Source/Tome/Features/Integrity/Controller/mapvaluetypedoesnotexisttask.h
index dae0ee8b..789836ff 100644
--- a/Source/Tome/Features/Integrity/Controller/mapvaluetypedoesnotexisttask.h
+++ b/Source/Tome/Features/Integrity/Controller/mapvaluetypedoesnotexisttask.h
@@ -1,13 +1,15 @@
#ifndef MAPVALUETYPEDOESNOTEXISTTASK_H
#define MAPVALUETYPEDOESNOTEXISTTASK_H
+#include
+
#include "../../Tasks/Controller/task.h"
namespace Tome
{
class TaskContext;
- class MapValueTypeDoesNotExistTask : public Task
+ class MapValueTypeDoesNotExistTask : public QObject, public Task
{
public:
MapValueTypeDoesNotExistTask();
diff --git a/Source/Tome/Features/Integrity/Controller/mapvaluetypenotsupportedtask.cpp b/Source/Tome/Features/Integrity/Controller/mapvaluetypenotsupportedtask.cpp
index bcc63b43..4d7867c0 100644
--- a/Source/Tome/Features/Integrity/Controller/mapvaluetypenotsupportedtask.cpp
+++ b/Source/Tome/Features/Integrity/Controller/mapvaluetypenotsupportedtask.cpp
@@ -34,7 +34,7 @@ const MessageList MapValueTypeNotSupportedTask::execute(const TaskContext& conte
valueType == BuiltInType::Vector3I || valueType == BuiltInType::Vector3R)
{
Message message;
- message.content = "Map value type " + valueType + " is not supported.";
+ message.content = tr("Map value type %1 is not supported.").arg(valueType);
message.messageCode = MessageCode;
message.severity = Severity::Error;
message.targetSiteId = type.name;
diff --git a/Source/Tome/Features/Integrity/Controller/mapvaluetypenotsupportedtask.h b/Source/Tome/Features/Integrity/Controller/mapvaluetypenotsupportedtask.h
index 9f3fcb81..4e10c91f 100644
--- a/Source/Tome/Features/Integrity/Controller/mapvaluetypenotsupportedtask.h
+++ b/Source/Tome/Features/Integrity/Controller/mapvaluetypenotsupportedtask.h
@@ -1,13 +1,15 @@
#ifndef MAPVALUETYPENOTSUPPORTEDTASK_H
#define MAPVALUETYPENOTSUPPORTEDTASK_H
+#include
+
#include "../../Tasks/Controller/task.h"
namespace Tome
{
class TaskContext;
- class MapValueTypeNotSupportedTask : public Task
+ class MapValueTypeNotSupportedTask : public QObject, public Task
{
public:
MapValueTypeNotSupportedTask();
diff --git a/Source/Tome/Features/Integrity/Controller/typefacetviolatedtask.cpp b/Source/Tome/Features/Integrity/Controller/typefacetviolatedtask.cpp
new file mode 100644
index 00000000..ace4cd6f
--- /dev/null
+++ b/Source/Tome/Features/Integrity/Controller/typefacetviolatedtask.cpp
@@ -0,0 +1,61 @@
+#include "typefacetviolatedtask.h"
+
+using namespace Tome;
+
+#include "../../Facets/Controller/facetscontroller.h"
+#include "../../Fields/Controller/fielddefinitionscontroller.h"
+#include "../../Records/Controller/recordscontroller.h"
+#include "../../Tasks/Model/taskcontext.h"
+
+
+const QString TypeFacetViolatedTask::MessageCode = "TO0200";
+
+
+TypeFacetViolatedTask::TypeFacetViolatedTask()
+{
+}
+
+const MessageList TypeFacetViolatedTask::execute(const TaskContext& context) const
+{
+ MessageList messages;
+
+ // Check all records.
+ const RecordList& records = context.recordsController.getRecords();
+
+ for (int i = 0; i < records.count(); ++i)
+ {
+ const Record& record = records.at(i);
+
+ // Check all field values.
+ const RecordFieldValueMap& fieldValues = context.recordsController.getRecordFieldValues(record.id);
+
+ for (RecordFieldValueMap::const_iterator it = fieldValues.begin();
+ it != fieldValues.end();
+ ++it)
+ {
+ // Validate all facets.
+ const QString& fieldId = it.key();
+ const QVariant& fieldValue = it.value();
+
+ const FieldDefinition& field = context.fieldDefinitionsController.getFieldDefinition(fieldId);
+
+ const QString& validationError = context.facetsController.validateFieldValue(field.fieldType, fieldValue);
+
+ if (validationError.isEmpty())
+ {
+ continue;
+ }
+
+ Message message;
+ message.content = tr("Record %1 field %2 violates a type facet: %3").arg(record.id, fieldId, validationError);
+ message.messageCode = MessageCode;
+ message.severity = Severity::Error;
+ message.targetSiteId = record.id;
+ message.targetSiteType = TargetSiteType::Record;
+
+ messages.append(message);
+ }
+ }
+
+ return messages;
+}
diff --git a/Source/Tome/Features/Integrity/Controller/typefacetviolatedtask.h b/Source/Tome/Features/Integrity/Controller/typefacetviolatedtask.h
new file mode 100644
index 00000000..15affb37
--- /dev/null
+++ b/Source/Tome/Features/Integrity/Controller/typefacetviolatedtask.h
@@ -0,0 +1,23 @@
+#ifndef TYPEFACETVIOLATEDTASK_H
+#define TYPEFACETVIOLATEDTASK_H
+
+#include
+
+#include "../../Tasks/Controller/task.h"
+
+namespace Tome
+{
+ class TaskContext;
+
+ class TypeFacetViolatedTask : public QObject, public Task
+ {
+ public:
+ TypeFacetViolatedTask();
+
+ const MessageList execute(const TaskContext& context) const;
+
+ static const QString MessageCode;
+ };
+}
+
+#endif // TYPEFACETVIOLATEDTASK_H
diff --git a/Source/Tome/Features/Projects/Controller/projectserializer.cpp b/Source/Tome/Features/Projects/Controller/projectserializer.cpp
index 4031e87a..c8dcc222 100644
--- a/Source/Tome/Features/Projects/Controller/projectserializer.cpp
+++ b/Source/Tome/Features/Projects/Controller/projectserializer.cpp
@@ -304,7 +304,7 @@ void ProjectSerializer::deserialize(QIODevice& device, QSharedPointer p
QString restrictionKey = reader.readAttribute(AttributeKey);
QString restrictionValue = reader.readAttribute(AttributeValue);
- type.restrictions.insert(restrictionKey, restrictionValue);
+ type.fundamentalFacets.insert(restrictionKey, restrictionValue);
// Advance reader.
reader.readEmptyElement(ElementRestriction);
diff --git a/Source/Tome/Features/Projects/View/projectoverviewwindow.cpp b/Source/Tome/Features/Projects/View/projectoverviewwindow.cpp
index 0c8e6940..99372a09 100644
--- a/Source/Tome/Features/Projects/View/projectoverviewwindow.cpp
+++ b/Source/Tome/Features/Projects/View/projectoverviewwindow.cpp
@@ -201,7 +201,7 @@ void ProjectOverviewWindow::updateComponentData()
const ComponentSetList& componentSets = componentsController.getComponentSets();
const int componentSetCount = componentSets.count();
const int componentCount = componentsController.getComponents().count();
- const QString componentsText = QString(tr("%1 component%2 (in %3 file%4)")).arg(
+ const QString componentsText = tr("%1 component%2 (in %3 file%4)").arg(
QString::number(componentCount),
componentCount != 1 ? "s" : "",
QString::number(componentSetCount),
@@ -234,7 +234,7 @@ void ProjectOverviewWindow::updateCustomTypeData()
const CustomTypeSetList& customTypeSets = typesController.getCustomTypeSets();
const int typeSetCount = customTypeSets.count();
const int typeCount = typesController.getCustomTypes().count();
- const QString typesText = QString(tr("%1 type%2 (in %3 file%4)")).arg(
+ const QString typesText = tr("%1 type%2 (in %3 file%4)").arg(
QString::number(typeCount),
typeCount != 1 ? "s" : "",
QString::number(typeSetCount),
@@ -264,9 +264,9 @@ void ProjectOverviewWindow::updateExportTemplateData()
{
// Count export templates.
ExportController& exportController = this->controller->getExportController();
- const RecordExportTemplateMap& exportTemplateMap = exportController.getRecordExportTemplates();
- const int exportTemplateCount = exportTemplateMap.count();
- const QString exportTemplatesText = QString(tr("%1 export template%2")).arg(
+ const RecordExportTemplateList& exportTemplateList = exportController.getRecordExportTemplates();
+ const int exportTemplateCount = exportTemplateList.count();
+ const QString exportTemplatesText = tr("%1 export template%2").arg(
QString::number(exportTemplateCount),
exportTemplateCount != 1 ? "s" : "");
this->ui->labelExportTemplatesValue->setText(exportTemplatesText);
@@ -274,8 +274,8 @@ void ProjectOverviewWindow::updateExportTemplateData()
// Add list items.
this->ui->listWidgetExportTemplates->clear();
- for (RecordExportTemplateMap::const_iterator it = exportTemplateMap.begin();
- it != exportTemplateMap.end();
+ for (RecordExportTemplateList::const_iterator it = exportTemplateList.begin();
+ it != exportTemplateList.end();
++it)
{
const RecordExportTemplate& exportTemplate = *it;
@@ -299,7 +299,7 @@ void ProjectOverviewWindow::updateFieldDefinitionData()
const FieldDefinitionSetList& fieldDefinitionSets = fieldDefintionsController.getFieldDefinitionSets();
const int fieldDefinitionSetCount = fieldDefinitionSets.count();
const int fieldDefintionCount = fieldDefintionsController.getFieldDefinitions().count();
- const QString fieldsText = QString(tr("%1 field%2 (in %3 file%4)")).arg(
+ const QString fieldsText = tr("%1 field%2 (in %3 file%4)").arg(
QString::number(fieldDefintionCount),
fieldDefintionCount != 1 ? "s" : "",
QString::number(fieldDefinitionSetCount),
@@ -332,7 +332,7 @@ void ProjectOverviewWindow::updateRecordData()
const RecordSetList& recordSets = recordsController.getRecordSets();
const int recordSetCount = recordSets.count();
const int recordCount = recordsController.getRecords().count();
- const QString recordsText = QString(tr("%1 record%2 (in %3 file%4)")).arg(
+ const QString recordsText = tr("%1 record%2 (in %3 file%4)").arg(
QString::number(recordCount),
recordCount != 1 ? "s" : "",
QString::number(recordSetCount),
@@ -368,6 +368,12 @@ void ProjectOverviewWindow::onAddExistingComponentsFileClicked(bool checked)
tr("Add Existing Components File"),
projectPath,
"Tome Component Files (*.tcomp)");
+
+ if (componentSetFileName.isEmpty())
+ {
+ return;
+ }
+
QDir projectDirectory = QDir(projectPath);
const QString& relativeComponentFilePath = projectDirectory.relativeFilePath(componentSetFileName);
@@ -405,6 +411,12 @@ void ProjectOverviewWindow::onAddExistingCustomTypesFileClicked(bool checked)
tr("Add Existing Types File"),
projectPath,
"Tome Type Files (*.ttypes)");
+
+ if (customTypeSetFileName.isEmpty())
+ {
+ return;
+ }
+
QDir projectDirectory = QDir(projectPath);
const QString& relativeCustomTypeFilePath = projectDirectory.relativeFilePath(customTypeSetFileName);
@@ -442,6 +454,12 @@ void ProjectOverviewWindow::onAddExistingExportTemplateFileClicked(bool checked)
tr("Add Existing Export Template File"),
projectPath,
"Tome Export Template Files (*.texport)");
+
+ if (exportTemplateFileName.isEmpty())
+ {
+ return;
+ }
+
QDir projectDirectory = QDir(projectPath);
const QString& relativeExportTemplateFilePath = projectDirectory.relativeFilePath(exportTemplateFileName);
@@ -479,6 +497,12 @@ void ProjectOverviewWindow::onAddExistingFieldDefinitionsFileClicked(bool checke
tr("Add Existing Field Definitions File"),
projectPath,
"Tome Field Definition Files (*.tfields)");
+
+ if (fieldDefinitionSetFileName.isEmpty())
+ {
+ return;
+ }
+
QDir projectDirectory = QDir(projectPath);
const QString& relativeFieldDefinitionFilePath = projectDirectory.relativeFilePath(fieldDefinitionSetFileName);
@@ -516,6 +540,12 @@ void ProjectOverviewWindow::onAddExistingRecordsFileClicked(bool checked)
tr("Add Existing Records File"),
projectPath,
"Tome Record Files (*.tdata)");
+
+ if (recordSetFileName.isEmpty())
+ {
+ return;
+ }
+
QDir projectDirectory = QDir(projectPath);
const QString& relativeRecordFilePath = projectDirectory.relativeFilePath(recordSetFileName);
@@ -553,6 +583,12 @@ void ProjectOverviewWindow::onAddNewComponentsFileClicked(bool checked)
tr("Add New Components File"),
projectPath,
"Tome Component Files (*.tcomp)");
+
+ if (componentSetFileName.isEmpty())
+ {
+ return;
+ }
+
QDir projectDirectory = QDir(projectPath);
const QString& relativeComponentFilePath = projectDirectory.relativeFilePath(componentSetFileName);
@@ -577,6 +613,12 @@ void ProjectOverviewWindow::onAddNewCustomTypesFileClicked(bool checked)
tr("Add New Types File"),
projectPath,
"Tome Type Files (*.ttypes)");
+
+ if (customTypeSetFileName.isEmpty())
+ {
+ return;
+ }
+
QDir projectDirectory = QDir(projectPath);
const QString& relativeCustomTypeFilePath = projectDirectory.relativeFilePath(customTypeSetFileName);
@@ -601,6 +643,12 @@ void ProjectOverviewWindow::onAddNewFieldDefinitionsFileClicked(bool checked)
tr("Add New Field Definitions File"),
projectPath,
"Tome Field Definition Files (*.tfields)");
+
+ if (fieldDefinitionSetFileName.isEmpty())
+ {
+ return;
+ }
+
QDir projectDirectory = QDir(projectPath);
const QString& relativeFieldDefinitionFilePath = projectDirectory.relativeFilePath(fieldDefinitionSetFileName);
@@ -625,6 +673,12 @@ void ProjectOverviewWindow::onAddNewRecordsFileClicked(bool checked)
tr("Add New Records File"),
projectPath,
"Tome Record Files (*.tdata)");
+
+ if (recordSetFileName.isEmpty())
+ {
+ return;
+ }
+
QDir projectDirectory = QDir(projectPath);
const QString& relativeRecordFilePath = projectDirectory.relativeFilePath(recordSetFileName);
diff --git a/Source/Tome/Features/Records/Controller/recordnamevalidator.cpp b/Source/Tome/Features/Records/Controller/recordnamevalidator.cpp
new file mode 100644
index 00000000..3d79ff21
--- /dev/null
+++ b/Source/Tome/Features/Records/Controller/recordnamevalidator.cpp
@@ -0,0 +1,32 @@
+#include "recordnamevalidator.h"
+
+RecordNameValidator::RecordNameValidator(QObject *parent)
+ : QValidator(parent)
+{
+}
+
+RecordNameValidator::~RecordNameValidator()
+{
+}
+
+void RecordNameValidator::fixup(QString &input) const
+{
+ while (input.startsWith(' '))
+ {
+ input.remove(0, 1);
+ }
+ while (input.endsWith(' '))
+ {
+ input.remove(input.size() - 1, 1);
+ }
+}
+
+QValidator::State RecordNameValidator::validate(QString &input, int &pos) const
+{
+ Q_UNUSED(pos)
+ return
+ ( !input.isEmpty() && !input.startsWith(' ') && !input.endsWith(' ') ) ?
+ State::Acceptable :
+ State::Invalid;
+}
+
diff --git a/Source/Tome/Features/Records/Controller/recordnamevalidator.h b/Source/Tome/Features/Records/Controller/recordnamevalidator.h
new file mode 100644
index 00000000..877422a3
--- /dev/null
+++ b/Source/Tome/Features/Records/Controller/recordnamevalidator.h
@@ -0,0 +1,17 @@
+#ifndef RECORDNAMEVALIDATOR_H
+#define RECORDNAMEVALIDATOR_H
+
+#include
+
+class RecordNameValidator : public QValidator
+{
+public:
+
+ RecordNameValidator(QObject *parent = Q_NULLPTR);
+ ~RecordNameValidator();
+ virtual void fixup(QString &input) const override;
+ virtual State validate(QString &input, int &pos) const override;
+
+};
+
+#endif // RECORDNAMEVALIDATOR_H
diff --git a/Source/Tome/Features/Records/Controller/recordscontroller.cpp b/Source/Tome/Features/Records/Controller/recordscontroller.cpp
index 8c140f6b..2bf01609 100644
--- a/Source/Tome/Features/Records/Controller/recordscontroller.cpp
+++ b/Source/Tome/Features/Records/Controller/recordscontroller.cpp
@@ -11,13 +11,16 @@
using namespace Tome;
-RecordsController::RecordsController(const FieldDefinitionsController& fieldDefinitionsController)
- : fieldDefinitionsController(fieldDefinitionsController)
+RecordsController::RecordsController(const FieldDefinitionsController& fieldDefinitionsController, const TypesController& typesController)
+ : fieldDefinitionsController(fieldDefinitionsController),
+ typesController(typesController)
{
}
const Record RecordsController::addRecord(const QString& id, const QString& displayName, const QString& recordSetName)
{
+ qInfo(QString("Adding record %1.").arg(id).toUtf8().constData());
+
Record record = Record();
record.id = id;
record.displayName = displayName;
@@ -40,6 +43,7 @@ const Record RecordsController::addRecord(const QString& id, const QString& disp
}
const QString errorMessage = "Record set not found: " + recordSetName;
+ qCritical(errorMessage.toUtf8().constData());
throw std::out_of_range(errorMessage.toStdString());
}
@@ -63,20 +67,32 @@ void RecordsController::addRecordSet(const RecordSet& recordSet)
emit this->recordSetsChanged();
}
-const Record RecordsController::duplicateRecord(const QString& existingRecordId, const QString& newRecordid)
+const Record RecordsController::duplicateRecord(const QString& existingRecordId, const QString& newRecordId)
{
+ qInfo(QString("Duplicating record %1 to %2.").arg(existingRecordId, newRecordId).toUtf8().constData());
+
// Get record to duplicate.
const Record& existingRecord = this->getRecord(existingRecordId);
// Create duplicate.
Record newRecord = Record();
- newRecord.id = newRecordid;
- newRecord.displayName = newRecordid;
+ newRecord.id = newRecordId;
+ newRecord.displayName = newRecordId;
newRecord.parentId = existingRecord.parentId;
newRecord.fieldValues = existingRecord.fieldValues;
+ newRecord.recordSetName = existingRecord.recordSetName;
// Add new record.
- RecordList& records = (*this->model)[0].records;
+ int recordSetIndex = 0;
+ for (int i = 0; this->model->size() > i; ++i)
+ {
+ if ((*this->model)[i].name == existingRecord.recordSetName)
+ {
+ recordSetIndex = i;
+ break;
+ }
+ }
+ RecordList& records = (*this->model)[recordSetIndex].records;
int index = findInsertionIndex(records, newRecord, recordLessThanDisplayName);
records.insert(index, newRecord);
@@ -416,6 +432,8 @@ void RecordsController::moveFieldToComponent(const QString& fieldId, const QStri
void RecordsController::moveRecordToSet(const QString& recordId, const QString& recordSetName)
{
+ qInfo(QString("Moving record %1 to set %2.").arg(recordId, recordSetName).toUtf8().constData());
+
Record record = this->getRecord(recordId);
for (RecordSetList::iterator itSets = this->model->begin();
@@ -452,6 +470,8 @@ void RecordsController::moveRecordToSet(const QString& recordId, const QString&
void RecordsController::removeRecord(const QString& recordId)
{
+ qInfo(QString("Removing record %1.").arg(recordId).toUtf8().constData());
+
// Remove references to record.
this->updateRecordReferences(recordId, QString());
@@ -517,6 +537,8 @@ void RecordsController::removeRecordField(const QString fieldId)
void RecordsController::removeRecordField(const QString& recordId, const QString& fieldId)
{
+ qInfo(QString("Removing field %1 from record %2.").arg(fieldId, recordId).toUtf8().constData());
+
Record& record = *this->getRecordById(recordId);
record.fieldValues.remove(fieldId);
@@ -573,6 +595,8 @@ void RecordsController::renameRecordField(const QString oldFieldId, const QStrin
QVariant RecordsController::revertFieldValue(const QString& recordId, const QString& fieldId)
{
+ qInfo(QString("Reverting field %1 of record %2.").arg(fieldId, recordId).toUtf8().constData());
+
// Check if there's anything to revert to.
QVariant valueToRevertTo = this->getInheritedFieldValue(recordId, fieldId);
@@ -594,20 +618,33 @@ QVariant RecordsController::revertFieldValue(const QString& recordId, const QStr
void RecordsController::revertRecord(const QString& recordId)
{
+ qInfo(QString("Reverting record %1.").arg(recordId).toUtf8().constData());
+
const RecordFieldValueMap& fields = this->getRecordFieldValues(recordId);
// Revert all fields.
+ int i = 0;
+
for (RecordFieldValueMap::const_iterator it = fields.begin();
it != fields.end();
- ++it)
+ ++it, ++i)
{
const QString& fieldId = it.key();
+
+ // Report progress.
+ emit this->progressChanged(tr("Reverting fields"), fieldId, i, fields.count());
+
this->revertFieldValue(recordId, fieldId);
}
+
+ // Report finish.
+ emit this->progressChanged(tr("Reverting fields"), QString(), 1, 1);
}
void RecordsController::reparentRecord(const QString& recordId, const QString& newParentId)
{
+ qInfo(QString("Reparenting record %1 to %2.").arg(recordId, newParentId).toUtf8().constData());
+
Record& record = *this->getRecordById(recordId);
record.parentId = newParentId;
}
@@ -643,6 +680,8 @@ void RecordsController::updateRecord(const QString& oldId, const QString& newId,
void RecordsController::updateRecordFieldValue(const QString& recordId, const QString& fieldId, const QVariant& fieldValue)
{
+ qInfo(QString("Updating record %1 field %2 to value %3.").arg(recordId, fieldId, fieldValue.toString()).toUtf8().constData());
+
Record& record = *this->getRecordById(recordId);
// Check if equals inherited field value.
@@ -663,12 +702,21 @@ void RecordsController::updateRecordFieldValue(const QString& recordId, const QS
void RecordsController::updateRecordReferences(const QString oldReference, const QString newReference)
{
+ if (oldReference == newReference)
+ {
+ return;
+ }
+
RecordList records = this->getRecords();
+ // First pass: update reference fields
for (int i = 0; i < records.count(); ++i)
{
const Record& record = records.at(i);
+ // Report progress.
+ emit this->progressChanged(tr("Updating references"), record.id, i, records.count());
+
// Update references.
const RecordFieldValueMap fieldValues = this->getRecordFieldValues(record.id);
@@ -679,7 +727,7 @@ void RecordsController::updateRecordReferences(const QString oldReference, const
const QString fieldId = it.key();
const FieldDefinition& field = this->fieldDefinitionsController.getFieldDefinition(fieldId);
- if (field.fieldType == BuiltInType::Reference)
+ if (this->typesController.isReferenceType(field.fieldType))
{
const QString reference = it.value().toString();
@@ -689,13 +737,24 @@ void RecordsController::updateRecordReferences(const QString oldReference, const
}
}
}
+ }
+
+ // Second pass: Update parents.
+ for (int i = 0; i < records.count(); ++i)
+ {
+ const Record& record = records.at(i);
+
+ // Report progress.
+ emit this->progressChanged(tr("Reparenting records"), record.id, i, records.count());
- // Update parents.
if (record.parentId == oldReference)
{
this->reparentRecord(record.id, newReference);
}
}
+
+ // Report finish.
+ emit this->progressChanged(tr("Reparenting records"), QString(), 1, 1);
}
Record* RecordsController::getRecordById(const QString& id) const
@@ -716,5 +775,6 @@ Record* RecordsController::getRecordById(const QString& id) const
}
const QString errorMessage = "Record not found: " + id;
+ qCritical(errorMessage.toUtf8().constData());
throw std::out_of_range(errorMessage.toStdString());
}
diff --git a/Source/Tome/Features/Records/Controller/recordscontroller.h b/Source/Tome/Features/Records/Controller/recordscontroller.h
index b5e63522..12c761ce 100644
--- a/Source/Tome/Features/Records/Controller/recordscontroller.h
+++ b/Source/Tome/Features/Records/Controller/recordscontroller.h
@@ -16,13 +16,13 @@ namespace Tome
Q_OBJECT
public:
- RecordsController(const FieldDefinitionsController& fieldDefinitionsController);
+ RecordsController(const FieldDefinitionsController& fieldDefinitionsController, const TypesController& typesController);
const Record addRecord(const QString& id, const QString& displayName, const QString& recordSetName);
void addRecordField(const QString& recordId, const QString& fieldId);
void addRecordSet(const RecordSet& recordSet);
- const Record duplicateRecord(const QString& existingRecordId, const QString& newRecordid);
+ const Record duplicateRecord(const QString& existingRecordId, const QString& newRecordId);
/**
* @brief getAncestors Gets the list of all ancestors of the record with the specified id, direct parent first.
@@ -94,6 +94,7 @@ namespace Tome
void updateRecordReferences(const QString oldReference, const QString newReference);
signals:
+ void progressChanged(const QString title, const QString text, const int currentValue, const int maximumValue);
void recordFieldsChanged(const QString& recordId);
void recordSetsChanged();
@@ -101,6 +102,7 @@ namespace Tome
RecordSetList* model;
const FieldDefinitionsController& fieldDefinitionsController;
+ const TypesController& typesController;
Record* getRecordById(const QString& id) const;
};
diff --git a/Source/Tome/Features/Records/Controller/recordsetserializer.cpp b/Source/Tome/Features/Records/Controller/recordsetserializer.cpp
index e2bb3f8b..63f9beef 100644
--- a/Source/Tome/Features/Records/Controller/recordsetserializer.cpp
+++ b/Source/Tome/Features/Records/Controller/recordsetserializer.cpp
@@ -19,11 +19,6 @@ const QString RecordSetSerializer::ElementRecords = "Records";
const QString RecordSetSerializer::ElementValue = "Value";
-RecordSetSerializer::RecordSetSerializer()
-{
-
-}
-
void RecordSetSerializer::serialize(QIODevice& device, const RecordSet& recordSet) const
{
// Open device stream.
@@ -41,6 +36,9 @@ void RecordSetSerializer::serialize(QIODevice& device, const RecordSet& recordSe
{
const Record& record = recordSet.records[i];
+ // Report progress.
+ emit progressChanged(tr("Saving Data"), record.id, i, recordSet.records.size());
+
// Begin record.
stream.writeStartElement(ElementRecord);
{
@@ -111,8 +109,10 @@ void RecordSetSerializer::serialize(QIODevice& device, const RecordSet& recordSe
}
// End document.
stream.writeEndDocument();
-}
+ // Report finish.
+ emit progressChanged(tr("Saving Data"), QString(), 1, 1);
+}
void RecordSetSerializer::deserialize(QIODevice& device, RecordSet& recordSet) const
{
@@ -139,6 +139,9 @@ void RecordSetSerializer::deserialize(QIODevice& device, RecordSet& recordSet) c
record.readOnly = reader.readAttribute(ElementReadOnly) == "true";
record.recordSetName = recordSet.name;
+ // Report progress.
+ emit progressChanged(tr("Loading Data"), record.id, device.pos(), device.size());
+
reader.readStartElement(ElementRecord);
while (!reader.isAtElement(ElementRecord))
@@ -201,4 +204,7 @@ void RecordSetSerializer::deserialize(QIODevice& device, RecordSet& recordSet) c
}
// End document.
reader.readEndDocument();
+
+ // Report finish.
+ emit progressChanged(tr("Loading Data"), QString(), 1, 1);
}
diff --git a/Source/Tome/Features/Records/Controller/recordsetserializer.h b/Source/Tome/Features/Records/Controller/recordsetserializer.h
index 70e773a6..5cfa5550 100644
--- a/Source/Tome/Features/Records/Controller/recordsetserializer.h
+++ b/Source/Tome/Features/Records/Controller/recordsetserializer.h
@@ -7,11 +7,11 @@ namespace Tome
{
class RecordSet;
- class RecordSetSerializer
+ class RecordSetSerializer : public QObject
{
- public:
- RecordSetSerializer();
+ Q_OBJECT
+ public:
/**
* @brief serialize Writes the passed record set to the specified device.
* @param device Device to write the record set to.
@@ -26,6 +26,9 @@ namespace Tome
*/
void deserialize(QIODevice& device, RecordSet& recordSet) const;
+ signals:
+ void progressChanged(const QString title, const QString text, const int currentValue, const int maximumValue) const;
+
private:
static const QString ElementDisplayName;
static const QString ElementId;
diff --git a/Source/Tome/Features/Records/View/duplicaterecordwindow.cpp b/Source/Tome/Features/Records/View/duplicaterecordwindow.cpp
index 1dbfca5f..f4d69a76 100644
--- a/Source/Tome/Features/Records/View/duplicaterecordwindow.cpp
+++ b/Source/Tome/Features/Records/View/duplicaterecordwindow.cpp
@@ -8,6 +8,7 @@ DuplicateRecordWindow::DuplicateRecordWindow(QWidget *parent) :
ui(new Ui::DuplicateRecordWindow)
{
ui->setupUi(this);
+ this->ui->lineEdit->setValidator(&nameValidator);
}
DuplicateRecordWindow::~DuplicateRecordWindow()
diff --git a/Source/Tome/Features/Records/View/duplicaterecordwindow.h b/Source/Tome/Features/Records/View/duplicaterecordwindow.h
index 3c22fb69..2e7c95fa 100644
--- a/Source/Tome/Features/Records/View/duplicaterecordwindow.h
+++ b/Source/Tome/Features/Records/View/duplicaterecordwindow.h
@@ -1,6 +1,8 @@
#ifndef DUPLICATERECORDWINDOW_H
#define DUPLICATERECORDWINDOW_H
+#include "../../Records/Controller/recordnamevalidator.h"
+
#include
namespace Ui {
@@ -30,6 +32,7 @@ class DuplicateRecordWindow : public QDialog
Ui::DuplicateRecordWindow *ui;
QStringList disallowedRecordIds;
+ RecordNameValidator nameValidator;
bool validate();
};
diff --git a/Source/Tome/Features/Records/View/recordfieldstablewidget.cpp b/Source/Tome/Features/Records/View/recordfieldstablewidget.cpp
index b690bce0..4f3170c8 100644
--- a/Source/Tome/Features/Records/View/recordfieldstablewidget.cpp
+++ b/Source/Tome/Features/Records/View/recordfieldstablewidget.cpp
@@ -77,24 +77,44 @@ void RecordFieldsTableWidget::setRecord(int i, const QString recordId)
}
// Show hyperlink for reference fields, and normal text for other fields.
- if (field.fieldType == BuiltInType::Reference)
+ if (this->typesController.isReferenceType(field.fieldType))
{
- QLabel* valueLabel = new QLabel("" + valueString + "");
-
- connect(
- valueLabel,
- SIGNAL(linkActivated(const QString&)),
- SLOT(onRecordLinkActivated(const QString&))
- );
+ QString href = "" + valueString + "";
+ QModelIndex index = this->model()->index(i, 1);
+ // Check for an existing index widget
+ QWidget *indexWidget = this->indexWidget(index);
+ if (nullptr != indexWidget)
+ {
+ QLabel* valueLabel = static_cast(indexWidget);
+ valueLabel->setText(href);
+ }
+ // Create a new index widget
+ else
+ {
+ QLabel* valueLabel = new QLabel(href);
- // Add margin for increased readability.
- valueLabel->setMargin(5);
+ connect(
+ valueLabel,
+ SIGNAL(linkActivated(const QString&)),
+ SLOT(onRecordLinkActivated(const QString&))
+ );
- QModelIndex index = this->model()->index(i, 1);
- this->setIndexWidget(index, valueLabel);
+ // Add margin for increased readability.
+ valueLabel->setMargin(5);
+ this->setIndexWidget(index, valueLabel);
+ }
}
else
{
+ // Remove any index widget
+ QModelIndex index = this->model()->index(i, 1);
+ QWidget *indexWidget = this->indexWidget(index);
+ if (nullptr != indexWidget)
+ {
+ this->setIndexWidget(index, nullptr);
+ delete indexWidget;
+ }
+
// Show normal text.
this->item(i, 1)->setData(Qt::DisplayRole, valueString);
diff --git a/Source/Tome/Features/Records/View/recordtreewidget.cpp b/Source/Tome/Features/Records/View/recordtreewidget.cpp
index b70fad02..39ac725f 100644
--- a/Source/Tome/Features/Records/View/recordtreewidget.cpp
+++ b/Source/Tome/Features/Records/View/recordtreewidget.cpp
@@ -4,12 +4,14 @@
#include "recordtreewidgetitem.h"
#include "../Controller/recordscontroller.h"
+#include "../../Settings/Controller/settingscontroller.h"
using namespace Tome;
-RecordTreeWidget::RecordTreeWidget(RecordsController& recordsController)
+RecordTreeWidget::RecordTreeWidget(RecordsController& recordsController, SettingsController& settingsController)
: recordsController(recordsController)
+ , settingsController(settingsController)
{
this->setDragEnabled(true);
this->viewport()->setAcceptDrops(true);
@@ -102,13 +104,13 @@ void RecordTreeWidget::selectRecord(const QString& id)
while (*it)
{
- if ((*it)->text(0) == id)
- {
- this->setCurrentItem((*it));
- break;
- }
-
- ++it;
+ RecordTreeWidgetItem *item = static_cast(*it);
+ if (item->getId() == id)
+ {
+ this->setCurrentItem(item);
+ break;
+ }
+ ++it;
}
}
@@ -126,8 +128,14 @@ void RecordTreeWidget::setRecords(const RecordList& records)
new RecordTreeWidgetItem(record.id, record.displayName, record.parentId, record.readOnly);
recordItems.insert(record.id, recordItem);
updateRecordItem( recordItem );
+
+ // Report progress.
+ emit this->progressChanged(tr("Refreshing Records"), record.id, i, records.size());
}
+ // Report finish.
+ emit this->progressChanged(tr("Refreshing Records"), QString(), 1, 1);
+
// Build hierarchy and prepare item list for tree widget.
QList items;
@@ -157,7 +165,10 @@ void RecordTreeWidget::setRecords(const RecordList& records)
// Fill tree widget.
this->insertTopLevelItems(0, items);
- this->expandAll();
+ if (this->settingsController.getExpandRecordTreeOnRefresh())
+ {
+ this->expandAll();
+ }
}
bool RecordTreeWidget::dropMimeData(QTreeWidgetItem* parent, int index, const QMimeData* data, Qt::DropAction action)
diff --git a/Source/Tome/Features/Records/View/recordtreewidget.h b/Source/Tome/Features/Records/View/recordtreewidget.h
index 59b29247..60f1d054 100644
--- a/Source/Tome/Features/Records/View/recordtreewidget.h
+++ b/Source/Tome/Features/Records/View/recordtreewidget.h
@@ -10,13 +10,14 @@ namespace Tome
{
class RecordsController;
class RecordTreeWidgetItem;
+ class SettingsController;
class RecordTreeWidget : public QTreeWidget
{
Q_OBJECT
public:
- RecordTreeWidget(RecordsController& recordsController);
+ RecordTreeWidget(RecordsController& recordsController, SettingsController& settingsController);
void addRecord(const QString& id, const QString& displayName);
@@ -30,6 +31,7 @@ namespace Tome
void setRecords(const RecordList& records);
signals:
+ void progressChanged(const QString title, const QString text, const int currentValue, const int maximumValue) const;
void recordReparented(const QString& recordId, const QString& newParentId);
protected:
@@ -38,6 +40,7 @@ namespace Tome
private:
RecordsController& recordsController;
+ SettingsController& settingsController;
};
}
diff --git a/Source/Tome/Features/Records/View/recordwindow.cpp b/Source/Tome/Features/Records/View/recordwindow.cpp
index d1e49327..b2e8813d 100644
--- a/Source/Tome/Features/Records/View/recordwindow.cpp
+++ b/Source/Tome/Features/Records/View/recordwindow.cpp
@@ -16,6 +16,8 @@ RecordWindow::RecordWindow(QWidget *parent) :
ui(new Ui::RecordWindow)
{
ui->setupUi(this);
+
+ this->ui->lineEditId->setValidator(&nameValidator);
}
RecordWindow::~RecordWindow()
diff --git a/Source/Tome/Features/Records/View/recordwindow.h b/Source/Tome/Features/Records/View/recordwindow.h
index 0fe8c851..bae7715a 100644
--- a/Source/Tome/Features/Records/View/recordwindow.h
+++ b/Source/Tome/Features/Records/View/recordwindow.h
@@ -7,6 +7,7 @@
#include "../../Fields/Model/fielddefinitionlist.h"
#include "../../Components/Model/componentlist.h"
#include "../../Records/Model/recordfieldvaluemap.h"
+#include "../../Records/Controller/recordnamevalidator.h"
namespace Ui {
class RecordWindow;
@@ -60,6 +61,7 @@ class RecordWindow : public QDialog
Ui::RecordWindow *ui;
QStringList disallowedRecordIds;
+ RecordNameValidator nameValidator;
// Whether to automatically update the record id to reflect the display name, or not.
bool recordIdLocked;
diff --git a/Source/Tome/Features/Search/Controller/findrecordcontroller.cpp b/Source/Tome/Features/Search/Controller/findrecordcontroller.cpp
index b34a6de4..c2ed9dc9 100644
--- a/Source/Tome/Features/Search/Controller/findrecordcontroller.cpp
+++ b/Source/Tome/Features/Search/Controller/findrecordcontroller.cpp
@@ -11,6 +11,8 @@ FindRecordController::FindRecordController(const Tome::RecordsController& record
const SearchResultList FindRecordController::findRecord(const QString& searchPattern)
{
+ qInfo(QString("Finding records matching pattern %1.").arg(searchPattern).toUtf8().constData());
+
// Build search result list.
SearchResultList results;
@@ -21,6 +23,9 @@ const SearchResultList FindRecordController::findRecord(const QString& searchPat
{
const Record& record = records[i];
+ // Report progress.
+ emit this->progressChanged(tr("Searching"), record.id, i, records.length());
+
if (record.id.toLower().contains(searchPattern.toLower()) ||
record.displayName.toLower().contains(searchPattern.toLower()))
{
@@ -33,6 +38,9 @@ const SearchResultList FindRecordController::findRecord(const QString& searchPat
}
}
+ // Report finish.
+ emit this->progressChanged(tr("Searching"), QString(), 1, 1);
+
emit searchResultChanged("Find " + searchPattern, results);
return results;
}
diff --git a/Source/Tome/Features/Search/Controller/findrecordcontroller.h b/Source/Tome/Features/Search/Controller/findrecordcontroller.h
index bfb209a5..c149bd86 100644
--- a/Source/Tome/Features/Search/Controller/findrecordcontroller.h
+++ b/Source/Tome/Features/Search/Controller/findrecordcontroller.h
@@ -19,6 +19,7 @@ namespace Tome
const SearchResultList findRecord(const QString& searchPattern);
signals:
+ void progressChanged(const QString title, const QString text, const int currentValue, const int maximumValue);
void searchResultChanged(const QString& title, const Tome::SearchResultList results);
private:
diff --git a/Source/Tome/Features/Search/Controller/findusagescontroller.cpp b/Source/Tome/Features/Search/Controller/findusagescontroller.cpp
index 93051b83..5420e8af 100644
--- a/Source/Tome/Features/Search/Controller/findusagescontroller.cpp
+++ b/Source/Tome/Features/Search/Controller/findusagescontroller.cpp
@@ -17,6 +17,8 @@ FindUsagesController::FindUsagesController(const FieldDefinitionsController& fie
const SearchResultList FindUsagesController::findUsagesOfField(const QString& fieldId)
{
+ qInfo(QString("Finding usages of field %1.").arg(fieldId).toUtf8().constData());
+
// Build search result list.
SearchResultList results;
@@ -26,6 +28,10 @@ const SearchResultList FindUsagesController::findUsagesOfField(const QString& fi
for (int i = 0; i < records.length(); ++i)
{
const Record& record = records[i];
+
+ // Report progress.
+ emit this->progressChanged(tr("Searching"), record.id, i, records.length());
+
const RecordFieldValueMap& fieldValues = this->recordsController.getRecordFieldValues(record.id);
for (RecordFieldValueMap::const_iterator it = fieldValues.begin();
@@ -46,12 +52,17 @@ const SearchResultList FindUsagesController::findUsagesOfField(const QString& fi
}
}
+ // Report finish.
+ emit this->progressChanged(tr("Searching"), QString(), 1, 1);
+
emit searchResultChanged("Usages of " + fieldId, results);
return results;
}
const SearchResultList FindUsagesController::findUsagesOfRecord(const QString& recordId)
{
+ qInfo(QString("Finding usages of record %1.").arg(recordId).toUtf8().constData());
+
// Build search result list.
SearchResultList results;
@@ -61,6 +72,10 @@ const SearchResultList FindUsagesController::findUsagesOfRecord(const QString& r
for (int i = 0; i < records.length(); ++i)
{
const Record& record = records[i];
+
+ // Report progress.
+ emit this->progressChanged(tr("Searching"), record.id, i, records.length());
+
const RecordFieldValueMap& fieldValues = this->recordsController.getRecordFieldValues(record.id);
for (RecordFieldValueMap::const_iterator it = fieldValues.begin();
@@ -70,7 +85,7 @@ const SearchResultList FindUsagesController::findUsagesOfRecord(const QString& r
const FieldDefinition& field = this->fieldDefinitionsController.getFieldDefinition(it.key());
const QVariant& fieldValue = it.value();
- if (field.fieldType == BuiltInType::Reference && fieldValue == recordId)
+ if (this->typesController.isReferenceType(field.fieldType) && fieldValue == recordId)
{
SearchResult result;
result.content = field.id;
@@ -82,12 +97,17 @@ const SearchResultList FindUsagesController::findUsagesOfRecord(const QString& r
}
}
+ // Report finish.
+ emit this->progressChanged(tr("Searching"), QString(), 1, 1);
+
emit searchResultChanged("Usages of " + recordId, results);
return results;
}
const SearchResultList FindUsagesController::findUsagesOfType(const QString& typeName)
{
+ qInfo(QString("Finding usages of type %1.").arg(typeName).toUtf8().constData());
+
// Build search result list.
SearchResultList results;
@@ -97,6 +117,10 @@ const SearchResultList FindUsagesController::findUsagesOfType(const QString& typ
for (int i = 0; i < records.length(); ++i)
{
const Record& record = records[i];
+
+ // Report progress.
+ emit this->progressChanged(tr("Searching"), record.id, i, records.length());
+
const RecordFieldValueMap& fieldValues = this->recordsController.getRecordFieldValues(record.id);
for (RecordFieldValueMap::const_iterator it = fieldValues.begin();
@@ -117,6 +141,9 @@ const SearchResultList FindUsagesController::findUsagesOfType(const QString& typ
}
}
+ // Report finish.
+ emit this->progressChanged(tr("Searching"), QString(), 1, 1);
+
emit searchResultChanged("Usages of " + typeName, results);
return results;
}
diff --git a/Source/Tome/Features/Search/Controller/findusagescontroller.h b/Source/Tome/Features/Search/Controller/findusagescontroller.h
index 0ee27a21..5e1a7f71 100644
--- a/Source/Tome/Features/Search/Controller/findusagescontroller.h
+++ b/Source/Tome/Features/Search/Controller/findusagescontroller.h
@@ -23,6 +23,7 @@ namespace Tome
const SearchResultList findUsagesOfType(const QString& typeName);
signals:
+ void progressChanged(const QString title, const QString text, const int currentValue, const int maximumValue);
void searchResultChanged(const QString& title, const Tome::SearchResultList results);
private:
diff --git a/Source/Tome/Features/Search/View/searchresultsdockwidget.cpp b/Source/Tome/Features/Search/View/searchresultsdockwidget.cpp
index 4f194ce9..adbdee46 100644
--- a/Source/Tome/Features/Search/View/searchresultsdockwidget.cpp
+++ b/Source/Tome/Features/Search/View/searchresultsdockwidget.cpp
@@ -52,6 +52,9 @@ void SearchResultsDockWidget::showResults(const QString& title, const SearchResu
{
const SearchResult& result = this->results.at(i);
+ // Report progress.
+ emit this->progressChanged(tr("Collecting results"), result.content, i, this->results.count());
+
// Create table row.
this->tableWidget->setItem(i, 0, new QTableWidgetItem());
QLabel* resultLabel;
@@ -77,6 +80,9 @@ void SearchResultsDockWidget::showResults(const QString& title, const SearchResu
QModelIndex index = this->tableWidget->model()->index(i, 0);
this->tableWidget->setIndexWidget(index, resultLabel);
}
+
+ // Report finish.
+ emit this->progressChanged(tr("Collecting results"), QString(), 1, 1);
}
void SearchResultsDockWidget::onRecordLinkActivated(const QString& recordId)
diff --git a/Source/Tome/Features/Search/View/searchresultsdockwidget.h b/Source/Tome/Features/Search/View/searchresultsdockwidget.h
index 5901fbce..c7044ac3 100644
--- a/Source/Tome/Features/Search/View/searchresultsdockwidget.h
+++ b/Source/Tome/Features/Search/View/searchresultsdockwidget.h
@@ -20,6 +20,7 @@ namespace Tome
void showResults(const QString& title, const SearchResultList& results);
signals:
+ void progressChanged(const QString title, const QString text, const int currentValue, const int maximumValue);
void recordLinkActivated(const QString& recordId);
private:
diff --git a/Source/Tome/Features/Settings/Controller/settingscontroller.cpp b/Source/Tome/Features/Settings/Controller/settingscontroller.cpp
index f94270b3..77d06602 100644
--- a/Source/Tome/Features/Settings/Controller/settingscontroller.cpp
+++ b/Source/Tome/Features/Settings/Controller/settingscontroller.cpp
@@ -7,8 +7,10 @@ using namespace Tome;
const QString SettingsController::SettingPath = "path";
const QString SettingsController::SettingRecentProjects = "recentProjects";
+const QString SettingsController::SettingRunIntegrityChecksOnLoad = "runIntegrityChecksOnLoad";
const QString SettingsController::SettingRunIntegrityChecksOnSave = "runIntegrityChecksOnSave";
const QString SettingsController::SettingShowDescriptionColumnInsteadOfFieldTooltips = "showDetailsColumnInsteadOfFieldTooltips";
+const QString SettingsController::SettingExpandRecordTreeOnRefresh = "expandRecordTreeOnRefresh";
SettingsController::SettingsController()
@@ -27,6 +29,8 @@ SettingsController::~SettingsController()
void SettingsController::addRecentProject(const QString& path)
{
+ qInfo(QString("Adding %1 to recent projects list.").arg(path).toUtf8().constData());
+
QStringList recentProjects = this->getRecentProjects();
recentProjects.removeOne(path);
@@ -51,6 +55,11 @@ const QStringList SettingsController::getRecentProjects() const
return recentProjects;
}
+bool SettingsController::getRunIntegrityChecksOnLoad() const
+{
+ return this->settings->value(SettingRunIntegrityChecksOnLoad).toBool();
+}
+
bool SettingsController::getRunIntegrityChecksOnSave() const
{
return this->settings->value(SettingRunIntegrityChecksOnSave).toBool();
@@ -61,8 +70,15 @@ bool SettingsController::getShowDescriptionColumnInsteadOfFieldTooltips() const
return this->settings->value(SettingShowDescriptionColumnInsteadOfFieldTooltips).toBool();
}
+bool SettingsController::getExpandRecordTreeOnRefresh() const
+{
+ return this->settings->value(SettingExpandRecordTreeOnRefresh).toBool();
+}
+
void SettingsController::removeRecentProject(const QString& path)
{
+ qInfo(QString("Removing %1 from recent projects list.").arg(path).toUtf8().constData());
+
QStringList recentProjects = this->getRecentProjects();
recentProjects.removeOne(path);
this->setRecentProjects(recentProjects);
@@ -79,12 +95,35 @@ void SettingsController::setRecentProjects(const QStringList& recentProjects)
this->settings->endArray();
}
+void SettingsController::setRunIntegrityChecksOnLoad(bool runIntegrityChecksOnLoad)
+{
+ qInfo(QString("Setting run integrity checks on load to %1.")
+ .arg(runIntegrityChecksOnLoad ? "true" : "false")
+ .toUtf8().constData());
+ this->settings->setValue(SettingRunIntegrityChecksOnLoad, runIntegrityChecksOnLoad);
+}
+
void SettingsController::setRunIntegrityChecksOnSave(bool runIntegrityChecksOnSave)
{
+ qInfo(QString("Setting run integrity checks on save to %1.")
+ .arg(runIntegrityChecksOnSave ? "true" : "false")
+ .toUtf8().constData());
this->settings->setValue(SettingRunIntegrityChecksOnSave, runIntegrityChecksOnSave);
}
void SettingsController::setShowDescriptionColumnInsteadOfFieldTooltips(bool showDescriptionColumnInsteadOfFieldTooltips)
{
+ qInfo(QString("Setting show description column in stead of field tooltips to %1.")
+ .arg(showDescriptionColumnInsteadOfFieldTooltips ? "true" : "false")
+ .toUtf8().constData());
this->settings->setValue(SettingShowDescriptionColumnInsteadOfFieldTooltips, showDescriptionColumnInsteadOfFieldTooltips);
}
+
+void SettingsController::setExpandRecordTreeOnRefresh(bool expandRecordTreeOnRefresh)
+{
+ qInfo(QString("Setting expand record tree on refresh to %1.")
+ .arg(expandRecordTreeOnRefresh ? "true" : "false")
+ .toUtf8().constData());
+ this->settings->setValue(SettingExpandRecordTreeOnRefresh, expandRecordTreeOnRefresh);
+}
+
diff --git a/Source/Tome/Features/Settings/Controller/settingscontroller.h b/Source/Tome/Features/Settings/Controller/settingscontroller.h
index d8d73dc0..e83ec8bf 100644
--- a/Source/Tome/Features/Settings/Controller/settingscontroller.h
+++ b/Source/Tome/Features/Settings/Controller/settingscontroller.h
@@ -14,18 +14,24 @@ namespace Tome
void addRecentProject(const QString& path);
const QStringList getRecentProjects() const;
+ bool getRunIntegrityChecksOnLoad() const;
bool getRunIntegrityChecksOnSave() const;
bool getShowDescriptionColumnInsteadOfFieldTooltips() const;
+ bool getExpandRecordTreeOnRefresh() const;
void removeRecentProject(const QString& path);
void setRecentProjects(const QStringList& recentProjects);
+ void setRunIntegrityChecksOnLoad(bool runIntegrityChecksOnLoad);
void setRunIntegrityChecksOnSave(bool runIntegrityChecksOnSave);
void setShowDescriptionColumnInsteadOfFieldTooltips(bool showDescriptionColumnInsteadOfFieldTooltips);
+ void setExpandRecordTreeOnRefresh(bool expandRecordTreeOnRefresh);
private:
static const QString SettingPath;
static const QString SettingRecentProjects;
+ static const QString SettingRunIntegrityChecksOnLoad;
static const QString SettingRunIntegrityChecksOnSave;
static const QString SettingShowDescriptionColumnInsteadOfFieldTooltips;
+ static const QString SettingExpandRecordTreeOnRefresh;
QSettings* settings;
};
diff --git a/Source/Tome/Features/Settings/View/usersettingswindow.cpp b/Source/Tome/Features/Settings/View/usersettingswindow.cpp
index 38290f35..131cacee 100644
--- a/Source/Tome/Features/Settings/View/usersettingswindow.cpp
+++ b/Source/Tome/Features/Settings/View/usersettingswindow.cpp
@@ -20,6 +20,11 @@ UserSettingsWindow::~UserSettingsWindow()
delete ui;
}
+bool UserSettingsWindow::getRunIntegrityChecksOnLoad()
+{
+ return this->ui->checkBoxRunIntegrityChecksOnLoad->isChecked();
+}
+
bool UserSettingsWindow::getRunIntegrityChecksOnSave()
{
return this->ui->checkBoxRunIntegrityChecksOnSave->isChecked();
@@ -30,14 +35,25 @@ bool UserSettingsWindow::getShowDescriptionColumnInsteadOfFieldTooltips()
return this->ui->checkBoxShowDescriptionColumnInsteadOfFieldTooltips->isChecked();
}
+bool UserSettingsWindow::getExpandRecordTreeOnRefresh()
+{
+ return this->ui->checkBoxExpandRecordTreeOnRefresh->isChecked();
+}
+
void UserSettingsWindow::showEvent(QShowEvent* event)
{
Q_UNUSED(event);
// Update view from model.
+ bool runIntegrityChecksOnLoad = this->settingsController.getRunIntegrityChecksOnLoad();
+ this->ui->checkBoxRunIntegrityChecksOnLoad->setChecked(runIntegrityChecksOnLoad);
+
bool runIntegrityChecksOnSave = this->settingsController.getRunIntegrityChecksOnSave();
this->ui->checkBoxRunIntegrityChecksOnSave->setChecked(runIntegrityChecksOnSave);
bool showDescriptionColumnInsteadOfFieldTooltips = this->settingsController.getShowDescriptionColumnInsteadOfFieldTooltips();
this->ui->checkBoxShowDescriptionColumnInsteadOfFieldTooltips->setChecked(showDescriptionColumnInsteadOfFieldTooltips);
+
+ bool expandRecordTreeOnRefresh = this->settingsController.getExpandRecordTreeOnRefresh();
+ this->ui->checkBoxExpandRecordTreeOnRefresh->setChecked(expandRecordTreeOnRefresh);
}
diff --git a/Source/Tome/Features/Settings/View/usersettingswindow.h b/Source/Tome/Features/Settings/View/usersettingswindow.h
index e815a100..546762c2 100644
--- a/Source/Tome/Features/Settings/View/usersettingswindow.h
+++ b/Source/Tome/Features/Settings/View/usersettingswindow.h
@@ -22,8 +22,10 @@ class UserSettingsWindow : public QDialog
QWidget *parent = 0);
~UserSettingsWindow();
+ bool getRunIntegrityChecksOnLoad();
bool getRunIntegrityChecksOnSave();
bool getShowDescriptionColumnInsteadOfFieldTooltips();
+ bool getExpandRecordTreeOnRefresh();
protected:
void showEvent(QShowEvent * event);
diff --git a/Source/Tome/Features/Settings/View/usersettingswindow.ui b/Source/Tome/Features/Settings/View/usersettingswindow.ui
index 501f5cd5..9310b16a 100644
--- a/Source/Tome/Features/Settings/View/usersettingswindow.ui
+++ b/Source/Tome/Features/Settings/View/usersettingswindow.ui
@@ -7,7 +7,7 @@
0
0
400
- 87
+ 133
@@ -21,6 +21,13 @@
+ -
+
+
+ Run integrity checks on load
+
+
+
-
@@ -28,6 +35,13 @@
+ -
+
+
+ Expand record tree on refresh
+
+
+
-
diff --git a/Source/Tome/Features/Tasks/Controller/taskscontroller.cpp b/Source/Tome/Features/Tasks/Controller/taskscontroller.cpp
index a79abbbf..ef5a0479 100644
--- a/Source/Tome/Features/Tasks/Controller/taskscontroller.cpp
+++ b/Source/Tome/Features/Tasks/Controller/taskscontroller.cpp
@@ -11,8 +11,14 @@
using namespace Tome;
-TasksController::TasksController(const ComponentsController& componentsController, const FieldDefinitionsController& fieldDefinitionsController, const RecordsController& recordsController, const TypesController& typesController)
+TasksController::TasksController(
+ const ComponentsController& componentsController,
+ const FacetsController& facetsController,
+ const FieldDefinitionsController& fieldDefinitionsController,
+ const RecordsController& recordsController,
+ const TypesController& typesController)
: componentsController(componentsController),
+ facetsController(facetsController),
fieldDefinitionsController(fieldDefinitionsController),
recordsController(recordsController),
typesController(typesController)
@@ -35,7 +41,12 @@ void TasksController::addTask(Task* task)
const MessageList TasksController::runAllTasks() const
{
// Build context.
- TaskContext context(this->componentsController, this->fieldDefinitionsController, this->recordsController, this->typesController);
+ TaskContext context(
+ this->componentsController,
+ this->facetsController,
+ this->fieldDefinitionsController,
+ this->recordsController,
+ this->typesController);
// Build message list.
MessageList messages;
diff --git a/Source/Tome/Features/Tasks/Controller/taskscontroller.h b/Source/Tome/Features/Tasks/Controller/taskscontroller.h
index 3619c348..1d2ea100 100644
--- a/Source/Tome/Features/Tasks/Controller/taskscontroller.h
+++ b/Source/Tome/Features/Tasks/Controller/taskscontroller.h
@@ -11,6 +11,7 @@ namespace Tome
class Task;
class ComponentsController;
+ class FacetsController;
class FieldDefinitionsController;
class RecordsController;
class TypesController;
@@ -18,7 +19,7 @@ namespace Tome
class TasksController
{
public:
- TasksController(const ComponentsController& componentsController, const FieldDefinitionsController& fieldDefinitionsController, const RecordsController& recordsController, const TypesController& typesController);
+ TasksController(const ComponentsController& componentsController, const FacetsController& facetsController, const FieldDefinitionsController& fieldDefinitionsController, const RecordsController& recordsController, const TypesController& typesController);
~TasksController();
void addTask(Task* task);
@@ -28,6 +29,7 @@ namespace Tome
QList tasks;
const ComponentsController& componentsController;
+ const FacetsController& facetsController;
const FieldDefinitionsController& fieldDefinitionsController;
const RecordsController& recordsController;
const TypesController& typesController;
diff --git a/Source/Tome/Features/Tasks/Model/taskcontext.cpp b/Source/Tome/Features/Tasks/Model/taskcontext.cpp
index 57fcc9a6..b740031f 100644
--- a/Source/Tome/Features/Tasks/Model/taskcontext.cpp
+++ b/Source/Tome/Features/Tasks/Model/taskcontext.cpp
@@ -1,6 +1,7 @@
#include "taskcontext.h"
#include "../../Components/Controller/componentscontroller.h"
+#include "../../Facets/Controller/facetscontroller.h"
#include "../../Fields/Controller/fielddefinitionscontroller.h"
#include "../../Records/Controller/recordscontroller.h"
#include "../../Types/Controller/typescontroller.h"
@@ -8,8 +9,14 @@
using namespace Tome;
-TaskContext::TaskContext(const ComponentsController& componentsController, const FieldDefinitionsController& fieldDefinitionsController, const RecordsController& recordsController, const TypesController& typesController)
+TaskContext::TaskContext(
+ const ComponentsController& componentsController,
+ const FacetsController& facetsController,
+ const FieldDefinitionsController& fieldDefinitionsController,
+ const RecordsController& recordsController,
+ const TypesController& typesController)
: componentsController(componentsController),
+ facetsController(facetsController),
fieldDefinitionsController(fieldDefinitionsController),
recordsController(recordsController),
typesController(typesController)
diff --git a/Source/Tome/Features/Tasks/Model/taskcontext.h b/Source/Tome/Features/Tasks/Model/taskcontext.h
index 6555cfd1..c36b41d0 100644
--- a/Source/Tome/Features/Tasks/Model/taskcontext.h
+++ b/Source/Tome/Features/Tasks/Model/taskcontext.h
@@ -4,6 +4,7 @@
namespace Tome
{
class ComponentsController;
+ class FacetsController;
class FieldDefinitionsController;
class RecordsController;
class TypesController;
@@ -13,11 +14,13 @@ namespace Tome
public:
TaskContext(
const ComponentsController& componentsController,
+ const FacetsController& facetsController,
const FieldDefinitionsController& fieldDefinitionsController,
const RecordsController& recordsController,
const TypesController& typesController);
const ComponentsController& componentsController;
+ const FacetsController& facetsController;
const FieldDefinitionsController& fieldDefinitionsController;
const RecordsController& recordsController;
const TypesController& typesController;
diff --git a/Source/Tome/Features/Tasks/View/errorlistdockwidget.cpp b/Source/Tome/Features/Tasks/View/errorlistdockwidget.cpp
index 1b3b1a82..daac47af 100644
--- a/Source/Tome/Features/Tasks/View/errorlistdockwidget.cpp
+++ b/Source/Tome/Features/Tasks/View/errorlistdockwidget.cpp
@@ -175,8 +175,27 @@ void ErrorListDockWidget::refreshMessages()
this->tableWidgetErrorList->setItem(i, 2, new QTableWidgetItem(message.content));
// Show location.
- QString location = TargetSiteType::toString(message.targetSiteType) + " - " + message.targetSiteId;
- this->tableWidgetErrorList->setItem(i, 3, new QTableWidgetItem(location));
+ QLabel* locationLabel;
+
+ if (message.targetSiteType == TargetSiteType::Record)
+ {
+ QString locationLink = QString("Record - %1").arg(message.targetSiteId);
+ locationLabel = new QLabel(locationLink);
+
+ connect(
+ locationLabel,
+ SIGNAL(linkActivated(const QString&)),
+ SLOT(onRecordLinkActivated(const QString&))
+ );
+ }
+ else
+ {
+ QString locationString = TargetSiteType::toString(message.targetSiteType) + " - " + message.targetSiteId;
+ locationLabel = new QLabel(locationString);
+ }
+
+ index = this->tableWidgetErrorList->model()->index(i, 3);
+ this->tableWidgetErrorList->setIndexWidget(index, locationLabel);
// Increase row counter.
++messagesShown;
@@ -194,3 +213,8 @@ void ErrorListDockWidget::refreshMessages()
this->tableWidgetErrorList->setHorizontalHeaderLabels(headers);
this->tableWidgetErrorList->resizeColumnsToContents();
}
+
+void ErrorListDockWidget::onRecordLinkActivated(const QString& recordId)
+{
+ emit this->recordLinkActivated(recordId);
+}
diff --git a/Source/Tome/Features/Tasks/View/errorlistdockwidget.h b/Source/Tome/Features/Tasks/View/errorlistdockwidget.h
index d5567f2a..1a38f122 100644
--- a/Source/Tome/Features/Tasks/View/errorlistdockwidget.h
+++ b/Source/Tome/Features/Tasks/View/errorlistdockwidget.h
@@ -13,12 +13,17 @@ namespace Tome
{
class ErrorListDockWidget : public QDockWidget
{
+ Q_OBJECT
+
public:
ErrorListDockWidget(QWidget* parent);
~ErrorListDockWidget();
void showMessages(const MessageList& messages);
+ signals:
+ void recordLinkActivated(const QString& recordId);
+
private:
QWidget* widget;
@@ -39,6 +44,9 @@ namespace Tome
void on_toolButtonMessages_toggled(bool checked);
void refreshMessages();
+
+ private slots:
+ void onRecordLinkActivated(const QString& recordId);
};
}
diff --git a/Source/Tome/Features/Types/Controller/customtypesetserializer.cpp b/Source/Tome/Features/Types/Controller/customtypesetserializer.cpp
index 8db4d74c..4e4bc945 100644
--- a/Source/Tome/Features/Types/Controller/customtypesetserializer.cpp
+++ b/Source/Tome/Features/Types/Controller/customtypesetserializer.cpp
@@ -10,12 +10,20 @@ using namespace Tome;
const QString CustomTypeSetSerializer::AttributeKey = "Key";
const QString CustomTypeSetSerializer::AttributeValue = "Value";
+const QString CustomTypeSetSerializer::AttributeVersionDeprecated = "Value";
+const QString CustomTypeSetSerializer::AttributeVersion = "Version";
+const QString CustomTypeSetSerializer::ElementConstrainingFacet = "ConstrainingFacet";
+const QString CustomTypeSetSerializer::ElementConstrainingFacets = "ConstrainingFacets";
+const QString CustomTypeSetSerializer::ElementFundamentalFacet = "FundamentalFacet";
+const QString CustomTypeSetSerializer::ElementFundamentalFacets = "FundamentalFacets";
const QString CustomTypeSetSerializer::ElementName = "Name";
const QString CustomTypeSetSerializer::ElementRestriction = "Restriction";
const QString CustomTypeSetSerializer::ElementRestrictions = "Restrictions";
const QString CustomTypeSetSerializer::ElementType = "Type";
const QString CustomTypeSetSerializer::ElementTypes = "Types";
+const int CustomTypeSetSerializer::Version = 2;
+
CustomTypeSetSerializer::CustomTypeSetSerializer()
{
@@ -33,6 +41,9 @@ void CustomTypeSetSerializer::serialize(QIODevice& device, const CustomTypeSet&
// Write types.
writer.writeStartElement(ElementTypes);
{
+ // Write version.
+ writer.writeAttribute(AttributeVersion, QString::number(Version));
+
for (int i = 0; i < customTypeSet.types.size(); ++i)
{
const CustomType& type = customTypeSet.types.at(i);
@@ -41,16 +52,30 @@ void CustomTypeSetSerializer::serialize(QIODevice& device, const CustomTypeSet&
{
writer.writeAttribute(ElementName, type.name);
- // Write restrictions map.
- writer.writeStartElement(ElementRestrictions);
+ // Write facet maps.
+ writer.writeStartElement(ElementFundamentalFacets);
+ {
+ for (QVariantMap::const_iterator itFundamentalFacets = type.fundamentalFacets.begin();
+ itFundamentalFacets != type.fundamentalFacets.end();
+ ++itFundamentalFacets)
+ {
+ writer.writeStartElement(ElementFundamentalFacet);
+ writer.writeAttribute(AttributeKey, itFundamentalFacets.key());
+ writer.writeAttribute(AttributeValue, itFundamentalFacets.value().toString());
+ writer.writeEndElement();
+ }
+ }
+ writer.writeEndElement();
+
+ writer.writeStartElement(ElementConstrainingFacets);
{
- for (QMap::const_iterator itRestrictions = type.restrictions.begin();
- itRestrictions != type.restrictions.end();
- ++itRestrictions)
+ for (QVariantMap::const_iterator itConstrainingFacets = type.constrainingFacets.begin();
+ itConstrainingFacets != type.constrainingFacets.end();
+ ++itConstrainingFacets)
{
- writer.writeStartElement(ElementRestriction);
- writer.writeAttribute(AttributeKey, itRestrictions.key());
- writer.writeAttribute(AttributeValue, itRestrictions.value());
+ writer.writeStartElement(ElementConstrainingFacet);
+ writer.writeAttribute(AttributeKey, itConstrainingFacets.key());
+ writer.writeAttribute(AttributeValue, itConstrainingFacets.value().toString());
writer.writeEndElement();
}
}
@@ -74,6 +99,14 @@ void CustomTypeSetSerializer::deserialize(QIODevice& device, CustomTypeSet& cust
// Begin document.
reader.readStartDocument();
{
+ // Read version.
+ int version = reader.readAttribute(AttributeVersion).toInt();
+
+ if (version == 0)
+ {
+ version = reader.readAttribute(AttributeVersionDeprecated).toInt();
+ }
+
// Read types.
reader.readStartElement(ElementTypes);
{
@@ -86,21 +119,57 @@ void CustomTypeSetSerializer::deserialize(QIODevice& device, CustomTypeSet& cust
reader.readStartElement(ElementType);
{
- // Read type restriction map.
- reader.readStartElement(ElementRestrictions);
+ if (version >= 2)
+ {
+ // Read facet maps.
+ reader.readStartElement(ElementFundamentalFacets);
+ {
+ while (reader.isAtElement(ElementFundamentalFacet))
+ {
+ QString restrictionKey = reader.readAttribute(AttributeKey);
+ QString restrictionValue = reader.readAttribute(AttributeValue);
+
+ type.fundamentalFacets.insert(restrictionKey, restrictionValue);
+
+ // Advance reader.
+ reader.readEmptyElement(ElementFundamentalFacet);
+ }
+ }
+ reader.readEndElement();
+
+ reader.readStartElement(ElementConstrainingFacets);
+ {
+ while (reader.isAtElement(ElementConstrainingFacet))
+ {
+ QString restrictionKey = reader.readAttribute(AttributeKey);
+ QString restrictionValue = reader.readAttribute(AttributeValue);
+
+ type.constrainingFacets.insert(restrictionKey, restrictionValue);
+
+ // Advance reader.
+ reader.readEmptyElement(ElementConstrainingFacet);
+ }
+ }
+ reader.readEndElement();
+ }
+ else
{
- while (reader.isAtElement(ElementRestriction))
+ // Read type restriction map.
+ reader.readStartElement(ElementRestrictions);
{
- QString restrictionKey = reader.readAttribute(AttributeKey);
- QString restrictionValue = reader.readAttribute(AttributeValue);
+ while (reader.isAtElement(ElementRestriction))
+ {
+ QString restrictionKey = reader.readAttribute(AttributeKey);
+ QString restrictionValue = reader.readAttribute(AttributeValue);
- type.restrictions.insert(restrictionKey, restrictionValue);
+ type.fundamentalFacets.insert(restrictionKey, restrictionValue);
- // Advance reader.
- reader.readEmptyElement(ElementRestriction);
+ // Advance reader.
+ reader.readEmptyElement(ElementRestriction);
+ }
}
+ reader.readEndElement();
}
- reader.readEndElement();
}
reader.readEndElement();
diff --git a/Source/Tome/Features/Types/Controller/customtypesetserializer.h b/Source/Tome/Features/Types/Controller/customtypesetserializer.h
index 77fe917b..1cf609e2 100644
--- a/Source/Tome/Features/Types/Controller/customtypesetserializer.h
+++ b/Source/Tome/Features/Types/Controller/customtypesetserializer.h
@@ -18,11 +18,19 @@ namespace Tome
private:
static const QString AttributeKey;
static const QString AttributeValue;
+ static const QString AttributeVersion;
+ static const QString AttributeVersionDeprecated;
+ static const QString ElementConstrainingFacet;
+ static const QString ElementConstrainingFacets;
+ static const QString ElementFundamentalFacet;
+ static const QString ElementFundamentalFacets;
static const QString ElementName;
static const QString ElementRestriction;
static const QString ElementRestrictions;
static const QString ElementType;
static const QString ElementTypes;
+
+ static const int Version;
};
}
diff --git a/Source/Tome/Features/Types/Controller/typescontroller.cpp b/Source/Tome/Features/Types/Controller/typescontroller.cpp
index 3db58c31..836eb0aa 100644
--- a/Source/Tome/Features/Types/Controller/typescontroller.cpp
+++ b/Source/Tome/Features/Types/Controller/typescontroller.cpp
@@ -19,8 +19,24 @@ void TypesController::addCustomTypeSet(const CustomTypeSet& customTypeSet)
this->model->push_back(customTypeSet);
}
+const CustomType TypesController::addDerivedType(const QString& name, const QString& baseType, const QVariantMap& facets, const QString& customTypeSetName)
+{
+ qInfo(QString("Adding derived type %1 with base type %2.").arg(name, baseType).toUtf8().constData());
+
+ CustomType newType = CustomType();
+ newType.name = name;
+ newType.constrainingFacets = facets;
+ newType.setBaseType(baseType);
+
+ this->addCustomType(newType, customTypeSetName);
+
+ return newType;
+}
+
const CustomType TypesController::addEnumeration(const QString& name, const QStringList& enumeration, const QString& customTypeSetName)
{
+ qInfo(QString("Adding enumeration %1.").arg(name).toUtf8().constData());
+
CustomType newType = CustomType();
newType.name = name;
newType.setEnumeration(enumeration);
@@ -32,6 +48,8 @@ const CustomType TypesController::addEnumeration(const QString& name, const QStr
const CustomType TypesController::addList(const QString& name, const QString& itemType, const QString& customTypeSetName)
{
+ qInfo(QString("Adding list %1.").arg(name).toUtf8().constData());
+
CustomType newType = CustomType();
newType.name = name;
newType.setItemType(itemType);
@@ -43,6 +61,8 @@ const CustomType TypesController::addList(const QString& name, const QString& it
const CustomType TypesController::addMap(const QString& name, const QString& keyType, const QString& valueType, const QString& customTypeSetName)
{
+ qInfo(QString("Adding map %1.").arg(name).toUtf8().constData());
+
CustomType newType = CustomType();
newType.name = name;
newType.setKeyType(keyType);
@@ -149,8 +169,26 @@ bool TypesController::isCustomType(const QString& name) const
return false;
}
+bool TypesController::isReferenceType(const QString& name) const
+{
+ if (name == BuiltInType::Reference)
+ {
+ return true;
+ }
+
+ if (!this->isCustomType(name))
+ {
+ return false;
+ }
+
+ const CustomType& type = this->getCustomType(name);
+ return type.isDerivedType() && type.getBaseType() == BuiltInType::Reference;
+}
+
void TypesController::moveCustomTypeToSet(const QString& customTypeName, const QString& customTypeSetName)
{
+ qInfo(QString("Moving type %1 to set %2.").arg(customTypeName, customTypeSetName).toUtf8().constData());
+
CustomType customType = this->getCustomType(customTypeName);
for (CustomTypeSetList::iterator itSets = this->model->begin();
@@ -187,6 +225,8 @@ void TypesController::moveCustomTypeToSet(const QString& customTypeName, const Q
void TypesController::removeCustomType(const QString& typeName)
{
+ qInfo(QString("Removing custom type %1.").arg(typeName).toUtf8().constData());
+
for (CustomTypeSetList::iterator itSets = this->model->begin();
itSets != this->model->end();
++itSets)
@@ -228,6 +268,8 @@ void TypesController::removeCustomTypeSet(const QString& name)
void TypesController::renameType(const QString oldName, const QString newName)
{
+ qInfo(QString("Renaming custom type %1 to %2.").arg(oldName, newName).toUtf8().constData());
+
CustomType& type = *this->getCustomTypeByName(oldName);
// Rename type.
@@ -269,6 +311,33 @@ void TypesController::setCustomTypes(CustomTypeSetList& model)
this->model = &model;
}
+void TypesController::updateDerivedType(const QString& oldName, const QString& newName, const QString& baseType, const QVariantMap facets, const QString& typeSetName)
+{
+ CustomType& type = *this->getCustomTypeByName(oldName);
+
+ bool needsSorting = type.name != newName;
+
+ this->renameType(oldName, newName);
+ type.setBaseType(baseType);
+ type.constrainingFacets = facets;
+
+ // Move type to other set, if necessary.
+ if (type.typeSetName != typeSetName)
+ {
+ this->moveCustomTypeToSet(type.name, typeSetName);
+ }
+
+ if (needsSorting)
+ {
+ for (CustomTypeSetList::iterator it = this->model->begin();
+ it != this->model->end();
+ ++it)
+ {
+ std::sort((*it).types.begin(), (*it).types.end(), customTypeLessThanName);
+ }
+ }
+}
+
void TypesController::updateEnumeration(const QString& oldName, const QString& newName, const QStringList& enumeration, const QString& typeSetName)
{
CustomType& type = *this->getCustomTypeByName(oldName);
@@ -417,6 +486,7 @@ void TypesController::addCustomType(CustomType customType, const QString& custom
}
const QString errorMessage = "Custom type set not found: " + customTypeSetName;
+ qCritical(errorMessage.toUtf8().constData());
throw std::out_of_range(errorMessage.toStdString());
}
@@ -438,5 +508,6 @@ CustomType* TypesController::getCustomTypeByName(const QString& name) const
}
const QString errorMessage = "Type not found: " + name;
+ qCritical(errorMessage.toUtf8().constData());
throw std::out_of_range(errorMessage.toStdString());
}
diff --git a/Source/Tome/Features/Types/Controller/typescontroller.h b/Source/Tome/Features/Types/Controller/typescontroller.h
index 9e5f523d..46c094a4 100644
--- a/Source/Tome/Features/Types/Controller/typescontroller.h
+++ b/Source/Tome/Features/Types/Controller/typescontroller.h
@@ -14,6 +14,8 @@ namespace Tome
TypesController();
void addCustomTypeSet(const CustomTypeSet& customTypeSet);
+
+ const CustomType addDerivedType(const QString& name, const QString& baseType, const QVariantMap& facets, const QString& customTypeSetName);
const CustomType addEnumeration(const QString& name, const QStringList& enumeration, const QString& customTypeSetName);
const CustomType addList(const QString& name, const QString& itemType, const QString& customTypeSetName);
const CustomType addMap(const QString& name, const QString& keyType, const QString& valueType, const QString& customTypeSetName);
@@ -40,12 +42,15 @@ namespace Tome
int indexOf(const CustomType& customType) const;
bool isBuiltInType(const QString& name) const;
bool isCustomType(const QString& name) const;
+ bool isReferenceType(const QString& name) const;
void moveCustomTypeToSet(const QString& customTypeName, const QString& customTypeSetName);
void removeCustomType(const QString& typeName);
void removeCustomTypeAt(const int index);
void removeCustomTypeSet(const QString& name);
void renameType(const QString oldName, const QString newName);
void setCustomTypes(CustomTypeSetList& model);
+
+ void updateDerivedType(const QString& oldName, const QString& newName, const QString& baseType, const QVariantMap facets, const QString& typeSetName);
void updateEnumeration(const QString& oldName, const QString& newName, const QStringList& enumeration, const QString& typeSetName);
void updateList(const QString& oldName, const QString& newName, const QString& itemType, const QString& typeSetName);
void updateMap(const QString& oldName, const QString& newName, const QString& keyType, const QString& valueType, const QString& typeSetName);
diff --git a/Source/Tome/Features/Types/Model/customtype.cpp b/Source/Tome/Features/Types/Model/customtype.cpp
index a6cc6d67..5d3a153c 100644
--- a/Source/Tome/Features/Types/Model/customtype.cpp
+++ b/Source/Tome/Features/Types/Model/customtype.cpp
@@ -3,16 +3,27 @@
using namespace Tome;
-const QString CustomType::RestrictionEnumeration = "Enumeration";
-const QString CustomType::RestrictionItemType = "ItemType";
-const QString CustomType::RestrictionKeyType = "KeyType";
-const QString CustomType::RestrictionValueType = "ValueType";
+const QString CustomType::FacetBaseType = "BaseType";
+const QString CustomType::FacetEnumeration = "Enumeration";
+const QString CustomType::FacetItemType = "ItemType";
+const QString CustomType::FacetKeyType = "KeyType";
+const QString CustomType::FacetValueType = "ValueType";
CustomType::CustomType()
{
}
+QString CustomType::getBaseType() const
+{
+ if (!this->isDerivedType())
+ {
+ return QString();
+ }
+
+ return this->fundamentalFacets[FacetBaseType].toString();
+}
+
QStringList CustomType::getEnumeration() const
{
if (!this->isEnumeration())
@@ -20,7 +31,7 @@ QStringList CustomType::getEnumeration() const
return QStringList();
}
- QString enumerationString = this->restrictions[RestrictionEnumeration];
+ QString enumerationString = this->fundamentalFacets[FacetEnumeration].toString();
if (enumerationString.isEmpty())
{
@@ -43,7 +54,7 @@ QString CustomType::getItemType() const
return QString();
}
- return this->restrictions[RestrictionItemType];
+ return this->fundamentalFacets[FacetItemType].toString();
}
QString CustomType::getKeyType() const
@@ -53,7 +64,7 @@ QString CustomType::getKeyType() const
return QString();
}
- return this->restrictions[RestrictionKeyType];
+ return this->fundamentalFacets[FacetKeyType].toString();
}
QString CustomType::getValueType() const
@@ -63,42 +74,52 @@ QString CustomType::getValueType() const
return QString();
}
- return this->restrictions[RestrictionValueType];
+ return this->fundamentalFacets[FacetValueType].toString();
+}
+
+bool CustomType::isDerivedType() const
+{
+ return this->fundamentalFacets.contains(FacetBaseType);
}
bool CustomType::isEnumeration() const
{
- return this->restrictions.contains(RestrictionEnumeration);
+ return this->fundamentalFacets.contains(FacetEnumeration);
}
bool CustomType::isList() const
{
- return this->restrictions.contains(RestrictionItemType);
+ return this->fundamentalFacets.contains(FacetItemType);
}
bool CustomType::isMap() const
{
- return this->restrictions.contains(RestrictionKeyType) &&
- this->restrictions.contains(RestrictionValueType);
+ return this->fundamentalFacets.contains(FacetKeyType) &&
+ this->fundamentalFacets.contains(FacetValueType);
+}
+
+void CustomType::setBaseType(const QString& baseType)
+{
+ this->fundamentalFacets[FacetBaseType] = baseType;
}
void CustomType::setEnumeration(const QStringList& enumeration)
{
QString enumerationString = enumeration.join(";");
- this->restrictions[RestrictionEnumeration] = enumerationString;
+ this->fundamentalFacets[FacetEnumeration] = enumerationString;
}
void CustomType::setItemType(const QString& itemType)
{
- this->restrictions[RestrictionItemType] = itemType;
+ this->fundamentalFacets[FacetItemType] = itemType;
}
void CustomType::setKeyType(const QString& keyType)
{
- this->restrictions[RestrictionKeyType] = keyType;
+ this->fundamentalFacets[FacetKeyType] = keyType;
}
void CustomType::setValueType(const QString& valueType)
{
- this->restrictions[RestrictionValueType] = valueType;
+ this->fundamentalFacets[FacetValueType] = valueType;
}
diff --git a/Source/Tome/Features/Types/Model/customtype.h b/Source/Tome/Features/Types/Model/customtype.h
index 3a4ae0af..ccc63c03 100644
--- a/Source/Tome/Features/Types/Model/customtype.h
+++ b/Source/Tome/Features/Types/Model/customtype.h
@@ -4,6 +4,7 @@
#include
#include
#include
+#include
namespace Tome
{
@@ -13,67 +14,81 @@ namespace Tome
CustomType();
QString name;
- QMap restrictions;
+ QVariantMap fundamentalFacets;
+ QVariantMap constrainingFacets;
QString typeSetName;
/**
- * @brief getEnumeration Convenience function for retrieving the Enumeration restriction of this type, if available. Returns an empty list, if not.
+ * @brief getBaseType Convenience function for retrieving the BaseType fundamental facet of this type, if available. Returns an empty string, if not.
+ * @return BaseType of this derived type, if available, and an empty string otherwise.
+ */
+ QString getBaseType() const;
+
+ /**
+ * @brief getEnumeration Convenience function for retrieving the Enumeration fundamental facet of this type, if available. Returns an empty list, if not.
* @return Enumeration of allowed field values, if available, and an empty list otherwise.
*/
QStringList getEnumeration() const;
/**
- * @brief getItemType Convenience function for retrieving the Item Type restriction of this type, if available. Returns an empty string, if not.
+ * @brief getItemType Convenience function for retrieving the Item Type fundamental facet of this type, if available. Returns an empty string, if not.
* @return Item Type of this list, if available, and an empty string otherwise.
*/
QString getItemType() const;
/**
- * @brief getKeyType Convenience function for retrieving the Key Type restriction of this type, if available. Returns an empty string, if not.
+ * @brief getKeyType Convenience function for retrieving the Key Type fundamental facet of this type, if available. Returns an empty string, if not.
* @return Key Type of this map, if available, and an empty string otherwise.
*/
QString getKeyType() const;
/**
- * @brief getKeyType Convenience function for retrieving the Value Type restriction of this type, if available. Returns an empty string, if not.
+ * @brief getKeyType Convenience function for retrieving the Value Type fundamental facet of this type, if available. Returns an empty string, if not.
* @return Value Type of this map, if available, and an empty string otherwise.
*/
QString getValueType() const;
-
+ bool isDerivedType() const;
bool isEnumeration() const;
bool isList() const;
bool isMap() const;
/**
- * @brief setEnumeration Sets the Enumeration restriction of this type.
+ * @brief setBaseType Sets the BaseType fundamental facet of this type.
+ * @param baseType Type the type is based on.
+ */
+ void setBaseType(const QString& baseType);
+
+ /**
+ * @brief setEnumeration Sets the Enumeration fundamental facet of this type.
* @param enumeration Allowed field values.
*/
void setEnumeration(const QStringList& enumeration);
/**
- * @brief setItemType Sets the Item Type restriction of this type.
+ * @brief setItemType Sets the Item Type fundamental facet of this type.
* @param itemType Type of the list items.
*/
void setItemType(const QString& itemType);
/**
- * @brief setItemType Sets the Key Type restriction of this type.
+ * @brief setItemType Sets the Key Type fundamental facet of this type.
* @param itemType Type of the map keys.
*/
void setKeyType(const QString& keyType);
/**
- * @brief setItemType Sets the Value Type restriction of this type.
+ * @brief setItemType Sets the Value Type fundamental facet of this type.
* @param itemType Type of the map values.
*/
void setValueType(const QString& valueType);
private:
- static const QString RestrictionEnumeration;
- static const QString RestrictionItemType;
- static const QString RestrictionKeyType;
- static const QString RestrictionValueType;
+ static const QString FacetBaseType;
+ static const QString FacetEnumeration;
+ static const QString FacetItemType;
+ static const QString FacetKeyType;
+ static const QString FacetValueType;
};
inline bool operator==(const CustomType& lhs, const CustomType& rhs){ return lhs.name == rhs.name; }
diff --git a/Source/Tome/Features/Types/View/customtypeswindow.cpp b/Source/Tome/Features/Types/View/customtypeswindow.cpp
index 3006a9a7..bdb7b4d9 100644
--- a/Source/Tome/Features/Types/View/customtypeswindow.cpp
+++ b/Source/Tome/Features/Types/View/customtypeswindow.cpp
@@ -1,12 +1,15 @@
#include "customtypeswindow.h"
#include "ui_customtypeswindow.h"
+#include "derivedtypewindow.h"
#include "enumerationwindow.h"
#include "listwindow.h"
#include "mapwindow.h"
#include "../Controller/typescontroller.h"
#include "../Model/builtintype.h"
+#include "../../Facets/Controller/facetscontroller.h"
#include "../../Fields/Controller/fielddefinitionscontroller.h"
+#include "../../Records/Controller/recordscontroller.h"
#include "../../Search/Controller/findusagescontroller.h"
#include "../../Types/Model/customtype.h"
#include "../../../Util/listutils.h"
@@ -14,12 +17,20 @@
using namespace Tome;
-CustomTypesWindow::CustomTypesWindow(TypesController& typesController, FieldDefinitionsController& fieldDefinitionsController, FindUsagesController& findUsagesController, QWidget *parent) :
+CustomTypesWindow::CustomTypesWindow(TypesController& typesController,
+ FacetsController& facetsController,
+ FieldDefinitionsController& fieldDefinitionsController,
+ FindUsagesController& findUsagesController,
+ RecordsController& recordsController,
+ QWidget *parent) :
QMainWindow(parent),
ui(new Ui::CustomTypesWindow),
typesController(typesController),
+ facetsController(facetsController),
fieldDefinitionsController(fieldDefinitionsController),
findUsagesController(findUsagesController),
+ recordsController(recordsController),
+ derivedTypeWindow(0),
enumerationWindow(0),
listWindow(0),
mapWindow(0)
@@ -27,9 +38,6 @@ CustomTypesWindow::CustomTypesWindow(TypesController& typesController, FieldDefi
ui->setupUi(this);
// Setup view.
- const CustomTypeList& types = this->typesController.getCustomTypes();
-
- this->ui->tableWidget->setRowCount(types.length());
this->ui->tableWidget->setColumnCount(3);
QStringList headers;
@@ -37,23 +45,61 @@ CustomTypesWindow::CustomTypesWindow(TypesController& typesController, FieldDefi
headers << tr("Type");
headers << tr("Details");
this->ui->tableWidget->setHorizontalHeaderLabels(headers);
-
- // Add all types.
- this->updateTable();
-
- // Enable sorting.
- this->ui->tableWidget->setSortingEnabled(true);
}
CustomTypesWindow::~CustomTypesWindow()
{
delete this->ui;
+ delete this->derivedTypeWindow;
delete this->enumerationWindow;
delete this->listWindow;
delete this->mapWindow;
}
+void CustomTypesWindow::showEvent(QShowEvent* event)
+{
+ Q_UNUSED(event)
+
+ // Custom types might have changed, i.e. by loading another project.
+ this->updateTable();
+
+ // Enable sorting.
+ this->ui->tableWidget->setSortingEnabled(true);
+}
+
+void CustomTypesWindow::on_actionNew_Derived_Type_triggered()
+{
+ // Show window.
+ if (!this->derivedTypeWindow)
+ {
+ this->derivedTypeWindow = new DerivedTypeWindow(
+ this->typesController,
+ this->facetsController,
+ this->recordsController,
+ this);
+ }
+
+ this->derivedTypeWindow->init();
+
+ int result = this->derivedTypeWindow->exec();
+
+ if (result == QDialog::Accepted)
+ {
+ // Update model.
+ CustomType newType =
+ this->typesController.addDerivedType(
+ this->derivedTypeWindow->getTypeName(),
+ this->derivedTypeWindow->getBaseType(),
+ this->derivedTypeWindow->getFacets(),
+ this->derivedTypeWindow->getTypeSetName());
+
+ // Update view.
+ this->ui->tableWidget->insertRow(0);
+ this->updateRow(0, newType);
+ }
+}
+
void CustomTypesWindow::on_actionNew_Custom_Type_triggered()
{
// Show window.
@@ -152,7 +198,11 @@ void CustomTypesWindow::on_actionEdit_Custom_Type_triggered()
const CustomType& type = this->typesController.getCustomType(typeName);
// Check type.
- if (type.isEnumeration())
+ if (type.isDerivedType())
+ {
+ this->editDerivedType(typeName, type);
+ }
+ else if (type.isEnumeration())
{
this->editEnumeration(typeName, type);
}
@@ -220,6 +270,41 @@ QString CustomTypesWindow::getSelectedTypeName() const
return this->ui->tableWidget->item(selectedIndex, 0)->data(Qt::DisplayRole).toString();
}
+void CustomTypesWindow::editDerivedType(QString typeName, const CustomType& type)
+{
+ // Show window.
+ if (!this->derivedTypeWindow)
+ {
+ this->derivedTypeWindow = new DerivedTypeWindow(
+ this->typesController,
+ this->facetsController,
+ this->recordsController,
+ this);
+ }
+
+ this->derivedTypeWindow->init();
+
+ // Update view.
+ this->derivedTypeWindow->setTypeName(type.name);
+ this->derivedTypeWindow->setBaseType(type.getBaseType());
+ this->derivedTypeWindow->setFacets(type.constrainingFacets);
+ this->derivedTypeWindow->setTypeSetNames(this->typesController.getCustomTypeSetNames());
+ this->derivedTypeWindow->setTypeSetName(type.typeSetName);
+
+ int result = this->derivedTypeWindow->exec();
+
+ if (result == QDialog::Accepted)
+ {
+ // Update type.
+ this->updateDerivedType(
+ typeName,
+ this->derivedTypeWindow->getTypeName(),
+ this->derivedTypeWindow->getBaseType(),
+ this->derivedTypeWindow->getFacets(),
+ this->derivedTypeWindow->getTypeSetName());
+ }
+}
+
void CustomTypesWindow::editEnumeration(QString typeName, const CustomType& type)
{
// Show window.
@@ -307,6 +392,16 @@ void CustomTypesWindow::editMap(QString typeName, const CustomType& type)
}
}
+void CustomTypesWindow::updateDerivedType(const QString& oldName, const QString& newName, const QString& baseType, const QVariantMap facets, const QString& typeSetName)
+{
+ // Update model.
+ this->fieldDefinitionsController.renameFieldType(oldName, newName);
+ this->typesController.updateDerivedType(oldName, newName, baseType, facets, typeSetName);
+
+ // Update view.
+ this->updateTable();
+}
+
void CustomTypesWindow::updateEnumeration(const QString& oldName, const QString& newName, const QStringList& enumeration, const QString& typeSetName)
{
// Update model.
@@ -345,7 +440,12 @@ void CustomTypesWindow::updateRow(const int index, const CustomType& type)
// Update view.
this->ui->tableWidget->setItem(index, 0, new QTableWidgetItem(type.name));
- if (type.isEnumeration())
+ if (type.isDerivedType())
+ {
+ this->ui->tableWidget->setItem(index, 1, new QTableWidgetItem("Derived Type"));
+ this->ui->tableWidget->setItem(index, 2, new QTableWidgetItem(type.getBaseType()));
+ }
+ else if (type.isEnumeration())
{
this->ui->tableWidget->setItem(index, 1, new QTableWidgetItem("Enumeration"));
this->ui->tableWidget->setItem(index, 2, new QTableWidgetItem(type.getEnumeration().join(", ")));
@@ -369,6 +469,11 @@ void CustomTypesWindow::updateTable()
{
const CustomTypeList& types = this->typesController.getCustomTypes();
+ if (this->ui->tableWidget->rowCount() != types.length())
+ {
+ this->ui->tableWidget->setRowCount(types.length());
+ }
+
for (int i = 0; i < types.length(); ++i)
{
this->updateRow(i, types[i]);
diff --git a/Source/Tome/Features/Types/View/customtypeswindow.h b/Source/Tome/Features/Types/View/customtypeswindow.h
index cde43f00..d937398b 100644
--- a/Source/Tome/Features/Types/View/customtypeswindow.h
+++ b/Source/Tome/Features/Types/View/customtypeswindow.h
@@ -3,6 +3,7 @@
#include
+class DerivedTypeWindow;
class EnumerationWindow;
class ListWindow;
class MapWindow;
@@ -14,8 +15,10 @@ namespace Ui {
namespace Tome
{
class CustomType;
+ class FacetsController;
class FieldDefinitionsController;
class FindUsagesController;
+ class RecordsController;
class TypesController;
}
@@ -24,10 +27,19 @@ class CustomTypesWindow : public QMainWindow
Q_OBJECT
public:
- explicit CustomTypesWindow(Tome::TypesController& typesController, Tome::FieldDefinitionsController& fieldDefinitionsController, Tome::FindUsagesController& findUsagesController, QWidget *parent = 0);
+ explicit CustomTypesWindow(Tome::TypesController& typesController,
+ Tome::FacetsController& facetsController,
+ Tome::FieldDefinitionsController& fieldDefinitionsController,
+ Tome::FindUsagesController& findUsagesController,
+ Tome::RecordsController& recordsController,
+ QWidget *parent = 0);
~CustomTypesWindow();
+ protected:
+ void showEvent(QShowEvent * event);
+
private slots:
+ void on_actionNew_Derived_Type_triggered();
void on_actionNew_Custom_Type_triggered();
void on_actionNew_List_triggered();
void on_actionNew_Map_triggered();
@@ -39,13 +51,16 @@ class CustomTypesWindow : public QMainWindow
void on_tableWidget_doubleClicked(const QModelIndex &index);
-
private:
Ui::CustomTypesWindow *ui;
+
Tome::TypesController& typesController;
+ Tome::FacetsController& facetsController;
Tome::FieldDefinitionsController& fieldDefinitionsController;
Tome::FindUsagesController& findUsagesController;
+ Tome::RecordsController& recordsController;
+ DerivedTypeWindow* derivedTypeWindow;
EnumerationWindow* enumerationWindow;
ListWindow* listWindow;
MapWindow* mapWindow;
@@ -53,10 +68,12 @@ class CustomTypesWindow : public QMainWindow
int getSelectedTypeIndex() const;
QString getSelectedTypeName() const;
+ void editDerivedType(QString typeName, const Tome::CustomType& type);
void editEnumeration(QString typeName, const Tome::CustomType& type);
void editList(QString typeName, const Tome::CustomType& type);
void editMap(QString typeName, const Tome::CustomType& type);
+ void updateDerivedType(const QString& oldName, const QString& newName, const QString& baseType, const QVariantMap facets, const QString& typeSetName);
void updateEnumeration(const QString& oldName, const QString& newName, const QStringList& enumeration, const QString& typeSetName);
void updateList(const QString& oldName, const QString& name, const QString& itemType, const QString& typeSetName);
void updateMap(const QString& oldName, const QString& newName, const QString& keyType, const QString& valueType, const QString& typeSetName);
diff --git a/Source/Tome/Features/Types/View/customtypeswindow.ui b/Source/Tome/Features/Types/View/customtypeswindow.ui
index a585dc91..4daef01a 100644
--- a/Source/Tome/Features/Types/View/customtypeswindow.ui
+++ b/Source/Tome/Features/Types/View/customtypeswindow.ui
@@ -49,6 +49,7 @@
Custom Types
+
@@ -118,7 +119,7 @@
-
+
:/Media/Icons/FindSymbol_6263.png:/Media/Icons/FindSymbol_6263.png
@@ -133,8 +134,15 @@
New Map...
+
+
+ New Derived Type...
+
+
-
+
+
+
actionClose
diff --git a/Source/Tome/Features/Types/View/derivedtypewindow.cpp b/Source/Tome/Features/Types/View/derivedtypewindow.cpp
new file mode 100644
index 00000000..effb82ac
--- /dev/null
+++ b/Source/Tome/Features/Types/View/derivedtypewindow.cpp
@@ -0,0 +1,190 @@
+#include "derivedtypewindow.h"
+#include "ui_derivedtypewindow.h"
+
+#include
+
+#include "../../Facets/Controller/facet.h"
+#include "../../Facets/Controller/facetscontroller.h"
+#include "../../Facets/Model/facetcontext.h"
+#include "../../Types/Controller/typescontroller.h"
+
+using namespace Tome;
+
+const int DerivedTypeWindow::FacetFormRow = 3;
+
+DerivedTypeWindow::DerivedTypeWindow(Tome::TypesController& typesController,
+ FacetsController& facetsController,
+ Tome::RecordsController& recordsController,
+ QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::DerivedTypeWindow),
+ typesController(typesController),
+ facetsController(facetsController),
+ recordsController(recordsController)
+{
+ ui->setupUi(this);
+}
+
+DerivedTypeWindow::~DerivedTypeWindow()
+{
+ delete ui;
+}
+
+QString DerivedTypeWindow::getBaseType() const
+{
+ return this->ui->comboBoxBaseType->currentText();
+}
+
+QVariantMap DerivedTypeWindow::getFacets() const
+{
+ QVariantMap facetMap;
+
+ // Get all facets registered for the current type.
+ QList typeFacets = this->facetsController.getFacets(this->getBaseType());
+
+ for (int i = 0; i < typeFacets.count(); ++i)
+ {
+ // Get facet.
+ Facet* facet = typeFacets[i];
+
+ // Get current facet value.
+ QString key = facet->getKey();
+ QVariant value = facet->getWidgetValue(this->facetWidgets[i]);
+
+ // Insert into facets map.
+ facetMap.insert(key, value);
+ }
+
+ return facetMap;
+}
+
+QString DerivedTypeWindow::getTypeName() const
+{
+ return this->ui->lineEditName->text();
+}
+
+QString DerivedTypeWindow::getTypeSetName() const
+{
+ return this->ui->comboBoxTypeSet->currentText();
+}
+
+void DerivedTypeWindow::init()
+{
+ // Set type names.
+ this->ui->comboBoxBaseType->clear();
+
+ const QStringList& typeNames = this->typesController.getBuiltInTypes();
+
+ for (int i = 0; i < typeNames.length(); ++i)
+ {
+ this->ui->comboBoxBaseType->addItem(typeNames[i]);
+ }
+
+ // Set type set names.
+ const QStringList typeSetNames = this->typesController.getCustomTypeSetNames();
+ this->setTypeSetNames(typeSetNames);
+ this->setTypeSetName(typeSetNames.first());
+}
+
+void DerivedTypeWindow::setBaseType(const QString& baseType) const
+{
+ this->ui->comboBoxBaseType->setCurrentText(baseType);
+}
+
+void DerivedTypeWindow::setFacets(const QVariantMap& facets)
+{
+ // Remove all existing facet widget from previous times this window was shown.
+ QFormLayout* layout = static_cast(this->layout());
+
+ while (!this->facetWidgets.empty())
+ {
+ QWidget* facetWidget = this->facetWidgets.takeAt(0);
+ QWidget* facetLabel = layout->labelForField(facetWidget);
+ layout->removeWidget(facetLabel);
+ layout->removeWidget(facetWidget);
+ delete facetLabel;
+ delete facetWidget;
+ }
+
+ // Add new facet widgets.
+ QList typeFacets = this->facetsController.getFacets(this->getBaseType());
+ FacetContext context = FacetContext(this->recordsController);
+
+ for (int i = 0; i < typeFacets.count(); ++i)
+ {
+ // Create facet widget and label.
+ Facet* facet = typeFacets[i];
+ QString facetDisplayName = facet->getDisplayName();
+ QWidget* facetWidget = facet->createWidget(context);
+
+ // Get current facet value from field definition.
+ QString facetKey = facet->getKey();
+ QVariant facetValue = facets.contains(facetKey) ? facets[facetKey] : facet->getDefaultValue();
+ facet->setWidgetValue(facetWidget, facetValue);
+
+ // Insert into layout and remember for later removal.
+ layout->insertRow(FacetFormRow + i, facetDisplayName + ":", facetWidget);
+ this->facetWidgets.push_back(facetWidget);
+ }
+}
+
+void DerivedTypeWindow::setTypeName(const QString& typeName)
+{
+ this->ui->lineEditName->setText(typeName);
+}
+
+void DerivedTypeWindow::setTypeSetName(const QString& typeSetName)
+{
+ this->ui->comboBoxTypeSet->setCurrentText(typeSetName);
+}
+
+void DerivedTypeWindow::setTypeSetNames(const QStringList& typeSetNames)
+{
+ this->ui->comboBoxTypeSet->clear();
+ this->ui->comboBoxTypeSet->addItems(typeSetNames);
+}
+
+void DerivedTypeWindow::accept()
+{
+ // Validate data.
+ if (this->validate())
+ {
+ this->done(Accepted);
+ }
+}
+
+void DerivedTypeWindow::on_comboBoxBaseType_currentIndexChanged(const QString &baseType)
+{
+ Q_UNUSED(baseType)
+
+ this->setFacets(QVariantMap());
+}
+
+bool DerivedTypeWindow::validate()
+{
+ // Name must not be empty.
+ if (this->getTypeName().isEmpty())
+ {
+ QMessageBox::information(
+ this,
+ tr("Missing data"),
+ tr("Please specify a name for the derived type."),
+ QMessageBox::Close,
+ QMessageBox::Close);
+ return false;
+ }
+
+ // Base type must not be empty.
+ if (this->getBaseType().isEmpty())
+ {
+ QMessageBox::information(
+ this,
+ tr("Missing data"),
+ tr("Please specify a base type for the derived type."),
+ QMessageBox::Close,
+ QMessageBox::Close);
+ return false;
+ }
+
+ return true;
+}
diff --git a/Source/Tome/Features/Types/View/derivedtypewindow.h b/Source/Tome/Features/Types/View/derivedtypewindow.h
new file mode 100644
index 00000000..8318b5ec
--- /dev/null
+++ b/Source/Tome/Features/Types/View/derivedtypewindow.h
@@ -0,0 +1,62 @@
+#ifndef DERIVEDTYPEWINDOW_H
+#define DERIVEDTYPEWINDOW_H
+
+#include
+
+namespace Ui {
+ class DerivedTypeWindow;
+}
+
+namespace Tome
+{
+ class TypesController;
+ class FacetsController;
+ class RecordsController;
+}
+
+class DerivedTypeWindow : public QDialog
+{
+ Q_OBJECT
+
+ public:
+ explicit DerivedTypeWindow(
+ Tome::TypesController& typesController,
+ Tome::FacetsController& facetsController,
+ Tome::RecordsController& recordsController,
+ QWidget *parent = 0);
+ ~DerivedTypeWindow();
+
+ QString getBaseType() const;
+ QVariantMap getFacets() const;
+ QString getTypeName() const;
+ QString getTypeSetName() const;
+
+ void init();
+
+ void setBaseType(const QString& baseType) const;
+ void setFacets(const QVariantMap& facets);
+ void setTypeName(const QString& typeName);
+ void setTypeSetName(const QString& typeSetName);
+ void setTypeSetNames(const QStringList& typeSetNames);
+
+ public slots:
+ void accept();
+
+ private slots:
+ void on_comboBoxBaseType_currentIndexChanged(const QString &arg1);
+
+ private:
+ static const int FacetFormRow;
+
+ Ui::DerivedTypeWindow *ui;
+
+ Tome::TypesController& typesController;
+ Tome::FacetsController& facetsController;
+ Tome::RecordsController& recordsController;
+
+ QList facetWidgets;
+
+ bool validate();
+};
+
+#endif // DERIVEDTYPEWINDOW_H
diff --git a/Source/Tome/Features/Types/View/derivedtypewindow.ui b/Source/Tome/Features/Types/View/derivedtypewindow.ui
new file mode 100644
index 00000000..fdb746a1
--- /dev/null
+++ b/Source/Tome/Features/Types/View/derivedtypewindow.ui
@@ -0,0 +1,94 @@
+
+
+ DerivedTypeWindow
+
+
+
+ 0
+ 0
+ 400
+ 119
+
+
+
+ Derived Type
+
+
+
-
+
+
+ Name:
+
+
+
+ -
+
+
+ Type Set:
+
+
+
+ -
+
+
+ Base Type:
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ DerivedTypeWindow
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ DerivedTypeWindow
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/Source/Tome/main.cpp b/Source/Tome/main.cpp
index f5286770..0b19c833 100644
--- a/Source/Tome/main.cpp
+++ b/Source/Tome/main.cpp
@@ -1,3 +1,10 @@
+#include
+#include
+
+#ifdef Q_OS_WIN
+#include
+#endif
+
#include "Core/commandlineoptions.h"
#include "Core/mainwindow.h"
#include "Core/controller.h"
@@ -6,7 +13,20 @@
#include "Util/pathutils.h"
-#include
+#ifdef Q_OS_WIN
+LONG WINAPI tomeUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *exceptionInfo)
+{
+ Q_UNUSED(exceptionInfo)
+
+ qCritical("CRASH!!!");
+
+ // Start issue reporter, if we're able to.
+ QProcess *process = new QProcess();
+ process->start("TomeIssueReporter.exe");
+
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+#endif
QCoreApplication* createApplication(Tome::CommandLineOptions* options)
{
@@ -22,6 +42,10 @@ QCoreApplication* createApplication(Tome::CommandLineOptions* options)
int main(int argc, char *argv[])
{
+#ifdef Q_OS_WIN
+ SetUnhandledExceptionFilter(tomeUnhandledExceptionFilter);
+#endif
+
// Parse command line options.
Tome::CommandLineOptions* options = new Tome::CommandLineOptions();
options->parse(argc, argv);
diff --git a/Source/TomeIssueReporter/config.h b/Source/TomeIssueReporter/config.h
new file mode 100644
index 00000000..f44c445c
--- /dev/null
+++ b/Source/TomeIssueReporter/config.h
@@ -0,0 +1,8 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#define GITHUB_USER "USERNAME"
+#define GITHUB_PASSWORD "PASSWORD"
+#define LOG_PATH "output.log"
+
+#endif // CONFIG_H
diff --git a/Source/TomeIssueReporter/main.cpp b/Source/TomeIssueReporter/main.cpp
new file mode 100644
index 00000000..b48f94ec
--- /dev/null
+++ b/Source/TomeIssueReporter/main.cpp
@@ -0,0 +1,11 @@
+#include "mainwindow.h"
+#include
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+ MainWindow w;
+ w.show();
+
+ return a.exec();
+}
diff --git a/Source/TomeIssueReporter/mainwindow.cpp b/Source/TomeIssueReporter/mainwindow.cpp
new file mode 100644
index 00000000..309df19e
--- /dev/null
+++ b/Source/TomeIssueReporter/mainwindow.cpp
@@ -0,0 +1,99 @@
+#include "mainwindow.h"
+#include "ui_mainwindow.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "config.h"
+
+
+MainWindow::MainWindow(QWidget *parent) :
+ QMainWindow(parent),
+ ui(new Ui::MainWindow)
+{
+ ui->setupUi(this);
+
+ // Setup network access.
+ this->networkAccessManager = new QNetworkAccessManager(this);
+
+ connect(this->networkAccessManager,
+ SIGNAL(finished(QNetworkReply*)),
+ this,
+ SLOT(onNetworkReply(QNetworkReply*)));
+}
+
+MainWindow::~MainWindow()
+{
+ delete this->networkAccessManager;
+
+ delete this->ui;
+}
+
+void MainWindow::on_buttonBox_accepted()
+{
+ // Get data.
+ const QString& title = this->ui->lineEditTitle->text();
+ QString description = this->ui->plainTextEditDescription->toPlainText();
+ const bool includeLog = this->ui->checkBoxIncludeLog->isChecked();
+
+ // Add log contents.
+ if (includeLog)
+ {
+ QFile logFile(LOG_PATH);
+
+ if (logFile.exists() && logFile.open(QIODevice::ReadOnly))
+ {
+ description += "\r\n";
+ description += "\r\n";
+ description += "output.log:\r\n";
+ description += "\r\n";
+ description += "```";
+ description += logFile.readAll();
+ description += "```";
+ }
+ }
+
+ qInfo(QString("Creating GitHub issue %1.").arg(title).toUtf8().constData());
+
+ // Build request JSON.
+ QJsonObject json;
+ json["title"] = title;
+ json["body"] = description;
+
+ QJsonDocument jsonDocument(json);
+ QByteArray jsonBytes = jsonDocument.toJson();
+
+ // Create request.
+ QUrl serviceURL("https://github.com/gitapi/repos/npruehs/tome-editor/issues");
+ QNetworkRequest request(serviceURL);
+
+ QByteArray authBytes;
+ authBytes.append(GITHUB_USER);
+ authBytes.append(":");
+ authBytes.append(GITHUB_PASSWORD);
+ QByteArray authBytesBase64 = authBytes.toBase64();
+ QString auth = "Basic " + QString(authBytesBase64);
+
+ request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
+ request.setHeader(QNetworkRequest::ContentLengthHeader, jsonBytes.size());
+ request.setRawHeader(QString("Accept").toUtf8(), QString("application/vnd.github.v3+json").toUtf8());
+ request.setRawHeader(QString("Authorization").toUtf8(), auth.toUtf8());
+
+ this->networkAccessManager->post(request, jsonBytes);
+}
+
+void MainWindow::on_buttonBox_rejected()
+{
+ this->close();
+}
+
+void MainWindow::onNetworkReply(QNetworkReply* reply)
+{
+ Q_UNUSED(reply)
+
+ this->close();
+}
diff --git a/Source/TomeIssueReporter/mainwindow.h b/Source/TomeIssueReporter/mainwindow.h
new file mode 100644
index 00000000..3e818076
--- /dev/null
+++ b/Source/TomeIssueReporter/mainwindow.h
@@ -0,0 +1,32 @@
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include
+#include
+#include
+
+namespace Ui {
+ class MainWindow;
+}
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+ public:
+ explicit MainWindow(QWidget *parent = 0);
+ ~MainWindow();
+
+ private slots:
+ void on_buttonBox_accepted();
+ void on_buttonBox_rejected();
+
+ void onNetworkReply(QNetworkReply* reply);
+
+ private:
+ Ui::MainWindow *ui;
+
+ QNetworkAccessManager* networkAccessManager;
+};
+
+#endif // MAINWINDOW_H
diff --git a/Source/TomeIssueReporter/mainwindow.ui b/Source/TomeIssueReporter/mainwindow.ui
new file mode 100644
index 00000000..556e457c
--- /dev/null
+++ b/Source/TomeIssueReporter/mainwindow.ui
@@ -0,0 +1,72 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 600
+ 400
+
+
+
+ Tome Issue Reporter
+
+
+
+ -
+
+
+ <html><head/><body><p>We're sincerely sorry you're having issues with Tome. We are.</p><p>You can help us to prevent that in future releases by submitting an issue report for us. Please provide as much information as possible, including what happened and what you expected to happen.</p><p><span style=" font-weight:600;">That issue will be publicly visible at https://github.com/npruehs/tome-editor/issues.</span><br/></p></body></html>
+
+
+ true
+
+
+
+ -
+
+
+ -
+
+
+ Title
+
+
+
+ -
+
+
+ Description
+
+
+
+ -
+
+
+ false
+
+
+
+ -
+
+
+ Upload log file to public issue tracker
+
+
+
+ -
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+
+
diff --git a/chocolateyinstall.ps1 b/chocolateyinstall.ps1
index bf3015eb..61ecdfa3 100644
--- a/chocolateyinstall.ps1
+++ b/chocolateyinstall.ps1
@@ -5,8 +5,8 @@ $packageName= 'tome-editor'
$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
$exampleDir = "$(Join-Path -Path $toolsDir -ChildPath ..\example)"
-$url = 'https://github.com/npruehs/tome-editor/releases/download/0.5/Tome-0.5-Windows-x86.zip'
-$urlExample= 'https://github.com/npruehs/tome-editor/releases/download/0.5/Tome-0.5-ExampleProject.zip'
+$url = 'https://github.com/npruehs/tome-editor/releases/download/0.6/Tome-0.6-Windows-x86.zip'
+$urlExample= 'https://github.com/npruehs/tome-editor/releases/download/0.6/Tome-0.6-ExampleProject.zip'
# Install Tome.
Install-ChocolateyZipPackage $packageName $url $toolsDir
diff --git a/tome-editor.nuspec b/tome-editor.nuspec
index 9f48d338..57c8c335 100644
--- a/tome-editor.nuspec
+++ b/tome-editor.nuspec
@@ -26,7 +26,7 @@ This is a nuspec. It mostly adheres to https://docs.nuget.org/create/Nuspec-Refe
- 0.5-alpha
+ 0.6-alpha
https://github.com/npruehs/tome-editor
Nick Prühs
@@ -55,49 +55,53 @@ Developing games is all about data. With game systems for modifying data and use
Tome solves two problems that arise with every new game you build: First, you can import the definition of your game's data so game designers can create new data records and modify existing ones very conveniently. Second, you can export the results to any format your game engine understands.
-Usability release.
+Advanced export release.
-With Tome starting to be used in production, this release focuses on bringing a lot of usability improvements rather than many new featues.
+This release adds a lot of customizability for exporting data, along with automatically enforced field value constraints and project and user settings.
## New Features
-* **Maps and Vectors.** Custom [key-value maps](https://github.com/npruehs/tome-editor/wiki/Custom-Data-Types#adding-maps) and two- and three-dimensional vectors with integer and floating-point numbers.
-* **Find Usages.** Added [finding usages](https://github.com/npruehs/tome-editor/wiki/Search) of custom types, fields and records in your project.
-* **Command Line Support.** Can start Tome [without showing a window](https://github.com/npruehs/tome-editor/wiki/Command-Line-Support) and run data exports now.
+* **Field Facets.** Specify [custom constraints](https://github.com/npruehs/tome-editor/wiki/Field-Definitions#field-facets) such as the minimum value of an integer, the maximum length of a string, or the required parent of a record reference.
+* **Project Overview.** [Add new or existing files](https://github.com/npruehs/tome-editor/wiki/Project-Structure#addingremoving-project-data-files) to and/or remove files from the project. Specify which file to add new data to and move data between files.
+* **Search.** [Find records](https://github.com/npruehs/tome-editor/wiki/Search#finding-records) by id or display name.
+* **User Settings.** Specify whether to show a Description column in the records table or to run integrity checks on save.
## Data
-* Added new integrity check for unsupported list and map item types.
-* Records without any fields are no longer exported. Consequently, records whose parents don't have any fields will have their parent id reset for the export.
-* Increased decimals for real fields to 4 (were 3).
-* Allowing to specify whether to export roots, inner nodes and/or leafs of the record tree.
-* Adding a new field to an existing component will now automatically cause that field to be added to all records who already have all exisiting fields of that component.
-* Moving a field to a different component will now automatically remove that field from all records who have all other fields of that component.
+* Added placeholders for list item types and map key and value types in export templates.
+* Added placeholders for specific field values in export templates.
+* Added possibility for ignoring specific records and/or fields in exports.
+* Using $ characters in record names is now prohibited.
+
+See https://github.com/npruehs/tome-editor/wiki/Exporting-Data for details.
+
+* Added read-only records that can't be edited, reverted, removed or reparented.
+
+See https://github.com/npruehs/tome-editor/wiki/Data-Records#read-only-records for details.
## Usability
-* Initially focusing display name field in field definition and record windows.
-* Initially focusing New Enumeration Member window text box.
-* Clearing Enumeration window when creating a new enumeration type, instead of showing contents of last edited enumeration.
-* Moved field definition type above default value in Field Definition window.
-* Set OK button as default in field value window, so users can immediately confirm their changes with Enter.
-* In the record window, users can add all fields of a component by either checking a field or a component checkbox.
-* Added description column to record field table and removed the tooltip.
-* Added record icons, differentiating between records with and without fields.
-* Added sorting field definitions, custom types and errors by arbitrary columns.
-* Added possibility to revert whole records, effectively reverting all of their fields.
-* Added possibility to duplicate records, creating a new record with the same parent and field values.
-* After manually changing a record id, the record id text is no longer linked to the display name text.
+* Initially selecting all content of Integer, Real and String controls for immediate editing.
+* Added hyperlinks to search results and references to other records.
-## Bug Fixes
+## Other
-* Fixed a bug that caused export templates to create superfluous delimiters in the output files.
-* Fixed a bug that prevented users from specifying negative real numbers.
-* Fixed a bug that caused some components and table values of inherited fields to omitted when exporting records.
+* Moved components, custom types and export templates from project file to dedicated files.
+* Added link to Roadmap to Help menu.
+* Added application icon.
-## Upgrading to Tome 0.5
+## Bug Fixes
-With map support added to Tome, export templates require three new template files for exporting maps. See the [Exporting Data](https://github.com/npruehs/tome-editor/wiki/Exporting-Data#export-map-template-texportm) manual page for further details.
+* Fixed a crash that could occur when removing a field while a record inheriting that field is selected.
+* Fixed a bug that caused the field definitions window not to correctly reflect all changes if any components had been removed since the last time the window was shown.
+* Fixed a bug that caused UTF-8 text not to be correctly exported.
+* Fixed a bug that allowed users to create enumerations without specifying a name.
+* Fixed a bug that caused enumerations without any members to appear as if they had a single empty member.
+* Fixed a bug that caused field values in the record table to overlap with column boundaries.
+* Fixed a bug that caused field values in the record table to overlap with color previews.
+* Fixed a bug that occasionally caused the last column of the record not to fill all available space.
+* Fixed a bug that caused huge empty areas in field value windows with small content (e.g. checkbox) after having shown a window with large content (e.g. color picker) before.
+* Fixed a bug that caused sorting field definitions by any column other than id to occasionally make the table show duplicate rows.