diff mde/content/Content.d @ 137:9f035cd139c6

BIG commit. Major change: old Options class is gone, all content values are loaded and saved automatically. All options updated to reflect this, some changed. Content restrutured a lot: New IContent module, Content module includes more functionality. New ContentLoader module to manage content loading/saving/translation. Translation module moved to content dir and cut down to reflect current usage. File format unchanged except renames: FontOptions -> Font, VideoOptions -> Screen. Font render mode and LCD filter options are now enums. GUI loading needs to create content (and set type for enums), but doesn't save/load value. Some setup of mainSchedule moved to mde.mainLoop. Content callbacks are called on content change now. ContentLists are set up implicitly from content symbols. Not as fast but much easier! Bug-fix in the new MTTagReader. Renamed MT *Reader maker functions to avoid confusion in paths.d. New mde.setup.logger module to allow logger setup before any other module's static this().
author Diggory Hardy <diggory.hardy@gmail.com>
date Sat, 07 Feb 2009 12:46:03 +0000
parents c9843fbaac88
children 2476790223b8
line wrap: on
line diff
--- a/mde/content/Content.d	Sun Feb 01 12:36:21 2009 +0000
+++ b/mde/content/Content.d	Sat Feb 07 12:46:03 2009 +0000
@@ -13,50 +13,40 @@
 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
-/*************************************************************************************************
+/******************************************************************************
  * The content system − interfaces and base Content class.
- *************************************************************************************************/
+ *****************************************************************************/
 module mde.content.Content;
 
-import util = mde.util;
+public import mde.content.IContent;
+import mde.content.Translation;	// loading strings
+import mde.content.ValueCache;	// saving/loading content values
+import util = mde.util;		// fp -> dlg
+import mde.exception;
+import mde.setup.logger;
 
-debug {
     import tango.util.log.Log : Log, Logger;
     private Logger logger;
     static this () {
         logger = Log.getLogger ("mde.gui.content.Content");
     }
-}
-
-/** IContent − interface for all Content classes.
- *
- * Very little code uses IContent (except for passing opaquely). */
-interface IContent
-{
-    /** Generically return strings.
-    *
-    * This serves two purposes: generically returning a string of/related to the content (i == 0),
-    * and returning associated descriptors. Functions should adhere to (or add to) this table.
-    *
-    *  $(TABLE
-    *  $(TR $(TH i) $(TH returns))
-    *  $(TR $(TD 0) $(TD value))
-    *  $(TR $(TD 1) $(TD Translated name or null))
-    *  $(TR $(TD 2) $(TD Translated description or null))
-    *  $(TR $(TD other) $(TD null))
-    *  ) */
-    char[] toString (uint i);
-}
 
 /** Content lists. Impemented by EnumContent as well as ContentList. */
 interface IContentList : IContent
 {
     /** Return all sub-contents. */
     Content[] list ();
+    
+    /** Append x to the content list.
+     *
+     * Done automatically in creation, from symbol name, so don't call
+     * externally. */
+    void append (Content x);
 }
 
 
-/** The base for most or all content classes.
+/******************************************************************************
+ * The base for most or all content classes.
  *
  * Includes generic callback support, toString implementation and symbol access.
  * 
@@ -66,20 +56,56 @@
  *  void opAssign (T val);	// assign val, calling callbacks
  *  T opCall ();		// return value
  *  alias opCall opCast;
- * --- */
+ *  void endEvent ();		// extend to set new value in changed (if Content has a value)
+ * ---
+ *****************************************************************************/
 class Content : IContent
 {
-    this (char[] symbol) {
-	this.symbol = symbol;
-	name_ = symbol;		// provide a temporary name
+    protected this (char[] symbol) {
+        //debug logger.trace ("Creating a {}: {}", this, symbol);
+        if (symbol in allContent)
+            throw new ContentException ("Multiple content with symbol "~ symbol);
+        allContent[symbol] = this;
+        this.symbol = symbol;
+        
+        // Name:
+        auto l10nP = symbol in translations;
+	if (l10nP)
+            name (*l10nP);
+        else
+            name_ = symbol;	// provide a temporary name
+        
+        // Add to a parent list:
+	ptrdiff_t i = symbol.length-1;
+	while (i >= 0 && symbol[i] != '.')
+	    --i;
+        IContentList parent;
+        if (i <= 0) {
+            if (symbol.length == 0)
+            	return;		// special case: is tree (content tree root)
+            parent = tree;	// otherwise, use tree as root
+        } else {
+            char[] parentSym = symbol[0..i];
+            Content* parObj = parentSym in allContent;
+            if (parObj) {	// existing object
+            	parent = cast(IContentList) *parObj;
+            	if (parent is null) {	// it's not an IContentList!
+                    logger.warn ("{} is not an IContentList, but a child, {}, exists!", parentSym, symbol);
+                    return;
+            	}
+            } else		// no existing object
+            	parent = new ContentList (parentSym);
+        }
+        parent.append (this);
     }
     
-    void name (char[] name, char[] desc = null) {
-	name_ = name;
-	desc_ = desc;
+    void name (Translation.Entry e) {
+	name_ = e.name;
+	desc_ = e.desc;
     }
-    
-    /** Add a callback. Callbacks are called in the order added. */
+   
+    /** Add a callback. Callbacks are called on a change or event, in the order
+     * added. */
     Content addCallback (void delegate (Content) cb) {
 	this.cb ~= cb;
 	return this;
@@ -97,14 +123,86 @@
 	: null;
     }
     
-    /// End of an event, e.g. a button release or end of an edit (calls callbacks).
+    /** End of an event, e.g. a button release or end of an edit (calls callbacks).
+     *
+     * Content holding a value should override this, setting its new value in
+     * changed as well as calling callbacks, e.g.:
+     * ---
+     * Content.changed.boolData[symbol] = val;
+     * super();
+     * --- */
     void endEvent () {
 	foreach (dg; cb)
 	    dg (this);
     }
     
-    final char[] symbol;	// Symbol name for this content
 protected:
-    char[] name_, desc_;	// name and description
+    char[] symbol;
+    char[] name_, desc_;	// translated name and description
     void delegate (Content) cb[];
+    
+public static:
+    /** Get Content with _symbol name symbol from the list of all content, or
+     * null if no such Content exists. */
+    Content get (char[] symbol) {
+        auto p = symbol in allContent;
+        if (p)
+            return *p;
+        logger.warn ("Content {} does not exist",symbol);
+        return null;
+    }
+    
+    // Would use tango.container.HashMap, but mde.workaround2371 doesn't work here.
+    Content[char[]] allContent;	// all content hashed by symbol name
+    ContentList tree;	// tree of all content.
+    
+    static this () {
+        tree = new ContentList ("");
+    }
+    
+package static:
+    // Kept to set the value of other content as it is created:
+    ValueCache loaded;	// loaded values not matching any content created yet
+    // The values in changed are what is written to file when saving:
+    ValueCache changed;	// changed values and values from local config file
+    
+    Translation.Entry[char[]] translations;
 }
+
+/** A generic way to handle a list of type IContent. */
+class ContentList : Content, IContentList
+{
+    this (char[] symbol) {
+	super (symbol);
+    }
+    
+    override Content[] list () {
+	return list_;
+    }
+    
+    override void append (Content x) {
+        list_ ~= x;
+    }
+    
+protected:
+    final Content[] list_;
+}
+
+/** Created on errors to display and log a message. */
+class ErrorContent : Content
+{
+    this (char[] symbol, char[] msg) {
+	super (symbol);
+        this.msg = msg;
+        logger.error (symbol ~ ": " ~ msg);
+    }
+    
+    override char[] toString (uint i) {
+	return i == 0 ? msg
+	     : i == 1 ? name_
+	     : null;
+    }
+    
+protected:
+    char[] msg;
+}