aboutsummaryrefslogtreecommitdiff
path: root/libdino/src/ui/add_conversation
diff options
context:
space:
mode:
authorMarvin W <git@larma.de>2017-03-10 19:34:56 +0100
committerMarvin W <git@larma.de>2017-03-10 19:50:34 +0100
commit29ca70a6d534e1cd79963718c793ae740318cff1 (patch)
tree295bc0a88b9f31f103bc970fbdcd2d940d6c83e2 /libdino/src/ui/add_conversation
parentcf51e1dee22273366700c41a185c4bea343dddfe (diff)
downloaddino-29ca70a6d534e1cd79963718c793ae740318cff1.tar.gz
dino-29ca70a6d534e1cd79963718c793ae740318cff1.zip
Initial plugin system
Diffstat (limited to 'libdino/src/ui/add_conversation')
-rw-r--r--libdino/src/ui/add_conversation/chat/add_contact_dialog.vala67
-rw-r--r--libdino/src/ui/add_conversation/chat/dialog.vala82
-rw-r--r--libdino/src/ui/add_conversation/chat/roster_list.vala77
-rw-r--r--libdino/src/ui/add_conversation/conference/add_groupchat_dialog.vala107
-rw-r--r--libdino/src/ui/add_conversation/conference/conference_details_fragment.vala175
-rw-r--r--libdino/src/ui/add_conversation/conference/conference_list.vala105
-rw-r--r--libdino/src/ui/add_conversation/conference/dialog.vala166
-rw-r--r--libdino/src/ui/add_conversation/list_row.vala43
-rw-r--r--libdino/src/ui/add_conversation/select_jid_fragment.vala124
9 files changed, 946 insertions, 0 deletions
diff --git a/libdino/src/ui/add_conversation/chat/add_contact_dialog.vala b/libdino/src/ui/add_conversation/chat/add_contact_dialog.vala
new file mode 100644
index 00000000..1be0225b
--- /dev/null
+++ b/libdino/src/ui/add_conversation/chat/add_contact_dialog.vala
@@ -0,0 +1,67 @@
+using Gee;
+using Gtk;
+
+using Dino.Entities;
+
+namespace Dino.Ui.AddConversation.Chat {
+
+[GtkTemplate (ui = "/org/dino-im/add_conversation/add_contact_dialog.ui")]
+protected class AddContactDialog : Gtk.Dialog {
+
+ [GtkChild]
+ private ComboBoxText accounts_comboboxtext;
+
+ [GtkChild]
+ private Button ok_button;
+
+ [GtkChild]
+ private Button cancel_button;
+
+ [GtkChild]
+ private Entry jid_entry;
+
+ [GtkChild]
+ private Entry alias_entry;
+
+ [GtkChild]
+ private CheckButton subscribe_checkbutton;
+
+ private StreamInteractor stream_interactor;
+
+ public AddContactDialog(StreamInteractor stream_interactor) {
+ Object(use_header_bar : 1);
+ this.stream_interactor = stream_interactor;
+
+ foreach (Account account in stream_interactor.get_accounts()) {
+ accounts_comboboxtext.append_text(account.bare_jid.to_string());
+ }
+ accounts_comboboxtext.set_active(0);
+
+ cancel_button.clicked.connect(() => { close(); });
+ ok_button.clicked.connect(on_ok_button_clicked);
+ jid_entry.changed.connect(on_jid_entry_changed);
+ }
+
+ private void on_ok_button_clicked() {
+ string? alias = alias_entry.text == "" ? null : alias_entry.text;
+ Account? account = null;
+ Jid jid = new Jid(jid_entry.text);
+ foreach (Account account2 in stream_interactor.get_accounts()) {
+ print(account2.bare_jid.to_string() + "\n");
+ if (accounts_comboboxtext.get_active_text() == account2.bare_jid.to_string()) {
+ account = account2;
+ }
+ }
+ RosterManager.get_instance(stream_interactor).add_jid(account, jid, alias);
+ if (subscribe_checkbutton.active) {
+ PresenceManager.get_instance(stream_interactor).request_subscription(account, jid);
+ }
+ close();
+ }
+
+ private void on_jid_entry_changed() {
+ Jid parsed_jid = Jid.parse(jid_entry.text);
+ ok_button.set_sensitive(parsed_jid != null && parsed_jid.resourcepart == null);
+ }
+}
+} \ No newline at end of file
diff --git a/libdino/src/ui/add_conversation/chat/dialog.vala b/libdino/src/ui/add_conversation/chat/dialog.vala
new file mode 100644
index 00000000..80dac68e
--- /dev/null
+++ b/libdino/src/ui/add_conversation/chat/dialog.vala
@@ -0,0 +1,82 @@
+using Gee;
+using Gdk;
+using Gtk;
+
+using Dino.Entities;
+
+namespace Dino.Ui.AddConversation.Chat {
+
+public class Dialog : Gtk.Dialog {
+
+ public signal void conversation_opened(Conversation conversation);
+
+ private Button ok_button;
+
+ private RosterList roster_list;
+ private SelectJidFragment select_jid_fragment;
+ private StreamInteractor stream_interactor;
+
+ public Dialog(StreamInteractor stream_interactor) {
+ Object(use_header_bar : 1);
+ this.title = "Start Chat";
+ this.modal = true;
+ this.stream_interactor = stream_interactor;
+
+ setup_headerbar();
+ setup_view();
+ }
+
+ private void setup_headerbar() {
+ HeaderBar header_bar = get_header_bar() as HeaderBar;
+ header_bar.show_close_button = false;
+
+ Button cancel_button = new Button();
+ cancel_button.set_label("Cancel");
+ cancel_button.visible = true;
+ header_bar.pack_start(cancel_button);
+
+ ok_button = new Button();
+ ok_button.get_style_context().add_class("suggested-action");
+ ok_button.label = "Start";
+ ok_button.sensitive = false;
+ ok_button.visible = true;
+ header_bar.pack_end(ok_button);
+
+ cancel_button.clicked.connect(() => { close(); });
+ ok_button.clicked.connect(on_ok_button_clicked);
+ }
+
+ private void setup_view() {
+ roster_list = new RosterList(stream_interactor);
+ roster_list.row_activated.connect(() => { ok_button.clicked(); });
+ select_jid_fragment = new SelectJidFragment(stream_interactor, roster_list);
+ select_jid_fragment.add_jid.connect((row) => {
+ AddContactDialog add_contact_dialog = new AddContactDialog(stream_interactor);
+ add_contact_dialog.set_transient_for(this);
+ add_contact_dialog.show();
+ });
+ select_jid_fragment.edit_jid.connect(() => {
+
+ });
+ select_jid_fragment.remove_jid.connect((row) => {
+ ListRow list_row = roster_list.get_selected_row() as ListRow;
+ RosterManager.get_instance(stream_interactor).remove_jid(list_row.account, list_row.jid);
+ });
+ select_jid_fragment.notify["done"].connect(() => {
+ ok_button.sensitive = select_jid_fragment.done;
+ });
+ get_content_area().add(select_jid_fragment);
+ }
+
+ protected void on_ok_button_clicked() {
+ ListRow? selected_row = roster_list.get_selected_row() as ListRow;
+ if (selected_row != null) {
+ // TODO move in list to front immediately
+ ConversationManager.get_instance(stream_interactor).ensure_start_conversation(selected_row.jid, selected_row.account);
+ Conversation conversation = ConversationManager.get_instance(stream_interactor).get_conversation(selected_row.jid, selected_row.account);
+ conversation_opened(conversation);
+ }
+ close();
+ }
+}
+} \ No newline at end of file
diff --git a/libdino/src/ui/add_conversation/chat/roster_list.vala b/libdino/src/ui/add_conversation/chat/roster_list.vala
new file mode 100644
index 00000000..9e970d8c
--- /dev/null
+++ b/libdino/src/ui/add_conversation/chat/roster_list.vala
@@ -0,0 +1,77 @@
+using Gee;
+using Gtk;
+
+using Dino.Entities;
+using Xmpp;
+
+namespace Dino.Ui.AddConversation.Chat {
+protected class RosterList : FilterableList {
+
+ public signal void conversation_selected(Conversation? conversation);
+ private StreamInteractor stream_interactor;
+
+ private HashMap<Jid, ListRow> rows = new HashMap<Jid, ListRow>(Jid.hash_func, Jid.equals_func);
+
+ public RosterList(StreamInteractor stream_interactor) {
+ this.stream_interactor = stream_interactor;
+
+ set_filter_func(filter);
+ set_header_func(header);
+ set_sort_func(sort);
+
+ RosterManager.get_instance(stream_interactor).removed_roster_item.connect( (account, jid, roster_item) => {
+ Idle.add(() => { on_removed_roster_item(account, jid, roster_item); return false;});});
+ RosterManager.get_instance(stream_interactor).updated_roster_item.connect( (account, jid, roster_item) => {
+ Idle.add(() => { on_updated_roster_item(account, jid, roster_item); return false;});});
+
+ foreach (Account account in stream_interactor.get_accounts()) {
+ foreach (Roster.Item roster_item in RosterManager.get_instance(stream_interactor).get_roster(account)) {
+ on_updated_roster_item(account, new Jid(roster_item.jid), roster_item);
+ }
+ }
+ }
+
+ private void on_removed_roster_item(Account account, Jid jid, Roster.Item roster_item) {
+ if (rows.has_key(jid)) {
+ remove(rows[jid]);
+ rows.unset(jid);
+ }
+ }
+
+ private void on_updated_roster_item(Account account, Jid jid, Roster.Item roster_item) {
+ on_removed_roster_item(account, jid, roster_item);
+ ListRow row = new ListRow.from_jid(stream_interactor, new Jid(roster_item.jid), account);
+ rows[jid] = row;
+ add(row);
+ invalidate_sort();
+ invalidate_filter();
+ }
+
+ private void header(ListBoxRow row, ListBoxRow? before_row) {
+ if (row.get_header() == null && before_row != null) {
+ row.set_header(new Separator(Orientation.HORIZONTAL));
+ }
+ }
+
+ private bool filter(ListBoxRow r) {
+ if (r.get_type().is_a(typeof(ListRow))) {
+ ListRow row = r as ListRow;
+ if (filter_values != null) {
+ foreach (string filter in filter_values) {
+ if (!(row.name_label.label.down().contains(filter.down()) ||
+ row.jid.to_string().down().contains(filter.down()))) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ public override int sort(ListBoxRow row1, ListBoxRow row2) {
+ ListRow c1 = (row1 as ListRow);
+ ListRow c2 = (row2 as ListRow);
+ return c1.name_label.label.collate(c2.name_label.label);
+ }
+}
+} \ No newline at end of file
diff --git a/libdino/src/ui/add_conversation/conference/add_groupchat_dialog.vala b/libdino/src/ui/add_conversation/conference/add_groupchat_dialog.vala
new file mode 100644
index 00000000..05589fe1
--- /dev/null
+++ b/libdino/src/ui/add_conversation/conference/add_groupchat_dialog.vala
@@ -0,0 +1,107 @@
+using Gee;
+using Gtk;
+
+using Dino.Entities;
+
+namespace Dino.Ui.AddConversation.Conference {
+
+[GtkTemplate (ui = "/org/dino-im/add_conversation/add_groupchat_dialog.ui")]
+protected class AddGroupchatDialog : Gtk.Dialog {
+
+ [GtkChild]
+ private Stack accounts_stack;
+
+ [GtkChild]
+ private ComboBoxText accounts_comboboxtext;
+
+ [GtkChild]
+ private Label account_label;
+
+ [GtkChild]
+ private Button ok_button;
+
+ [GtkChild]
+ private Button cancel_button;
+
+ [GtkChild]
+ private Entry jid_entry;
+
+ [GtkChild]
+ private Entry alias_entry;
+
+ [GtkChild]
+ private Entry nick_entry;
+
+ [GtkChild]
+ private CheckButton autojoin_checkbutton;
+
+ private StreamInteractor stream_interactor;
+ private Xmpp.Xep.Bookmarks.Conference? edit_confrence = null;
+ private bool alias_entry_changed = false;
+
+ public AddGroupchatDialog(StreamInteractor stream_interactor) {
+ Object(use_header_bar : 1);
+ this.stream_interactor = stream_interactor;
+ ok_button.label = "Add";
+ ok_button.get_style_context().add_class("suggested-action"); // TODO why doesn't it work in XML
+ accounts_stack.set_visible_child_name("combobox");
+ foreach (Account account in stream_interactor.get_accounts()) {
+ accounts_comboboxtext.append_text(account.bare_jid.to_string());
+ }
+ accounts_comboboxtext.set_active(0);
+
+ cancel_button.clicked.connect(() => { close(); });
+ ok_button.clicked.connect(on_ok_button_clicked);
+ jid_entry.key_release_event.connect(on_jid_key_release);
+ nick_entry.key_release_event.connect(check_ok);
+ }
+
+ public AddGroupchatDialog.for_conference(StreamInteractor stream_interactor, Account account, Xmpp.Xep.Bookmarks.Conference conference) {
+ this(stream_interactor);
+ edit_confrence = conference;
+ ok_button.label = "Save";
+ ok_button.sensitive = true;
+ accounts_stack.set_visible_child_name("label");
+ account_label.label = account.bare_jid.to_string();
+ jid_entry.text = conference.jid;
+ nick_entry.text = conference.nick;
+ autojoin_checkbutton.active = conference.autojoin;
+ alias_entry.text = conference.name;
+ }
+
+ private bool on_jid_key_release() {
+ check_ok();
+ if (!alias_entry_changed) {
+ Jid? parsed_jid = Jid.parse(jid_entry.text);
+ alias_entry.text = parsed_jid != null && parsed_jid.localpart != null ? parsed_jid.localpart : jid_entry.text;
+ }
+ return false;
+ }
+
+ private bool check_ok() {
+ Jid? parsed_jid = Jid.parse(jid_entry.text);
+ ok_button.sensitive = parsed_jid != null && parsed_jid.localpart != null && parsed_jid.resourcepart == null &&
+ nick_entry.text != "" && alias_entry.text != null;
+ return false;
+ }
+
+ private void on_ok_button_clicked() {
+ Account? account = null;
+ foreach (Account account2 in stream_interactor.get_accounts()) {
+ if (accounts_comboboxtext.get_active_text() == account2.bare_jid.to_string()) {
+ account = account2;
+ }
+ }
+ Xmpp.Xep.Bookmarks.Conference conference = new Xmpp.Xep.Bookmarks.Conference(jid_entry.text);
+ conference.nick = nick_entry.text;
+ conference.name = alias_entry.text;
+ conference.autojoin = autojoin_checkbutton.active;
+ if (edit_confrence == null) {
+ MucManager.get_instance(stream_interactor).add_bookmark(account, conference);
+ } else {
+ MucManager.get_instance(stream_interactor).replace_bookmark(account, edit_confrence, conference);
+ }
+ close();
+ }
+}
+} \ No newline at end of file
diff --git a/libdino/src/ui/add_conversation/conference/conference_details_fragment.vala b/libdino/src/ui/add_conversation/conference/conference_details_fragment.vala
new file mode 100644
index 00000000..324c133d
--- /dev/null
+++ b/libdino/src/ui/add_conversation/conference/conference_details_fragment.vala
@@ -0,0 +1,175 @@
+using Gdk;
+using Gtk;
+
+using Dino.Entities;
+
+namespace Dino.Ui.AddConversation.Conference {
+
+[GtkTemplate (ui = "/org/dino-im/add_conversation/conference_details_fragment.ui")]
+protected class ConferenceDetailsFragment : Box {
+
+ public bool done {
+ get {
+ Jid? parsed_jid = Jid.parse(jid);
+ return parsed_jid != null && parsed_jid.localpart != null &&
+ parsed_jid.resourcepart == null && nick != "";
+ }
+ private set {}
+ }
+
+ public Account account {
+ owned get {
+ foreach (Account account in stream_interactor.get_accounts()) {
+ if (accounts_comboboxtext.get_active_text() == account.bare_jid.to_string()) {
+ return account;
+ }
+ }
+ return null;
+ }
+ set {
+ accounts_label.label = value.bare_jid.to_string();
+ accounts_comboboxtext.set_active_id(value.bare_jid.to_string());
+ }
+ }
+ public string jid {
+ get { return jid_entry.text; }
+ set {
+ jid_label.label = value;
+ jid_entry.text = value;
+ }
+ }
+ public string nick {
+ get { return nick_entry.text; }
+ set {
+ nick_label.label = value;
+ nick_entry.text = value;
+ }
+ }
+ public string password {
+ get { return password_entry.text == "" ? null : password_entry.text; }
+ set {
+ password_label.label = value;
+ password_entry.text = value;
+ }
+ }
+
+ [GtkChild]
+ private Stack accounts_stack;
+
+ [GtkChild]
+ private Stack jid_stack;
+
+ [GtkChild]
+ private Stack nick_stack;
+
+ [GtkChild]
+ private Stack password_stack;
+
+ [GtkChild]
+ private Button accounts_button;
+
+ [GtkChild]
+ private Button jid_button;
+
+ [GtkChild]
+ private Button nick_button;
+
+ [GtkChild]
+ private Button password_button;
+
+ [GtkChild]
+ private Label accounts_label;
+
+ [GtkChild]
+ private Label jid_label;
+
+ [GtkChild]
+ private Label nick_label;
+
+ [GtkChild]
+ private Label password_label;
+
+ [GtkChild]
+ private ComboBoxText accounts_comboboxtext;
+
+ [GtkChild]
+ private Entry jid_entry;
+
+ [GtkChild]
+ private Entry nick_entry;
+
+ [GtkChild]
+ private Entry password_entry;
+
+ private StreamInteractor stream_interactor;
+
+ public ConferenceDetailsFragment(StreamInteractor stream_interactor) {
+ this.stream_interactor = stream_interactor;
+
+ accounts_stack.set_visible_child_name("label");
+ jid_stack.set_visible_child_name("label");
+ nick_stack.set_visible_child_name("label");
+ password_stack.set_visible_child_name("label");
+
+ accounts_button.clicked.connect(() => { set_active_stack(accounts_stack); });
+ jid_button.clicked.connect(() => { set_active_stack(jid_stack); });
+ nick_button.clicked.connect(() => { set_active_stack(nick_stack); });
+ password_button.clicked.connect(() => { set_active_stack(password_stack); });
+
+ accounts_comboboxtext.changed.connect(() => {accounts_label.label = accounts_comboboxtext.get_active_text(); });
+ jid_entry.key_release_event.connect(on_jid_key_release_event);
+ nick_entry.key_release_event.connect(on_nick_key_release_event);
+ password_entry.key_release_event.connect(on_password_key_release_event);
+
+ jid_entry.key_release_event.connect(() => { done = true; return false; }); // just for notifying
+ nick_entry.key_release_event.connect(() => { done = true; return false; });
+
+ foreach (Account account in stream_interactor.get_accounts()) {
+ accounts_comboboxtext.append_text(account.bare_jid.to_string());
+ }
+ accounts_comboboxtext.set_active(0);
+ }
+
+ public void set_editable() {
+ accounts_stack.set_visible_child_name("entry");
+ nick_stack.set_visible_child_name("entry");
+ password_stack.set_visible_child_name("entry");
+ }
+
+ public void clear() {
+ jid = "";
+ nick = "";
+ password = "";
+ }
+
+ private bool on_jid_key_release_event(EventKey event) {
+ jid_label.label = jid_entry.text;
+ if (event.keyval == Key.Return) jid_stack.set_visible_child_name("label");
+ return false;
+ }
+
+ private bool on_nick_key_release_event(EventKey event) {
+ nick_label.label = nick_entry.text;
+ if (event.keyval == Key.Return) nick_stack.set_visible_child_name("label");
+ return false;
+ }
+
+ private bool on_password_key_release_event(EventKey event) {
+ string filler = "";
+ for (int i = 0; i < password_entry.text.length; i++) filler += password_entry.get_invisible_char().to_string();
+ password_label.label = filler;
+ if (event.keyval == Key.Return) password_stack.set_visible_child_name("label");
+ return false;
+ }
+
+ private void set_active_stack(Stack stack) {
+ stack.set_visible_child_name("entry");
+ if (stack != accounts_stack) accounts_stack.set_visible_child_name("label");
+ if (stack != jid_stack) jid_stack.set_visible_child_name("label");
+ if (stack != nick_stack) nick_stack.set_visible_child_name("label");
+ if (stack != password_stack) password_stack.set_visible_child_name("label");
+ }
+
+}
+
+} \ No newline at end of file
diff --git a/libdino/src/ui/add_conversation/conference/conference_list.vala b/libdino/src/ui/add_conversation/conference/conference_list.vala
new file mode 100644
index 00000000..2e461472
--- /dev/null
+++ b/libdino/src/ui/add_conversation/conference/conference_list.vala
@@ -0,0 +1,105 @@
+using Gee;
+using Gtk;
+
+using Xmpp;
+using Dino.Entities;
+
+namespace Dino.Ui.AddConversation.Conference {
+protected class ConferenceList : FilterableList {
+
+ public signal void conversation_selected(Conversation? conversation);
+
+ private StreamInteractor stream_interactor;
+ private HashMap<Account, ArrayList<Xep.Bookmarks.Conference>> lists = new HashMap<Account, ArrayList<Xep.Bookmarks.Conference>>();
+
+ public ConferenceList(StreamInteractor stream_interactor) {
+ this.stream_interactor = stream_interactor;
+
+ set_filter_func(filter);
+ set_header_func(header);
+ set_sort_func(sort);
+
+ MucManager.get_instance(stream_interactor).bookmarks_updated.connect((account, conferences) => {
+ Idle.add(() => {
+ lists[account] = conferences;
+ refresh_conferences();
+ return false;
+ });
+ });
+
+ foreach (Account account in stream_interactor.get_accounts()) {
+ MucManager.get_instance(stream_interactor).get_bookmarks(account, new BookmarksListener(this, stream_interactor, account));
+ }
+ }
+
+ public void refresh_conferences() {
+ @foreach((widget) => { remove(widget); });
+ foreach (Account account in lists.keys) {
+ foreach (Xep.Bookmarks.Conference conference in lists[account]) {
+ add(new ConferenceListRow(stream_interactor, conference, account));
+ }
+ }
+ }
+
+ private class BookmarksListener : Xep.Bookmarks.ConferencesRetrieveResponseListener, Object {
+ ConferenceList outer;
+ Account account;
+ public BookmarksListener(ConferenceList outer, StreamInteractor stream_interactor, Account account) {
+ this.outer = outer;
+ this.account = account;
+ }
+
+ public void on_result(Core.XmppStream stream, ArrayList<Xep.Bookmarks.Conference> conferences) {
+ outer.lists[account] = conferences;
+ Idle.add(() => { outer.refresh_conferences(); return false; });
+ }
+ }
+
+ private void header(ListBoxRow row, ListBoxRow? before_row) {
+ if (row.get_header() == null && before_row != null) {
+ row.set_header(new Separator(Orientation.HORIZONTAL));
+ }
+ }
+
+ private bool filter(ListBoxRow r) {
+ if (r.get_type().is_a(typeof(ListRow))) {
+ ListRow row = r as ListRow;
+ if (filter_values != null) {
+ foreach (string filter in filter_values) {
+ if (!(row.name_label.label.down().contains(filter.down()) ||
+ row.jid.to_string().down().contains(filter.down()))) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ public override int sort(ListBoxRow row1, ListBoxRow row2) {
+ ListRow c1 = (row1 as ListRow);
+ ListRow c2 = (row2 as ListRow);
+ return c1.name_label.label.collate(c2.name_label.label);
+ }
+}
+
+internal class ConferenceListRow : ListRow {
+
+ public Xep.Bookmarks.Conference bookmark;
+
+ public ConferenceListRow(StreamInteractor stream_interactor, Xep.Bookmarks.Conference bookmark, Account account) {
+ this.jid = new Jid(bookmark.jid);
+ this.account = account;
+ this.bookmark = bookmark;
+
+ if (bookmark.name != "" && bookmark.name != bookmark.jid) {
+ name_label.label = bookmark.name;
+ via_label.label = bookmark.jid;
+ } else {
+ name_label.label = bookmark.jid;
+ via_label.visible = false;
+ }
+ image.set_from_pixbuf((new AvatarGenerator(35, 35)).set_stateless(true).draw_jid(stream_interactor, jid, account));
+ }
+}
+} \ No newline at end of file
diff --git a/libdino/src/ui/add_conversation/conference/dialog.vala b/libdino/src/ui/add_conversation/conference/dialog.vala
new file mode 100644
index 00000000..ff548699
--- /dev/null
+++ b/libdino/src/ui/add_conversation/conference/dialog.vala
@@ -0,0 +1,166 @@
+using Gee;
+using Gtk;
+
+using Dino.Entities;
+
+namespace Dino.Ui.AddConversation.Conference {
+
+public class Dialog : Gtk.Dialog {
+
+ public signal void conversation_opened(Conversation conversation);
+
+ private Stack stack = new Stack();
+ private Button cancel_button;
+ private Button ok_button;
+ private Label cancel_label = new Label("Cancel") {visible=true};
+ private Image cancel_image = new Image.from_icon_name("go-previous-symbolic", IconSize.MENU) {visible=true};
+
+ private SelectJidFragment select_fragment;
+ private ConferenceDetailsFragment details_fragment;
+ private ConferenceList conference_list;
+
+ private StreamInteractor stream_interactor;
+
+ public Dialog(StreamInteractor stream_interactor) {
+ Object(use_header_bar : 1);
+ this.title = "Join Conference";
+ this.modal = true;
+ this.stream_interactor = stream_interactor;
+
+ stack.visible = true;
+ stack.vhomogeneous = false;
+ get_content_area().add(stack);
+
+ setup_headerbar();
+ setup_jid_add_view();
+ setup_conference_details_view();
+ show_jid_add_view();
+ }
+
+ private void show_jid_add_view() {
+ cancel_button.remove(cancel_image);
+ cancel_button.add(cancel_label);
+ cancel_button.clicked.disconnect(show_jid_add_view);
+ cancel_button.clicked.connect(close);
+ ok_button.label = "Next";
+ ok_button.sensitive = select_fragment.done;
+ ok_button.clicked.disconnect(on_ok_button_clicked);
+ ok_button.clicked.connect(on_next_button_clicked);
+ details_fragment.notify["done"].disconnect(set_ok_sensitive_from_details);
+ select_fragment.notify["done"].connect(set_ok_sensitive_from_select);
+ stack.transition_type = StackTransitionType.SLIDE_RIGHT;
+ stack.set_visible_child_name("select");
+ }
+
+ private void show_conference_details_view() {
+ cancel_button.remove(cancel_label);
+ cancel_button.add(cancel_image);
+ cancel_button.clicked.disconnect(close);
+ cancel_button.clicked.connect(show_jid_add_view);
+ ok_button.label = "Join";
+ ok_button.sensitive = details_fragment.done;
+ ok_button.clicked.disconnect(on_next_button_clicked);
+ ok_button.clicked.connect(on_ok_button_clicked);
+ select_fragment.notify["done"].disconnect(set_ok_sensitive_from_select);
+ details_fragment.notify["done"].connect(set_ok_sensitive_from_details);
+ stack.transition_type = StackTransitionType.SLIDE_LEFT;
+ stack.set_visible_child_name("details");
+ animate_window_resize();
+ }
+
+ private void setup_headerbar() {
+ HeaderBar header_bar = get_header_bar() as HeaderBar;
+ header_bar.show_close_button = false;
+
+ cancel_button = new Button();
+ header_bar.pack_start(cancel_button);
+ cancel_button.visible = true;
+
+ ok_button = new Button();
+ header_bar.pack_end(ok_button);
+ ok_button.get_style_context().add_class("suggested-action");
+ ok_button.visible = true;
+ ok_button.can_focus = true;
+ ok_button.can_default = true;
+ ok_button.has_default = true;
+ }
+
+ private void setup_jid_add_view() {
+ conference_list = new ConferenceList(stream_interactor);
+ conference_list.row_activated.connect(() => { ok_button.clicked(); });
+ select_fragment = new SelectJidFragment(stream_interactor, conference_list);
+ select_fragment.add_jid.connect((row) => {
+ AddGroupchatDialog dialog = new AddGroupchatDialog(stream_interactor);
+ dialog.set_transient_for(this);
+ dialog.show();
+ });
+ select_fragment.edit_jid.connect((row) => {
+ ConferenceListRow conference_row = row as ConferenceListRow;
+ AddGroupchatDialog dialog = new AddGroupchatDialog.for_conference(stream_interactor, conference_row.account, conference_row.bookmark);
+ dialog.set_transient_for(this);
+ dialog.show();
+ });
+ select_fragment.remove_jid.connect((row) => {
+ ConferenceListRow conference_row = row as ConferenceListRow;
+ MucManager.get_instance(stream_interactor).remove_bookmark(conference_row.account, conference_row.bookmark);
+ });
+ stack.add_named(select_fragment, "select");
+ }
+
+ private void setup_conference_details_view() {
+ details_fragment = new ConferenceDetailsFragment(stream_interactor);
+ stack.add_named(details_fragment, "details");
+ }
+
+ private void set_ok_sensitive_from_select() {
+ ok_button.sensitive = select_fragment.done;
+ }
+
+ private void set_ok_sensitive_from_details() {
+ ok_button.sensitive = select_fragment.done;
+ }
+
+ private void on_next_button_clicked() {
+ details_fragment.clear();
+ ListRow? row = conference_list.get_selected_row() as ListRow;
+ ConferenceListRow? conference_row = conference_list.get_selected_row() as ConferenceListRow;
+ if (conference_row != null) {
+ details_fragment.jid = conference_row.bookmark.jid;
+ details_fragment.nick = conference_row.bookmark.nick;
+ if (conference_row.bookmark.password != null) details_fragment.password = conference_row.bookmark.password;
+ ok_button.grab_focus();
+ } else if (row != null) {
+ details_fragment.jid = row.jid.to_string();
+ details_fragment.set_editable();
+ }
+ show_conference_details_view();
+ }
+
+ private void on_ok_button_clicked() {
+ MucManager.get_instance(stream_interactor).join(details_fragment.account, new Jid(details_fragment.jid), details_fragment.nick, details_fragment.password);
+ close();
+ }
+
+ private void close() {
+ base.close();
+ }
+
+ private void animate_window_resize() {
+ int def_height, curr_width, curr_height;
+ get_size(out curr_width, out curr_height);
+ stack.get_preferred_height(null, out def_height);
+ int difference = def_height - curr_height;
+ Timer timer = new Timer();
+ Timeout.add((int) (stack.transition_duration / 30),
+ () => {
+ ulong microsec;
+ timer.elapsed(out microsec);
+ ulong millisec = microsec / 1000;
+ double partial = double.min(1, (double) millisec / stack.transition_duration);
+ resize(curr_width, (int) (curr_height + difference * partial));
+ return millisec < stack.transition_duration;
+ });
+ }
+}
+
+} \ No newline at end of file
diff --git a/libdino/src/ui/add_conversation/list_row.vala b/libdino/src/ui/add_conversation/list_row.vala
new file mode 100644
index 00000000..5c2eff97
--- /dev/null
+++ b/libdino/src/ui/add_conversation/list_row.vala
@@ -0,0 +1,43 @@
+using Gee;
+using Gtk;
+
+using Dino.Entities;
+
+namespace Dino.Ui.AddConversation {
+
+[GtkTemplate (ui = "/org/dino-im/add_conversation/list_row.ui")]
+public class ListRow : ListBoxRow {
+
+ [GtkChild]
+ public Image image;
+
+ [GtkChild]
+ public Label name_label;
+
+ [GtkChild]
+ public Label via_label;
+
+ public Jid? jid;
+ public Account? account;
+
+ public ListRow() {}
+
+ public ListRow.from_jid(StreamInteractor stream_interactor, Jid jid, Account account) {
+ this.jid = jid;
+ this.account = account;
+
+ string display_name = Util.get_display_name(stream_interactor, jid, account);
+ if (stream_interactor.get_accounts().size > 1) {
+ via_label.label = @"via $(account.bare_jid)";
+ this.has_tooltip = true;
+ set_tooltip_text(jid.to_string());
+ } else if (display_name != jid.bare_jid.to_string()){
+ via_label.label = jid.bare_jid.to_string();
+ } else {
+ via_label.visible = false;
+ }
+ name_label.label = display_name;
+ image.set_from_pixbuf((new AvatarGenerator(35, 35)).draw_jid(stream_interactor, jid, account));
+ }
+}
+} \ No newline at end of file
diff --git a/libdino/src/ui/add_conversation/select_jid_fragment.vala b/libdino/src/ui/add_conversation/select_jid_fragment.vala
new file mode 100644
index 00000000..847a9ecb
--- /dev/null
+++ b/libdino/src/ui/add_conversation/select_jid_fragment.vala
@@ -0,0 +1,124 @@
+using Gee;
+using Gtk;
+
+using Dino.Entities;
+
+namespace Dino.Ui.AddConversation {
+
+[GtkTemplate (ui = "/org/dino-im/add_conversation/select_jid_fragment.ui")]
+public class SelectJidFragment : Gtk.Box {
+
+ public signal void add_jid();
+ public signal void edit_jid(ListRow row);
+ public signal void remove_jid(ListRow row);
+ public bool done {
+ get {
+ return filterable_list.get_selected_row() != null;
+ }
+ private set {} }
+
+ [GtkChild]
+ private Entry entry;
+
+ [GtkChild]
+ private Box box;
+
+ [GtkChild]
+ private Button add_button;
+
+ [GtkChild]
+ private Button edit_button;
+
+ [GtkChild]
+ private Button remove_button;
+
+ private FilterableList filterable_list;
+ private ArrayList<AddListRow> added_rows = new ArrayList<AddListRow>();
+ private StreamInteractor stream_interactor;
+
+ public SelectJidFragment(StreamInteractor stream_interactor, FilterableList filterable_list) {
+ this.stream_interactor = stream_interactor;
+ this.filterable_list = filterable_list;
+
+ filterable_list.visible = true;
+ filterable_list.activate_on_single_click = false;
+ filterable_list.vexpand = true;
+ box.add(filterable_list);
+
+ filterable_list.set_sort_func(sort);
+ filterable_list.row_selected.connect(check_buttons_active);
+ filterable_list.row_selected.connect(() => { done = true; }); // just for notifying
+ entry.changed.connect(on_entry_changed);
+ add_button.clicked.connect(() => { add_jid(); });
+ remove_button.clicked.connect(() => { remove_jid(filterable_list.get_selected_row() as ListRow); });
+ edit_button.clicked.connect(() => { edit_jid(filterable_list.get_selected_row() as ListRow); });
+ }
+
+ private void on_entry_changed() {
+ foreach (AddListRow row in added_rows) {
+ filterable_list.remove(row);
+ }
+ added_rows.clear();
+
+ string[] ? values;
+ string str = entry.get_text();
+ values = str == "" ? null : str.split(" ");
+ filterable_list.set_filter_values(values);
+ Jid? parsed_jid = Jid.parse(str);
+ if (parsed_jid != null && parsed_jid.localpart != null) {
+ foreach (Account account in stream_interactor.get_accounts()) {
+ AddListRow row = new AddListRow(stream_interactor, str, account);
+ filterable_list.add(row);
+ added_rows.add(row);
+ }
+ }
+ }
+
+ private void check_buttons_active() {
+ ListBoxRow? row = filterable_list.get_selected_row();
+ bool active = row != null && !row.get_type().is_a(typeof(AddListRow));
+ edit_button.sensitive = active;
+ remove_button.sensitive = active;
+ }
+
+ private int sort(ListBoxRow row1, ListBoxRow row2) {
+ AddListRow al1 = (row1 as AddListRow);
+ AddListRow al2 = (row2 as AddListRow);
+ if (al1 != null && al2 == null) {
+ return -1;
+ } else if (al2 != null && al1 == null) {
+ return 1;
+ }
+ return filterable_list.sort(row1, row2);
+ }
+
+ private class AddListRow : ListRow {
+
+ public AddListRow(StreamInteractor stream_interactor, string jid, Account account) {
+ this.account = account;
+ this.jid = new Jid(jid);
+
+ name_label.label = jid;
+ if (stream_interactor.get_accounts().size > 1) {
+ via_label.label = account.bare_jid.to_string();
+ } else {
+ via_label.visible = false;
+ }
+ image.set_from_pixbuf((new AvatarGenerator(35, 35)).set_greyscale(true).draw_text("?"));
+ }
+ }
+}
+
+public abstract class FilterableList : Gtk.ListBox {
+ public string[]? filter_values;
+
+ public void set_filter_values(string[] values) {
+ if (filter_values == values) return;
+ filter_values = values;
+ invalidate_filter();
+ }
+
+ public abstract int sort(ListBoxRow row1, ListBoxRow row2);
+}
+
+} \ No newline at end of file