1/* 2 * This file is part of gitg 3 * 4 * Copyright (C) 2012 - Jesse van den Kieboom 5 * 6 * gitg 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 2 of the License, or 9 * (at your option) any later version. 10 * 11 * gitg 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 gitg. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20namespace GitgExt 21{ 22 23public delegate void MessageCallback(GitgExt.Message message); 24 25public class MessageBus : Object 26{ 27 class Listener 28 { 29 public uint id; 30 public bool blocked; 31 32 public MessageCallback callback; 33 34 public Listener(uint id, owned MessageCallback callback) 35 { 36 this.id = id; 37 38 // TODO: destroy notify is lost... 39 this.callback = (owned)callback; 40 this.blocked = false; 41 } 42 } 43 44 class Message 45 { 46 public MessageId id; 47 public List<Listener> listeners; 48 49 public Message(MessageId id) 50 { 51 this.id = id.copy(); 52 this.listeners = new List<Listener>(); 53 } 54 } 55 56 class IdMap 57 { 58 public Message message; 59 public unowned List<Listener> listener; 60 61 public IdMap(Message message) 62 { 63 this.message = message; 64 } 65 } 66 67 private HashTable<MessageId, Message> d_messages; 68 private HashTable<uint, IdMap> d_idmap; 69 private HashTable<MessageId, Type> d_types; 70 private static MessageBus? s_instance; 71 private static uint s_next_id; 72 73 public signal void registered(MessageId id); 74 public signal void unregistered(MessageId id); 75 76 public virtual signal void dispatch(GitgExt.Message message) 77 { 78 Message? msg = lookup_message(message.id, false); 79 80 if (msg != null) 81 { 82 dispatch_message_real(msg, message); 83 } 84 } 85 86 public MessageBus() 87 { 88 d_messages = new HashTable<MessageId, Message>(MessageId.hash, MessageId.equal); 89 d_idmap = new HashTable<uint, IdMap>(direct_hash, direct_equal); 90 d_types = new HashTable<MessageId, Type>(MessageId.hash, MessageId.equal); 91 } 92 93 public static MessageBus get_default() 94 { 95 if (s_instance == null) 96 { 97 s_instance = new MessageBus(); 98 s_instance.add_weak_pointer(&s_instance); 99 } 100 101 return s_instance; 102 } 103 104 private void dispatch_message_real(Message msg, GitgExt.Message message) 105 { 106 foreach (Listener l in msg.listeners) 107 { 108 if (!l.blocked) 109 { 110 l.callback(message); 111 } 112 } 113 } 114 115 public Type lookup(MessageId id) 116 { 117 Type ret; 118 119 if (!d_types.lookup_extended(id, null, out ret)) 120 { 121 return Type.INVALID; 122 } 123 else 124 { 125 return ret; 126 } 127 } 128 129 public void register(Type message_type, MessageId id) 130 { 131 if (is_registered(id)) 132 { 133 warning("Message type for `%s' is already registered", id.id); 134 return; 135 } 136 137 var cp = id.copy(); 138 139 d_types.insert(cp, message_type); 140 141 registered(cp); 142 } 143 144 private void unregister_real(MessageId id, bool remove_from_store) 145 { 146 var cp = id; 147 148 if (!remove_from_store || d_types.remove(cp)) 149 { 150 unregistered(cp); 151 } 152 } 153 154 public void unregister(MessageId id) 155 { 156 unregister_real(id, true); 157 } 158 159 public void unregister_all(string object_path) 160 { 161 d_types.foreach_remove((key, val) => { 162 if (key.object_path == object_path) 163 { 164 unregister_real(key, true); 165 return true; 166 } 167 else 168 { 169 return false; 170 } 171 }); 172 } 173 174 public bool is_registered(MessageId id) 175 { 176 return d_types.lookup_extended(id, null, null); 177 } 178 179 private Message new_message(MessageId id) 180 { 181 var ret = new Message(id); 182 183 d_messages.insert(id, ret); 184 return ret; 185 } 186 187 private Message? lookup_message(MessageId id, bool create) 188 { 189 var message = d_messages.lookup(id); 190 191 if (message == null && !create) 192 { 193 return null; 194 } 195 196 if (message == null) 197 { 198 message = new_message(id); 199 } 200 201 return message; 202 } 203 204 private uint add_listener(Message message, owned MessageCallback callback) 205 { 206 var listener = new Listener(++s_next_id, (owned)callback); 207 208 message.listeners.append(listener); 209 210 var idmap = new IdMap(message); 211 idmap.listener = message.listeners.last(); 212 213 d_idmap.insert(listener.id, idmap); 214 return listener.id; 215 } 216 217 private void remove_listener(Message message, List<Listener> listener) 218 { 219 unowned Listener lst = listener.data; 220 221 d_idmap.remove(lst.id); 222 223 message.listeners.delete_link(listener); 224 225 if (message.listeners == null) 226 { 227 d_messages.remove(message.id); 228 } 229 } 230 231 private void block_listener(Message message, List<Listener> listener) 232 { 233 listener.data.blocked = true; 234 } 235 236 private void unblock_listener(Message message, List<Listener> listener) 237 { 238 listener.data.blocked = false; 239 } 240 241 public new uint connect(MessageId id, owned MessageCallback callback) 242 { 243 var message = lookup_message(id, true); 244 245 return add_listener(message, (owned)callback); 246 } 247 248 private delegate void MatchCallback(Message message, List<Listener> listeners); 249 250 private void process_by_id(uint id, MatchCallback processor) 251 { 252 IdMap? idmap = d_idmap.lookup(id); 253 254 if (idmap == null) 255 { 256 return; 257 } 258 259 processor(idmap.message, idmap.listener); 260 } 261 262 public new void disconnect(uint id) 263 { 264 process_by_id(id, remove_listener); 265 } 266 267 public void block(uint id) 268 { 269 process_by_id(id, block_listener); 270 } 271 272 public void unblock(uint id) 273 { 274 process_by_id(id, unblock_listener); 275 } 276 277 private void dispatch_message(GitgExt.Message message) 278 { 279 dispatch(message); 280 } 281 282 public GitgExt.Message send_message(GitgExt.Message message) 283 { 284 dispatch_message(message); 285 return message; 286 } 287 288 public GitgExt.Message? send(MessageId id, string? firstprop, ...) 289 { 290 Type type = lookup(id); 291 292 if (type == Type.INVALID) 293 { 294 warning("Could not find message type for `%s'", id.id); 295 return null; 296 } 297 298 GitgExt.Message? msg = (GitgExt.Message?)Object.new_valist(type, firstprop, va_list()); 299 300 if (msg != null) 301 { 302 msg.id = id; 303 } 304 305 dispatch_message(msg); 306 307 return msg; 308 } 309} 310 311} 312 313// ex:set ts=4 noet: 314