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