changeset 101:71f0f1f83620

Some path adjustments for windows (untested) and fonts. All types of option can be edited. paths: support for getting the full path for a font when just the file name is entered, in order to unify usage on windows and linux. paths: Used getSpecialPath for some windows paths; needs testing. Content: Moved line-editing code to abstract ValueContent class and added some conversion functions, so that any type of ValueContent can be edited as text.
author Diggory Hardy <diggory.hardy@gmail.com>
date Sun, 16 Nov 2008 17:03:47 +0000
parents 0ea4a3e651ae
children ba035eba07b4
files data/conf/gui.mtt data/conf/options.mtt mde/font/font.d mde/gui/content/Content.d mde/gui/widget/createWidget.d mde/gui/widget/miscContent.d mde/gui/widget/textContent.d mde/lookup/Options.d mde/scheduler/Scheduler.d mde/setup/Init.d mde/setup/paths.d
diffstat 11 files changed, 198 insertions(+), 135 deletions(-) [+]
line wrap: on
line diff
--- a/data/conf/gui.mtt	Sat Nov 15 17:39:14 2008 +0000
+++ b/data/conf/gui.mtt	Sun Nov 16 17:03:47 2008 +0000
@@ -14,7 +14,6 @@
 <WidgetData|optDesc={0:[0x4020, 2, 0xaf6000]}>
 <WidgetData|optVal={0:[0x6030]}>
 <WidgetData|optSep={0:[0x21, 0xff],1:["="]}>
-<WidgetData|floating={0:[0x8200,13,6,14],1:["text","button","blank"]}>
-<WidgetData|text={0:[0x4032]}>
+<WidgetData|floating={0:[0x8200,6,14],1:["button","blank"]}>
 {Basic}
 <WidgetData|root={0:[0x21,0x90D970],1:["A string!"]}>
--- a/data/conf/options.mtt	Sat Nov 15 17:39:14 2008 +0000
+++ b/data/conf/options.mtt	Sun Nov 16 17:03:47 2008 +0000
@@ -10,8 +10,8 @@
 {FontOptions}
 <int|lcdFilter=2>
 <int|renderMode=0x30000>
-<char[]|defaultFont="/usr/share/fonts/X11/Type1/n019003l.pfb">
-!<char[]|defaultFont="/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf">
+!<char[]|defaultFont="n019003l.pfb">
+<char[]|defaultFont="DejaVuSans.ttf">
 <int|defaultSize=16>
 
 {VideoOptions}
--- a/mde/font/font.d	Sat Nov 15 17:39:14 2008 +0000
+++ b/mde/font/font.d	Sun Nov 16 17:03:47 2008 +0000
@@ -22,6 +22,7 @@
 import mde.font.exception;
 
 import mde.setup.exception;     // InitStage stuff
+import mde.setup.paths;
 
 import derelict.freetype.ft;
 
@@ -125,16 +126,16 @@
      * Even if the same font is used at multiple sizes, multiple copies of FT_Face are used.
      * Sharing an FT_Face would require calling FT_Set_Pixel_Sizes each time a glyph is rendered or
      * swapping the size information (face.size)? */
-    this (char[] path, int size)
+    this (char[] file, int size)
     in {
         assert (library !is null, "font: library is null");
     } body {
-        if (FT_New_Face (library, toStringz(path), 0, &face))
-            throw new fontLoadException ("Unable to read font: "~path);
+	if (FT_New_Face (library, getFontPath (file).ptr, 0, &face))
+            throw new fontLoadException ("Unable to read font: "~file[0..$-1]);
         
         if (!FT_IS_SCALABLE (face))
             throw new fontLoadException ("Currently no support for non-scalable fonts (which " ~
-                    path ~ " is). Please report if you want to see support.");
+                    file[0..$-1] ~ " is). Please report if you want to see support.");
         /* The following will need to be addressed when adding support for non-scalables:
          *	Use of face.size.metrics.height property.
          */
--- a/mde/gui/content/Content.d	Sat Nov 15 17:39:14 2008 +0000
+++ b/mde/gui/content/Content.d	Sun Nov 16 17:03:47 2008 +0000
@@ -91,7 +91,75 @@
         name_ = n;
         desc_ = d;
     }
+    
+    /// Get the text.
+    char[] toString (uint i) {
+        return (i == 0) ? sv
+            : (i == 1) ? name_
+            : (i == 2) ? desc_
+            : null;
+    }
+    
+    /** Acts on a keystroke and returns the new value.
+     *
+     * Supports one-line editing: left/right, home/end, backspace/delete. */
+    char[] keyStroke (ushort sym, char[] i) {
+	debug assert (i.length, "TextContent.keyStroke: no value (??)");	// impossible?
+	char k = *i;
+	if (k > 0x20) {
+	    if (k == 0x7f) {		// delete
+		size_t p = pos;
+		if (p < sv.length) ++p;
+		while (p < sv.length && (sv[p] & 0x80) && !(sv[p] & 0x40))
+		    ++p;
+		sv = sv[0..pos] ~ sv[p..$];
+	    } else {			// insert character
+		char[] tail = sv[pos..$];
+		sv.length = sv.length + i.length;
+		size_t npos = pos+i.length;
+		if (tail) sv[npos..$] = tail.dup;	// cannot assign with overlapping ranges
+		    sv[pos..npos] = i;
+		pos = npos;
+	    }
+	} else {			// use sym; many keys output 0
+	    if (sym == SDLK_BACKSPACE) {	// backspace; k == 0x8
+		char[] tail = sv[pos..$];
+		if (pos) --pos;
+		while (pos && (sv[pos] & 0x80) && !(sv[pos] & 0x40))
+		    --pos;
+		sv = sv[0..pos] ~ tail;
+	    } else if (sym == SDLK_LEFT) {
+		if (pos) --pos;
+		while (pos && (sv[pos] & 0x80) && !(sv[pos] & 0x40))
+		    --pos;
+	    } else if (sym == SDLK_RIGHT) {
+		if (pos < sv.length) ++pos;
+		while (pos < sv.length && (sv[pos] & 0x80) && !(sv[pos] & 0x40))
+		    ++pos;
+	    } else if (sym == SDLK_HOME || sym == SDLK_UP) {
+		pos = 0;
+	    } else if (sym == SDLK_END || sym == SDLK_DOWN) {
+		pos = sv.length;
+	    } else
+		debug logger.trace ("Symbol: {}", sym);
+	}
+	return sv;
+    }
+    
+    size_t getEditIndex () {
+	size_t i = 0;
+	for (size_t p = 0; p < pos; ++p)
+	    if (!(sv[p] & 0x80) || sv[p] & 0x40)
+		++i;
+	return i;
+    }
+    
+    /// Call at the end of an edit to convert sv to v and call callbacks
+    void endEdit ();
 protected:
+    char[] sv;		// string of value; updated on assignment for displaying and editing
+    size_t pos;		// editing position; used by keyStroke
+    char[] symb;
     char[] name_, desc_;// name and description, loaded by lookup.Translation
 }
 
@@ -113,7 +181,7 @@
     /** Create a content with _symbol name symbol. */
     this (char[] symbol, bool val = false) {
         symb = symbol;
-        v = val;
+	assignNoCB (val);
     }
     
     /** Adds cb to the list of callback functions called when the value is changed. Returns this. */
@@ -122,17 +190,13 @@
         return this;
     }
     
-    /// Get the text.
-    char[] toString (uint i) {
-        return (i == 0) ? v ? "true" : "false"
-             : (i == 1) ? name_
-             : (i == 2) ? desc_
-             : null;
+    void assignNoCB (bool val) {
+	v = val;
+	sv = v ? "true" : "false";
     }
-    
     void opAssign (bool val) {
-        v = val;
-        foreach (cb; cngCb)
+	assignNoCB (val);
+	foreach (cb; cngCb)
             cb(symb, val);
     }
     bool opCall () {
@@ -140,9 +204,14 @@
     }
     alias opCall opCast;
     
-    bool v;     //FIXME: should be protected but Options needs to set without calling callbacks
+    void endEdit () {
+	v = sv && (sv[0] == 't' || sv[0] == 'T' || sv[0] == '1');
+	foreach (cb; cngCb)
+	    cb(symb, v);
+    }
+    
 protected:
-    char[] symb;
+    bool v;
     void delegate (char[],bool)[] cngCb;       // change callbacks
 }
 
@@ -152,7 +221,6 @@
     this (char[] symbol, char[] val = null) {
         symb = symbol;
         v = val;
-	pos = v.length;
     }
     
     /** Adds cb to the list of callback functions called when the value is changed. Returns this. */
@@ -161,14 +229,9 @@
         return this;
     }
     
-    /// Get the text.
-    char[] toString (uint i) {
-        return (i == 0) ? v
-            : (i == 1) ? name_
-            : (i == 2) ? desc_
-            : null;
+    void assignNoCB (char[] val) {
+	v = val;
     }
-    
     void opAssign (char[] val) {
         v = val;
         foreach (cb; cngCb)
@@ -179,70 +242,13 @@
     }
     alias opCall opCast;
     
-    /** Acts on a keystroke and returns the new value.
-     *
-     * Supports one-line editing: left/right, home/end, backspace/delete. */
-    char[] keyStroke (ushort sym, char[] i) {
-	debug assert (i.length, "TextContent.keyStroke: no value (??)");	// impossible?
-	char k = *i;
-	if (k > 0x20) {
-	    if (k == 0x7f) {		// delete
-		size_t p = pos;
-		if (p < v.length) ++p;
-		while (p < v.length && (v[p] & 0x80) && !(v[p] & 0x40))
-		    ++p;
-		v = v[0..pos] ~ v[p..$];
-	    } else {			// insert character
-		char[] tail = v[pos..$];
-		v.length = v.length + i.length;
-		size_t npos = pos+i.length;
-		if (tail) v[npos..$] = tail.dup;	// cannot assign with overlapping ranges
-		    v[pos..npos] = i;
-		pos = npos;
-	    }
-	} else {			// use sym; many keys output 0
-	    if (sym == SDLK_BACKSPACE) {	// backspace; k == 0x8
-		char[] tail = v[pos..$];
-		if (pos) --pos;
-		while (pos && (v[pos] & 0x80) && !(v[pos] & 0x40))
-		    --pos;
-		v = v[0..pos] ~ tail;
-	    } else if (sym == SDLK_LEFT) {
-		if (pos) --pos;
-		while (pos && (v[pos] & 0x80) && !(v[pos] & 0x40))
-		    --pos;
-	    } else if (sym == SDLK_RIGHT) {
-		if (pos < v.length) ++pos;
-		while (pos < v.length && (v[pos] & 0x80) && !(v[pos] & 0x40))
-		    ++pos;
-	    } else if (sym == SDLK_HOME || sym == SDLK_UP) {
-		pos = 0;
-	    } else if (sym == SDLK_END || sym == SDLK_DOWN) {
-		pos = v.length;
-	    } else
-		debug logger.trace ("Symbol: {}", sym);
-	}
-	return v;
-    }
-    
-    size_t getEditIndex () {
-	size_t i = 0;
-	for (size_t p = 0; p < pos; ++p)
-	    if (!(v[p] & 0x80) || v[p] & 0x40)
-		++i;
-	return i;
-    }
-    
-    /// Gives all callbacks the modified value
     void endEdit () {
 	foreach (cb; cngCb)
 	    cb(symb, v);
     }
     
-    char[] v;
 protected:
-    char[] symb;
-    size_t pos;		// editing position; used by keyStroke
+    alias sv v;		// don't need separate v and sv in this case
     void delegate (char[],char[])[] cngCb;
 }
 
@@ -252,7 +258,7 @@
     /** Create a content with _symbol name symbol. */
     this (char[] symbol, int val = 0) {
         symb = symbol;
-        v = val;
+	assignNoCB (val);
     }
     
     /** Adds cb to the list of callback functions called when the value is changed. Returns this. */
@@ -261,17 +267,13 @@
         return this;
     }
     
-    /// Get the text.
-    char[] toString (uint i) {
-        return (i == 0) ? Int.toString (v)
-        : (i == 1) ? name_
-        : (i == 2) ? desc_
-        : null;
+    void assignNoCB (int val) {
+	v = val;
+	sv = Int.toString (v);
     }
-    
     void opAssign (int val) {
-        v = val;
-        foreach (cb; cngCb)
+	assignNoCB (val);
+	foreach (cb; cngCb)
             cb(symb, val);
     }
     int opCall () {
@@ -279,9 +281,14 @@
     }
     alias opCall opCast;
     
-    int v;
+    void endEdit () {
+	v = Int.toInt (sv);
+	foreach (cb; cngCb)
+	    cb(symb, v);
+    }
+    
 protected:
-    char[] symb;
+    int v;
     void delegate (char[],int)[] cngCb;
 }
 
@@ -291,7 +298,7 @@
     /** Create a content with _symbol name symbol. */
     this (char[] symbol, double val = 0) {
         symb = symbol;
-        v = val;
+	assignNoCB (val);
     }
     
     /** Adds cb to the list of callback functions called when the value is changed. Returns this. */
@@ -300,17 +307,13 @@
         return this;
     }
     
-    /// Get the text.
-    char[] toString (uint i) {
-        return (i == 0) ? Float.toString (v)
-        : (i == 1) ? name_
-        : (i == 2) ? desc_
-        : null;
+    void assignNoCB (double val) {
+	v = val;
+	sv = Float.toString (v);
     }
-    
     void opAssign (double val) {
-        v = val;
-        foreach (cb; cngCb)
+	assignNoCB (val);
+	foreach (cb; cngCb)
             cb(symb, val);
     }
     double opCall () {
@@ -318,8 +321,13 @@
     }
     alias opCall opCast;
     
-    double v;
+    void endEdit () {
+	v = Float.toFloat (sv);
+	foreach (cb; cngCb)
+	    cb(symb, v);
+    }
+    
 protected:
-    char[] symb;
+    double v;
     void delegate (char[],double)[] cngCb;
 }
--- a/mde/gui/widget/createWidget.d	Sat Nov 15 17:39:14 2008 +0000
+++ b/mde/gui/widget/createWidget.d	Sun Nov 16 17:03:47 2008 +0000
@@ -106,7 +106,7 @@
     editContent		= FUNCTION | TAKES_CONTENT | 0x30,
     DisplayContent	= TAKES_CONTENT | 0x30,
     BoolContent		= TAKES_CONTENT | 0x31,
-    TextContent		= TAKES_CONTENT | 0x32,
+    ValueContent		= TAKES_CONTENT | 0x32,
     
     GridLayout		= TAKES_CONTENT | PARENT | 0x100,
     TrialContentLayout	= PARENT | 0x110,
@@ -126,7 +126,7 @@
         "ContentLabel",
         "DisplayContent",
         "BoolContent",
-	"TextContent",
+	"ValueContent",
         "editContent",
         "TrialContentLayout",
         "FloatingArea",
--- a/mde/gui/widget/miscContent.d	Sat Nov 15 17:39:14 2008 +0000
+++ b/mde/gui/widget/miscContent.d	Sun Nov 16 17:03:47 2008 +0000
@@ -28,8 +28,8 @@
 IChildWidget editContent (IWidgetManager mgr, widgetID id, WidgetData data, IContent c) {
     if (cast(BoolContent) c)
         return new BoolContentWidget(mgr,id,data,c);
-    else if (cast(TextContent) c)
-	return new TextContentWidget(mgr,id,data,c);
+    else if (cast(ValueContent) c)
+	return new ValueContentWidget(mgr,id,data,c);
     else        // generic uneditable option
         return new DisplayContentWidget(mgr,id,data,c);
 }
--- a/mde/gui/widget/textContent.d	Sat Nov 15 17:39:14 2008 +0000
+++ b/mde/gui/widget/textContent.d	Sun Nov 16 17:03:47 2008 +0000
@@ -31,14 +31,14 @@
     }
 }
 
-
-class TextContentWidget : ATextWidget
+/// Capable of editing any ValueContent class
+class ValueContentWidget : ATextWidget
 {
     this (IWidgetManager mgr, widgetID id, WidgetData data, IContent c) {
         WDCheck(data, 1);
-        content = cast(TextContent) c;
-        if (!content) content = new TextContent (null, null);
-	    //throw new ContentException ();
+        content = cast(ValueContent) c;
+        if (!content) //content = new TextContent (null, null);
+	    throw new ContentException ();
         adapter = mgr.renderer.getAdapter (content.toString(0));
         super (mgr, id, data);
     }
@@ -63,5 +63,5 @@
     }
     
 protected:
-    TextContent content;
+    ValueContent content;
 }
--- a/mde/lookup/Options.d	Sat Nov 15 17:39:14 2008 +0000
+++ b/mde/lookup/Options.d	Sun Nov 16 17:03:47 2008 +0000
@@ -124,7 +124,7 @@
     auto p = id in opts;
     if (p) {
         auto q = cast(`~VContentN!(T)~`) (*p);
-        if (q) q.v = parseTo!(`~T.stringof~`) (dt);
+        if (q) q.assignNoCB = parseTo!(`~T.stringof~`) (dt);
     }
 }`;
             } else
--- a/mde/scheduler/Scheduler.d	Sat Nov 15 17:39:14 2008 +0000
+++ b/mde/scheduler/Scheduler.d	Sun Nov 16 17:03:47 2008 +0000
@@ -25,9 +25,9 @@
 debug {
     import tango.util.log.Log : Log, Logger;
     private Logger logger;
-}
-static this() {
-    debug logger = Log.getLogger ("mde.scheduler.Scheduler");
+    static this() {
+	logger = Log.getLogger ("mde.scheduler.Scheduler");
+    }
 }
 
 /// This class can run scheduled functions per frame, per time interval and per request.
--- a/mde/setup/Init.d	Sat Nov 15 17:39:14 2008 +0000
+++ b/mde/setup/Init.d	Sun Nov 16 17:03:47 2008 +0000
@@ -119,6 +119,7 @@
                 paths.extraDataPath = args["data-path"];
             if (args.contains("conf-path"))
                 paths.extraConfPath = args["conf-path"];
+	    
             if (args.contains("base-path"))
                 paths.resolvePaths (args["base-path"]);
             else
--- a/mde/setup/paths.d	Sat Nov 15 17:39:14 2008 +0000
+++ b/mde/setup/paths.d	Sun Nov 16 17:03:47 2008 +0000
@@ -39,8 +39,20 @@
 
 import tango.io.Console;
 import tango.io.FilePath;
-import tango.sys.Environment;
-//import tango.scrapple.sys.win32.Registry;     // Trouble getting this to work
+version (linux) {
+    import tango.io.FileScan;
+    import tango.util.container.SortedMap;
+    import tango.sys.Environment;
+} else version (Windows)
+    import tango.sys.win32.SpecialPath;
+
+debug {
+    import tango.util.log.Log : Log, Logger;
+    private Logger logger;
+    static this() {
+	logger = Log.getLogger ("mde.setup.paths");
+    }
+}
 
 /** Order to read files in.
 *
@@ -111,7 +123,15 @@
         dataDir.coutPaths;
         Cout ("\nConf paths found:");
         confDir.coutPaths;
-        Cout ("\nLog file directory:\n\t")(logDir).newline;
+        Cout ("\nLog file directory:\n\t")(logDir);
+	version (Windows) {
+	    Cout ("\nFont directory:\n\t")(fontDir).newline;
+	} else version (linux) {
+	    Cout ("\nFont filse found:");
+	    foreach (f,p; fontFiles)
+		Cout ("\n\t")(f)("\t")(p[0..$-1]);
+	    Cout.newline;
+	}
     }
     
 private:
@@ -190,7 +210,7 @@
 
 /** These are the actual instances, one for each of the data and conf "directories". */
 mdeDirectory dataDir, confDir;
-char[] logDir;
+char[] logDir;		/// Directory for log files
 
 //BEGIN Path resolution
 // These are used several times:
@@ -202,15 +222,26 @@
 * Note: the logger cannot be used yet, so only output is exception messages. */
 // FIXME: use tango/sys/Environment.d
 version (linux) {
+    SortedMap!(char[],char[]) fontFiles;	// key is file name, value is CString path
+    /** Get the actual path of a font file, or throw NoFileException if not found.
+     *
+     * Returns a C string (null terminated). */
+    char[] getFontPath (char[] file) {
+	char[] ret;
+	if (fontFiles.get (file, ret))
+	    return ret;
+	throw new NoFileException ("Unable to find font file: "~file);
+    }
+    
     // base-path not used on posix
-    void resolvePaths (char[] = null) {
+    void resolvePaths (char[] base = "data") {
         // Home directory:
         char[] HOME = Environment.get("HOME", ".");
         
         // Base paths:
         // Static data (must exist):
         FilePath staticPath =
-                findPath (false, "/usr/share/games/mde", "/usr/local/share/games/mde", "data");
+                findPath (false, "/usr/share/games/mde", "/usr/local/share/games/mde", base);
         // Config (can just use defaults if necessary, so long as we can save afterwards):
         FilePath userPath = findPath (true, HOME~"/.config/mde", HOME~"/.mde");
         
@@ -229,17 +260,37 @@
         
         // Logging path:
         logDir = userPath.toString;
+	
+	// Font paths:
+	auto fs = new FileScan;
+	// Scan for directories containing truetype and type1 fonts:
+	fs.sweep ("/usr/share/fonts", (FilePath fp, bool isDir)
+	    {	return isDir || fp.suffix == ".ttf" || fp.suffix == ".pfb";	},
+		   true);
+	fontFiles = new SortedMap!(char[],char[]);
+	foreach (fp; fs.files)
+	    fontFiles.add (fp.file, fp.cString);	// both strings should be slices of same memory
+	logger.trace ("found {} font files, {} dirs", fs.files.length, fs.folders.length);
     }
 } else version (Windows) {
+    char[] fontDir;
+    /** Get the actual path of a font file, or throw NoFileException if not found.
+     *
+     * Returns a C string (null terminated). */
+    char[] getFontPath (char[] file) {
+	FilePath path = new FilePath (fontDir~file);
+	if (path.exists && !path.isFolder)
+	    return path.CString;
+	throw new NoFileException ("Unable to find font file: "~file);
+    }
+    
     void resolvePaths (char[] base = "./") {
-        //FIXME: Get path from registry
-        //FIXME: Get user path (Docs&Settings/USER/Local Settings/Application data/mde)
-        //http://www.dsource.org/projects/tango/forums/topic/187
+        //FIXME: Get base path from registry
         
         // Base paths:
         FilePath installPath = findPath (false, base);
-        FilePath staticPath = findPath (false, installPath.append("data").toString);
-        FilePath userPath = findPath (true, installPath.append("user").toString);   // FIXME: see above
+        FilePath staticPath = findPath (false, installPath.toString);
+	FilePath userPath = findPath (true, getSpecialPath(CSIDL_LOCAL_APPDATA) ~ "/mde");
         
         // Static data paths:
         dataDir.addPath (staticPath.toString);   // we know this is valid anyway
@@ -256,6 +307,9 @@
         
         // Logging path:
         logDir = userPath.toString;
+	
+	// Font path:
+	fontDir = getSpecialPath (CSIDL_FONTS) ~ "/";	// append separator
     }
 } else {
     static assert (false, "Platform is not linux or Windows: no support for paths on this platform yet!");