Mercurial > projects > mde
view mde/content/ServiceContent.d @ 173:a1ba9157510e
Enabled ServiceContentList to call its callbacks when its value changes. Tried to fix some other bugs, but this is not a very clean commit, due to wanting to make some big changes to enable better use of invariants next.
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Sat, 08 Aug 2009 15:53:10 +0200 |
parents | 7f7b2011b759 |
children | 1f9d00f392bd |
line wrap: on
line source
/* LICENSE BLOCK Part of mde: a Modular D game-oriented Engine Copyright © 2007-2009 Diggory Hardy This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ /** Content services. * * Each is a Content, so it can be used in a menu. Since the content being * acted on is not passed when functions are called like this, it is set when * a context menu is opened, which also allows the services to specify their * compatibility with the content (type) passed (via BoolContent value and * callbacks). */ module mde.content.ServiceContent; import mde.content.AStringContent; debug { import tango.util.log.Log : Log, Logger; private Logger logger; static this () { logger = Log.getLogger ("mde.gui.content.ServiceContent"); } } /** Interface for ServiceContent and ServiceContentList. */ interface IServiceContent : IContent { void setContent (Content cont); } /** A class for services acting on content. * * Provides services for any content usable as type T, where T is some content * type. * * The (boolean) value is true to indicate this service can act on the passed * content type, false if not. */ class ServiceContent(T : Content = AStringContent) : Content, IServiceContent, IBoolContent, IEventContent { this (char[] symbol) { super (symbol); } /** Pass the content this service should prepare for. * * Stores the reference, because it won't be passed later. */ override void setContent (Content cont) { T oCont = activeCont; activeCont = cast(T)cont; if ((oCont !is null) != (activeCont !is null)) endEvent; } override bool opCall () { debug logger.trace ("ServiceContent.opCall: {}", symbol); return activeCont !is null; } // Doesn't support directly setting the value override void opAssign (bool val) {} package: T activeCont; } /** Aliases for common types */ alias ServiceContent!(AStringContent) AStringService; alias ServiceContent!(IntContent) IntService; ///ditto /** A generic way to handle a list of type IContent. */ class ServiceContentList : ContentList, IServiceContent, IBoolContent { this (char[] symbol) { super (symbol); } void setContent (Content cont) { foreach (child; list_) { (cast(IServiceContent)child).setContent (cont); } } override void append (Content x) { assert (cast(IBoolContent) x, "Only IBoolContent children are allowed!"); list_ ~= x; x.addCallback (&childChangeCB); } override bool opCall () { debug logger.trace ("ServiceContentList.opCall"); return v; } // Doesn't support directly setting the value override void opAssign (bool val) {} /** Create all services. * * Each WidgetManager has it's own instance, but these share one clipboard, etc. * * Currently all clipboard operations convert to/from a string. * TODO: Possible extensions: multi-item clipboard displaying value in menu, * copying without converting everything to/from a string. */ static ServiceContentList createItems (char[] name) { char[] lName = "menus.services."~name; auto ret = new ServiceContentList (lName); // Many use callbacks on a generic class type. For this, we should be sure of the type. (new AStringService(lName~".copy")).addCallback (delegate void(IContent c) { debug assert (cast(AStringService)c); with (cast(AStringService)c) { if (activeCont !is null) clipboard = activeCont.toString(0); } }); (new AStringService(lName~".paste")).addCallback (delegate void(IContent c) { debug assert (cast(AStringService)c); with (cast(AStringService)c) { if (activeCont !is null) activeCont = clipboard; } }); new ServiceContentList(lName~".calculator"); (new IntService(lName~".calculator.increment")).addCallback (delegate void(IContent c) { debug assert (cast(IntService)c); with (cast(IntService)c) { if (activeCont !is null) activeCont = activeCont()+1; } }); (new IntService(lName~".calculator.decrement")).addCallback (delegate void(IContent c) { debug assert (cast(IntService)c); with (cast(IntService)c) { if (activeCont !is null) activeCont = activeCont()-1; } }); return ret; } private: void childChangeCB (IContent icont) { if (v == false) { // then value changes iff icont() debug assert (cast(IBoolContent) icont); if ((cast(IBoolContent) icont)()) { v = true; endEvent; } } else { // we must check all children v = false; foreach (child; list_) { if ((cast(IBoolContent)child)()) { v = true; break; } } if (!v) endEvent; } } bool v = false; // cache value so we can see when it changes static char[] clipboard; //TODO: lock on clipboard: static Mutex mutex; }