1 /*
2  Copyright (c) 2013 yvt
3 
4  This file is part of OpenSpades.
5 
6  OpenSpades is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  OpenSpades is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with OpenSpades.  If not, see <http://www.gnu.org/licenses/>.
18 
19  */
20 
21 #pragma once
22 
23 #include <map>
24 #include <string>
25 #include <type_traits>
26 #include <vector>
27 
28 namespace spades {
29 	enum class SettingItemFlags { None = 0 };
30 
31 	inline SettingItemFlags operator|(SettingItemFlags lhs, SettingItemFlags rhs)
32 
33 	{
34 		using T = std::underlying_type<SettingItemFlags>::type;
35 		return (SettingItemFlags)(static_cast<T>(lhs) | static_cast<T>(rhs));
36 	}
37 
38 	inline SettingItemFlags &operator|=(SettingItemFlags &lhs, SettingItemFlags rhs) {
39 		using T = std::underlying_type<SettingItemFlags>::type;
40 		lhs = (SettingItemFlags)(static_cast<T>(lhs) | static_cast<T>(rhs));
41 		return lhs;
42 	}
43 
44 	struct SettingItemDescriptor {
45 		const std::string defaultValue;
46 		const SettingItemFlags flags;
47 
48 		SettingItemDescriptor(const std::string &defaultValue = std::string(),
49 		                      SettingItemFlags flags = SettingItemFlags::None)
defaultValueSettingItemDescriptor50 		    : defaultValue(defaultValue), flags(flags) {}
51 
52 		bool operator==(const SettingItemDescriptor &o) const {
53 			return defaultValue == o.defaultValue && flags == o.flags;
54 		}
55 		bool operator!=(const SettingItemDescriptor &o) const { return !(*this == o); }
56 	};
57 
58 	class ISettingItemListener {
59 	protected:
60 		ISettingItemListener() = default;
~ISettingItemListener()61 		virtual ~ISettingItemListener() {}
62 
63 	public:
64 		virtual void SettingChanged(const std::string &name) = 0;
65 	};
66 
67 	class Settings {
68 		struct Item {
69 			std::string name;
70 			std::string string;
71 			float value;
72 			int intValue;
73 
74 			const SettingItemDescriptor *descriptor;
75 			bool defaults;
76 
77 			std::vector<ISettingItemListener *> listeners;
78 
79 			void Load();
80 			void Set(const std::string &);
81 			void Set(int);
82 			void Set(float);
83 
84 			void NotifyChange();
85 		};
86 		std::map<std::string, Item *> items;
87 		bool loaded;
88 		Settings();
89 
90 		Item *GetItem(const std::string &name, const SettingItemDescriptor *descriptor);
91 
92 		void Save();
93 
94 	public:
95 		static Settings *GetInstance();
96 
97 		class ItemHandle {
98 			Item *item;
99 
100 		public:
101 			ItemHandle(const std::string &name, const SettingItemDescriptor *descriptor);
102 			void operator=(const std::string &);
103 			void operator=(int);
104 			void operator=(float);
105 			operator std::string();
106 			operator float();
107 			operator int();
108 			operator bool();
109 			const char *CString();
110 
111 			const SettingItemDescriptor &GetDescriptor();
112 
113 			/**
114 			 * Returns whether this config variable is used and defined by the program
115 			 * or not.
116 			 */
117 			bool IsUnknown();
118 
119 			void AddListener(ISettingItemListener *);
120 			void RemoveListener(ISettingItemListener *);
121 		};
122 
123 		void Load();
124 		void Flush();
125 		std::vector<std::string> GetAllItemNames();
126 	};
127 	/*
128 	template<const char *name, const char *def>
129 	class Setting: public Settings::ItemHandle {
130 	public:
131 	    Setting(): Settings::ItemHandle(name, def, desc){
132 	    }
133 	};*/
134 
135 	static inline bool operator==(const std::string &str, Settings::ItemHandle &handle) {
136 		return str == (std::string)handle;
137 	}
138 
139 // Define SettingItemDescriptor with external linkage so duplicates are
140 // detected as linker errors.
141 #define DEFINE_SPADES_SETTING(name, ...)                                                           \
142 	spades::SettingItemDescriptor name##_desc{__VA_ARGS__};                                        \
143 	static spades::Settings::ItemHandle name(#name, &name##_desc)
144 
145 #define SPADES_SETTING(name) static spades::Settings::ItemHandle name(#name, nullptr)
146 }
147