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