fe9268d777c91209b6f989c5dd0d80470c481e18
[debian/pino.git] / src / accounts.vala
1 using Gee;
2 using Xml;
3 using PinoEnums;
4
5 public class Accounts : ArrayList<AAccount> {
6         
7         public signal void insert_new_account(AAccount account);
8         public signal void insert_new_stream_after(string after_path, AStream stream);
9         public signal void element_was_removed(string path, AAccount account);
10         public signal void fresh_items_changed(int items, string path);
11         public signal void account_was_changed(string path, AAccount account);
12         public signal void stream_was_changed(string path, AStream stream);
13         public signal void stream_was_updated(string hash, AStream stream);
14         public signal void status_sent(AAccount account, bool ok, string error_msg = "");
15         public signal void message_indicate(string msg);
16         public signal void stop_indicate();
17         public signal void do_reply(AAccount account, Status status);
18         
19         //private ArrayList<AccountState> accounts_states;
20         
21         private string accounts_path;
22         
23         public Accounts() {
24                 base();
25                 
26                 init();
27                 
28                 /*string data = """<accounts>
29         <account type="TwitterAccount">
30                 <s-name>testo</s-name>
31                 <s-update-interval>3000</s-update-interval>
32                 <streams>
33                         <stream type="0">
34                                 <s-update-interval>5000</s-update-interval>
35                         </stream>
36                         <stream type="1">
37                                 <s-update-interval>5000</s-update-interval>
38                         </stream>
39                         <stream type="0">
40                                 <s-update-interval>5000</s-update-interval>
41                         </stream>
42                 </streams>
43         </account>
44         <account type="TwitterAccount">
45                 <s-name>trededed</s-name>
46                 <s-update-interval>6000</s-update-interval>
47                 <streams>
48                         <stream type="0">
49                                 <s-update-interval>5000</s-update-interval>
50                         </stream>
51                 </streams>
52         </account>
53 </accounts>""";*/
54         }
55         
56         private void init() {
57                 string app_dir = Environment.get_user_config_dir() + "/%s".printf(Config.APPNAME);
58                 
59                 var dir = File.new_for_path(app_dir);
60                 
61                 if(!dir.query_exists(null)) {
62                         try {
63                                 dir.make_directory(null);
64                         } catch(GLib.Error e) {
65                                 debug(e.message); //TODO
66                         }
67                 }
68                 
69                 dir = null;
70                 
71                 accounts_path = app_dir + "/accounts.xml";
72                 File acc_file = File.new_for_path(accounts_path);
73                 
74                 if(acc_file.query_exists(null)) {
75                         FileInputStream fstream;
76                         try {
77                                 fstream = acc_file.read(null);
78                         } catch(GLib.Error e) {
79                                 debug(e.message); //TODO
80                                 return;
81                         }
82                         DataInputStream stream = new DataInputStream(fstream);
83                         
84                         string data;
85                         try {
86                                 data = stream.read_until("", null, null);
87                         } catch(GLib.Error e) {
88                                 debug(e.message); //TODO
89                                 return;
90                         }
91                         from_xml(data);
92                 }
93         }
94         
95         public void sync() {
96                 File file = File.new_for_path(accounts_path);
97                 
98                 FileOutputStream stream;
99                 try {
100                         stream = file.replace(null, false, FileCreateFlags.NONE, null);
101                 } catch(GLib.Error e) {
102                         debug(e.message); //TODO
103                         return;
104                 }
105                 
106                 DataOutputStream data_stream = new DataOutputStream(stream);
107                 
108                 try {
109                         data_stream.put_string(to_xml(), null);
110                 } catch(GLib.Error e) {
111                         debug(e.message); //TODO
112                 }
113         }
114         
115         /** Converting values to string */
116         public static string value_to_string(Value v, Type t) {
117                 switch(t.name()) {
118                 case "gint":
119                         return v.get_int().to_string();
120                 
121                 case "gdouble":
122                         return v.get_double().to_string();
123                 
124                 case "gboolean":
125                         return v.get_boolean().to_string();
126                 
127                 case "gchararray":
128                         return v.get_string();
129                 
130                 case "PinoEnumsStreamEnum":
131                         return v.get_enum().to_string();
132                 
133                 default:
134                         return "";
135                 }
136         }
137         
138         /** Restoring values from string */
139         public static Value? value_from_string(string data, Type t) {
140                 Value? v = Value(t);
141                 
142                 switch(t.name()) {
143                 case "gint":
144                         v.set_int(data.to_int());
145                         break;
146                 
147                 case "gdouble":
148                         v.set_double(data.to_double());
149                         break;
150                 
151                 case "gboolean":
152                         v.set_boolean(data.to_bool());
153                         break;
154                 
155                 case "gchararray":
156                         v.set_string(data);
157                         break;
158                 
159                 case "PinoEnumsStreamEnum":
160                         v.set_enum(data.to_int());
161                         break;
162                 
163                 default:
164                         v = null;
165                         break;
166                 }
167                 
168                 return v;
169         }
170         
171         /** Save all accounts and it's streams properties to xml */
172         public string to_xml() {
173                 string result = "\n<accounts>\n%s</accounts>";
174                 string istr = "";
175                 
176                 foreach(AAccount account in this) {
177                         var obj = (ObjectClass) account.get_type().class_ref();
178                         
179                         string account_str = "\t<account type=\"%s\">\n%s\t</account>\n";
180                         string account_props = "";
181                         
182                         foreach(var p in obj.list_properties()) {
183                                 //debug("%s, %s", p.value_type.name(), p.get_name());
184                                 
185                                 if(p.get_name().substring(0, 2) != "s-") //we need only s-* properties
186                                         continue;
187                                 
188                                 Value v = Value(p.value_type);
189                                 account.get_property(p.get_name(), ref v);
190                         
191                                 string s_val = value_to_string(v, p.value_type);
192                         
193                                 if(s_val != "")
194                                         account_props += "\t\t<%s>%s</%s>\n".printf(p.get_name(), s_val, p.get_name());
195                         }
196                         
197                         string streams_result = "\t\t<streams>\n%s\t\t</streams>\n";
198                         string streams_str = "";
199                         
200                         foreach(AStream stream in account.streams) { //saving streams
201                                 var obj_stream = (ObjectClass) stream.get_type().class_ref();
202                                 string stream_str = "\t\t\t<stream type=\"%d\">\n%s\t\t\t</stream>\n";
203                                 string stream_props = "";
204                                 
205                                 foreach(var p in obj_stream.list_properties()) {
206                                         if(p.get_name().substring(0, 2) != "s-") //we need only s-* properties
207                                                 continue;
208                                         
209                                         Value v = Value(p.value_type);
210                                         stream.get_property(p.get_name(), ref v);
211                                         
212                                         string s_val = value_to_string(v, p.value_type);
213                                         
214                                         if(s_val != "")
215                                                 stream_props += "\t\t\t\t<%s>%s</%s>\n".printf(p.get_name(), s_val, p.get_name());
216                                 }
217                                 
218                                 streams_str += stream_str.printf(stream.stream_type, stream_props);
219                         }
220                         
221                         account_props += streams_result.printf(streams_str);
222                         
223                         //debug(streams_str);
224                         istr += account_str.printf(account.get_type().name(), account_props);
225                 }
226                 
227                 return result.printf(istr);
228         }
229         
230         /** Restore accounts and streams from xml */
231         public void from_xml(string data) {
232                 Doc* xml_doc = Parser.parse_memory(data, (int) data.size());
233                 Xml.Node* root_node = xml_doc->get_root_element();
234                 
235                 if(root_node == null)
236                         return;
237                 
238                 for(Xml.Node* iter = root_node->children; iter != null; iter = iter->next) {
239                         if(iter->type != ElementType.ELEMENT_NODE || iter->name != "account")
240                                 continue;
241                         
242                         string? account_type = iter->get_prop("type");
243                         if(account_type == null)
244                                 continue;
245                         
246                         AAccount account = (AAccount) Object.new(Type.from_name(account_type));
247                         prepare_account(account);
248                         
249                         HashMap<string, string> account_map = new HashMap<string, string>();
250                         
251                         /** Getting properties into hashmap and adding streams to account */
252                         for(Xml.Node* iter_acc = iter->children; iter_acc != null; iter_acc = iter_acc->next) {
253                                 if(iter_acc->type != ElementType.ELEMENT_NODE)
254                                         continue;
255                                 
256                                 switch(iter_acc->name) {
257                                 case "streams": //streams list
258                                         for(Xml.Node* iter_streams = iter_acc->children; iter_streams != null; iter_streams = iter_streams->next) {
259                                                 if(iter_streams->type != ElementType.ELEMENT_NODE || iter_streams->name != "stream")
260                                                         continue;
261                                                 debug("ok");
262                                                 string? stream_type = iter_streams->get_prop("type");
263                                                 if(stream_type == null)
264                                                         continue;
265                                                 
266                                                 HashMap<string, string> stream_map = new HashMap<string, string>();
267                                                 
268                                                 for(Xml.Node* iter_stream = iter_streams->children; iter_stream != null; iter_stream = iter_stream->next) {
269                                                         if(iter_stream->type != ElementType.ELEMENT_NODE)
270                                                                 continue;
271                                                         
272                                                         stream_map.set(iter_stream->name, iter_stream->get_content());
273                                                 }
274                                                 
275                                                 account.add_stream((StreamEnum) stream_type.to_int(),
276                                                         false, stream_map);
277                                         }
278                                         break;
279                                 
280                                 default: //all other properties
281                                         account_map.set(iter_acc->name, iter_acc->get_content());
282                                         break;
283                                 }
284                         }
285                         
286                         /** Setting properties to account */
287                         var obj = (ObjectClass) Type.from_name(account_type).class_ref();
288                         foreach(var p in obj.list_properties()) {
289                                 string? pval = account_map.get(p.get_name());
290                                 
291                                 if(pval == null)
292                                         continue;
293                                 
294                                 Value? val = value_from_string(pval, p.value_type);
295                                 
296                                 if(val == null) {
297                                         debug("this value type is not supported");
298                                         continue;
299                                 }
300                                 
301                                 account.set_property(p.get_name(), val);
302                         }
303                         
304                         account.post_install();
305                         add(account);
306                 }
307         }
308         
309         /** Add new account */
310         public void add_account(Type t, Gtk.Window w) {
311                 AAccount account = (AAccount) GLib.Object.new(t);
312                 
313                 if(!account.create(w))
314                         return;
315                 
316                 prepare_account(account);
317                 account.post_install();
318                 insert_new_account(account); //signal for tree and others
319                 
320                 add(account);
321         }
322         
323         private void prepare_account(AAccount account) {
324                 account.stream_was_added.connect(new_stream);
325                 account.stream_was_removed.connect(remove_stream);
326                 account.account_was_changed.connect(account_update);
327                 account.stream_was_changed.connect(stream_change);
328                 account.stream_was_updated.connect(stream_update);
329                 account.status_sent.connect((ac, ok, msg) => {
330                         status_sent(ac, ok, msg);
331                 });
332                 account.message_indicate.connect((msg) => {
333                         message_indicate(msg);
334                 });
335                 account.stop_indicate.connect(() => {
336                         stop_indicate();
337                 });
338                 
339                 account.do_reply.connect((acc, status) => {
340                         do_reply(acc, status);
341                 });
342         }
343
344         /** New stream was added, we need to report about this to the tree widget */
345         private void new_stream(AAccount account, AStream stream) {
346                 int account_index = index_of(account);
347                 insert_new_stream_after(account_index.to_string(), stream);
348         }
349         
350         /** Stream was removed from some account */
351         private void remove_stream(AAccount account, int stream_index) {
352                 int account_index = index_of(account);
353                 element_was_removed("%d:%d".printf(account_index, stream_index), account);
354         }
355         
356         /** New data in account */
357         private void account_update(AAccount account) {
358                 int account_index = index_of(account);
359                 account_was_changed(account_index.to_string(), account);
360         }
361         
362         /** New data in stream */
363         private void stream_change(AAccount account, AStream stream, int stream_index) {
364                 int account_index = index_of(account);
365                 stream_was_changed("%d:%d".printf(account_index, stream_index), stream);
366         }
367         
368         /** Generate list from stream */
369         private void stream_update(string hash, AStream stream) {
370                 stream_was_updated(hash, stream);
371         }
372         
373         /** Send 'updated' signal from all streams */
374         public void update_all_streams() {
375                 foreach(AAccount account in this) {
376                         foreach(AStream stream in account.streams) {
377                                 stream.updated();
378                         }
379                 }
380         }
381         
382         /** Somesing pressed in content view */
383         public void new_content_action(string action_type, string path) {
384                 string[] params = path.split("##");
385                 
386                 if(params.length < 3)
387                         return;
388                 
389                 string account_hash = params[0];
390                 string stream_hash = params[1];
391                 string val = params[2];
392                 
393                 
394                 AAccount? account = null;
395                 foreach(AAccount acc in this) {
396                         if(acc.get_hash() == account_hash) {
397                                 account = acc;
398                                 break;
399                         }
400                 }
401                 
402                 if(account == null) {
403                         debug("can't find this account");
404                         return;
405                 }
406                 
407                 account.new_content_action(action_type, stream_hash, val);
408         }
409         
410         /** Return stream from hash */
411         public AStream? stream_from_hash(string hash) {
412                 foreach(AAccount account in this) {
413                         foreach(AStream fstream in account.streams) {
414                                 if(account.get_stream_hash(fstream) == hash)
415                                         return fstream;
416                         }
417                 }
418                 
419                 return null;
420         }
421         
422         /** Action for accounts */
423         public virtual void actions_tracker(AAccount account, MenuItems item,
424                 Gtk.Window parent) {
425                 
426                 switch(item) {
427                 case MenuItems.REMOVE:
428                         Gtk.MessageDialog dlg = new Gtk.MessageDialog(parent, Gtk.DialogFlags.MODAL,
429                                 Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO,
430                                 _("Do you realy want to remove this account?"));
431                         
432                         int result = dlg.run();
433                         dlg.close();
434                         
435                         if(result == Gtk.ResponseType.YES) {
436                                 debug("remove");
437                                 int account_index = index_of(account);
438                                 element_was_removed(account_index.to_string(), account);
439                                 this.remove(account);
440                         }
441                         break;
442                 }
443         }
444         
445         public AAccount? get_by_hash(string hash) {
446                 foreach(AAccount account in this) {
447                         if(account.get_hash() == hash)
448                                 return account;
449                 }
450                 
451                 return null;
452         }
453 }