Mercurial repo sync
[debian/pino.git] / src / account_abstract.vala
1 using Gee;
2 using PinoEnums;
3 using Gtk;
4
5 /** Abstract class for any account in this app */
6 public abstract class AAccount : GLib.Object {
7
8         public signal void stream_was_added(AAccount account, AStream stream);
9         public signal void stream_was_removed(AAccount account, int stream_index);
10         public signal void fresh_items_changed(int items, int stream_index, AAccount account);
11         public signal void account_was_changed(AAccount account);
12         public signal void status_sent(AAccount account, bool ok, string error_msg = "");
13         public signal void message_indicate(string msg);
14         public signal void stop_indicate();
15         public signal void do_reply(AAccount account, Status status);
16         public signal void insert_reply(string stream_hash, string status_id, Status result);
17         
18         /* For tree widget */
19         public signal void stream_was_changed(AAccount account, AStream stream, int stream_index);
20         
21         /** For updating list content */
22         public signal void stream_was_updated(string hash, AStream stream);
23         
24         /** Userpic */
25         //private Gdk.Pixbuf? _userpic = null;
26         public Gdk.Pixbuf? userpic {get; set; default = null;}
27         public string s_avatar_url {get; set; default = "";}
28         
29         /** Holdes list of current streams */
30         private ArrayList<AStream> _streams = new ArrayList<AStream>();
31         public ArrayList<AStream> streams {get { return _streams; } }
32         
33         /** Items in context menu of this account */
34         public abstract MenuItems[] popup_items {owned get;}
35         
36         /** Items in statuses context menu */
37         public abstract StatusMenuItems[] status_popup_items {owned get;}
38         
39         /** All streams wich can be added to this account */
40         public abstract HashMap<StreamEnum, GLib.Type> avaliable_streams {owned get;}
41
42         /** Default number of streams for account */
43         protected abstract StreamEnum[] default_streams {owned get;}
44
45         /** Account setup, must contain stream_setup */
46         //public abstract void set_properties(AccountState state);
47         
48         public virtual string s_name {get; set; default = "unknown";}
49         
50         /** Update interval in secs */
51         public virtual int s_update_interval {get; set; default = 3000;}
52         
53         /** Account string id */
54         public abstract string id {get; set;}
55         
56         /** Account unique hash */
57         public string s_hash {get; set; default = "";}
58         
59         public string get_hash() {
60                 if(s_hash == "") {
61                         TimeVal t = TimeVal();
62                         t.get_current_time();
63                         s_hash = t.tv_usec.to_string() + Random.int_range(10000, 99999).to_string();
64                 }
65                 
66                 return "%s::%s::%s".printf(id, s_name, s_hash);
67         }
68         
69         /** Send status */
70         public virtual void send_status(string status_text, string reply_id) {
71                 debug("not implemented");
72         }
73         
74         /** Do something after creating */
75         public virtual void post_install() {
76         
77         }
78         
79         construct {
80                 notify["userpic"].connect((s) => {
81                         debug("userpic changed");
82                         account_was_changed(this);
83                 });
84         }
85         
86         /** Unique hash of stream+account */
87         public string get_stream_hash(AStream stream) {
88                 if(stream.s_hash == "") {
89                         TimeVal t = TimeVal();
90                         t.get_current_time();
91                         stream.s_hash = t.tv_usec.to_string() + Random.int_range(10000, 99999).to_string(); //this is how we generate unique hash
92                 }
93                 //debug("unique hash: %s", stream.s_hash);
94                 return "%s::%s::%s::%s".printf(id, s_name, stream.id, stream.s_hash);
95         }
96         
97         /** Unique hash from stream index + account 
98         public string get_stream_hash_indexed(int index) {
99                 
100         }*/
101         
102         /** Init new account by dialog */
103         public virtual bool create(Gtk.Window w) {
104                 setup_default_streams();
105                 debug(streams.size.to_string());
106                 return true;
107         }
108         
109         /** Create default streams from scratch */
110         public void setup_default_streams() {
111                 foreach(StreamEnum stream_type in default_streams) {
112                         add_stream(stream_type, true);
113                 }
114         }
115         
116         /** Add new stream */
117         public void add_stream(StreamEnum stream_type, bool emit_signal = false,
118                 HashMap<string, string>? props = null) {
119                 
120                 Type? stype = null;
121                 stype = avaliable_streams.get(stream_type);//streams_types.get_type_by_string(state.stream_type);
122                         
123                 if(stype == null) {
124                         warning("Stream type is not supported");
125                         return;
126                 }
127
128                 AStream stream = (AStream) GLib.Object.new(stype);
129                 stream.account = this;
130                 streams.add(stream);
131                 
132                 if(props != null) {
133                         var obj = (ObjectClass) stype.class_ref();
134                         
135                         foreach(var p in obj.list_properties()) {
136                                 string? pval = props.get(p.get_name());
137                         
138                                 if(pval == null)
139                                         continue;
140                         
141                                 Value? val = Accounts.value_from_string(pval, p.value_type);
142                         
143                                 if(val == null) {
144                                         debug("this value type is not supported");
145                                         continue;
146                                 }
147                         
148                                 stream.set_property(p.get_name(), val);
149                         }
150                 }
151                 
152                 //count of fresh items is was changed
153                 stream.notify["fresh-items"].connect((s) => {
154                         stream_was_changed(this, stream, streams.index_of(stream));
155                 });
156                 
157                 stream.notify["status"].connect((s) => {
158                         stream_was_changed(this, stream, streams.index_of(stream));
159                 });
160                 
161                 stream.updated.connect(() => {
162                         stream_was_updated(get_stream_hash(stream), stream);
163                 });
164                 
165                 if(emit_signal) {
166                         stream_was_added(this, stream);
167                 }
168                 
169                 init_stream(stream);
170         }
171         
172         /** If we need to add some options to the stream */
173         protected virtual void init_stream(AStream stream) {
174                 debug("no init for this stream");
175         }
176         
177         /** Create list of streams from restored state */
178         protected virtual void streams_setup(ArrayList<StreamState> streams_states) {
179                 debug("start creating streams");
180                 foreach(StreamState state in streams_states) {
181                         add_stream(state.stream_type);
182                 }
183                 debug("streams created");
184         }
185         
186         /** Reaction on stream's popup menu actions */
187         public void streams_actions_tracker(int stream_index, MenuItems item) {
188                 debug("action: %d", item);
189                 switch(item) {
190                 case MenuItems.REMOVE:
191                         stream_was_removed(this, stream_index);
192                         streams.remove_at(stream_index);
193                         break;
194                 
195                 case MenuItems.REFRESH:
196                         streams.get(stream_index).menu_refresh();
197                         break;
198                 
199                 case MenuItems.SETTINGS:
200                         streams.get(stream_index).menu_settings();
201                         break;
202                 
203                 case MenuItems.MORE:
204                         streams.get(stream_index).menu_more();
205                         break;
206                 }
207         }
208         
209         /** Return status from some stream */
210         protected Status? get_status(StreamEnum stype, string status_id) {
211                 foreach(AStream stream in streams) {
212                         if(stream.stream_type != stype)
213                                 continue;
214                         
215                         return get_status_from_stream(stream, status_id);
216                         
217                         break;
218                 }
219                 
220                 return null;
221         }
222         
223         /** Return all statuses with some id */
224         protected ArrayList<Status> get_statuses(string status_id) {
225                 ArrayList<Status> lst = new ArrayList<Status>();
226                 foreach(AStream stream in streams) {
227                         Status? status = get_status_from_stream(stream, status_id);
228                         if(status != null)
229                                 lst.add(status);
230                 }
231                 
232                 return lst;
233         }
234         
235         /** Remove status from all streams */
236         protected void remove_status_complete(string status_id) {
237                 foreach(AStream stream in streams) {
238                         Status? status = get_status_from_stream(stream, status_id);
239                         if(status != null) {
240                                 if(!stream.statuses_fresh.remove(status))
241                                         stream.statuses.remove(status);
242                                 
243                                 stream.menu_refresh();
244                         }
245                 }
246         }
247         
248         protected Status? get_status_from_stream(AStream stream, string status_id) {
249                 foreach(Status status in stream.statuses_fresh) {
250                         if(status.id == status_id) {
251                                 return status;
252                         }
253                 }
254                 
255                 foreach(Status status in stream.statuses) {
256                         if(status.id == status_id) {
257                                 return status;
258                         }
259                 }
260                 
261                 return null;
262         }
263         
264         /** Action from content view */
265         public virtual AStream? new_content_action(string action_type,
266                 string stream_hash, string val) {
267                 
268                 debug("menu action");
269                 AStream? stream = null;
270                 foreach(AStream s in streams) {
271                         if(s.s_hash == stream_hash) {
272                                 stream = s;
273                                 break;
274                         }
275                 }
276                 
277                 return stream;
278         }
279         
280         /** Show status context menu */
281         protected virtual void context_menu(AStream stream, Status status) {
282                 Menu menu = new Menu();
283                 debug("creating context menu");
284                 
285                 foreach(StatusMenuItems item in status_popup_items) {
286                         ImageMenuItem? menu_item = null;
287                         
288                         
289                         switch(item) {
290                         case StatusMenuItems.REPLY:
291                                 if(status.own)
292                                         break;
293                                 
294                                 Image? img = new Image.from_stock("gtk-undo", IconSize.MENU);
295                                 menu_item = new ImageMenuItem.with_label(_("Reply"));
296                                 menu_item.set_image(img);
297                                 menu_item.activate.connect(() => menu_do_reply(status));
298                                 break;
299                         
300                         case StatusMenuItems.FAVORITE:
301                                 string label = "";
302                                 Image? img = new Image.from_stock("gtk-about", IconSize.MENU);
303                                 
304                                 if(status.favorited)
305                                         label = _("Remove from favorites");
306                                 else
307                                         label = _("Add to favorites");
308                                 
309                                 menu_item = new ImageMenuItem.with_label(label);
310                                 menu_item.set_image(img);
311                                 menu_item.activate.connect(() => menu_do_favorite(status));
312                                 break;
313                         
314                         case StatusMenuItems.RETWEET:
315                                 if(status.own)
316                                         break;
317                                 
318                                 Image img = new Image.from_file(Config.RETWEET_PATH);
319                                 menu_item = new ImageMenuItem.with_label(_("Retweet"));
320                                 menu_item.set_image(img);
321                                 menu_item.activate.connect(() => menu_do_retweet(status));
322                                 break;
323                         
324                         case StatusMenuItems.REMOVE:
325                                 if(!status.own)
326                                         break;
327                                 
328                                 Image img = new Image.from_stock("gtk-remove", IconSize.MENU);
329                                 menu_item = new ImageMenuItem.with_label(_("Delete"));
330                                 menu_item.set_image(img);
331                                 menu_item.activate.connect(() => menu_do_remove(status));
332                                 break;
333                         }
334                         
335                         
336                         if(menu_item != null)
337                                 menu.add(menu_item);
338                 }
339                 
340                 
341                 menu.popup(null, null, null, 0, 0);
342                 menu.show_all();
343         }
344         
345         /** Virtual context menu actions */
346         protected virtual void menu_do_reply(Status status) {}
347         
348         protected virtual void menu_do_favorite(Status status) {}
349         
350         protected virtual void menu_do_retweet(Status status) {}
351         
352         protected virtual void menu_do_remove(Status status) {}
353 }