diff --git a/libdino/src/application.vala b/libdino/src/application.vala index 4b5fd274..9cabf0f4 100644 --- a/libdino/src/application.vala +++ b/libdino/src/application.vala @@ -29,10 +29,28 @@ public class Dino.Application : Gtk.Application { RosterManager.start(stream_interaction); ConversationManager.start(stream_interaction, db); ChatInteraction.start(stream_interaction); + + activate.connect(() => { + restore(); + }); } public static string get_storage_dir() { return Path.build_filename(Environment.get_user_data_dir(), "dino"); } + + protected void add_connection(Account account) { + stream_interaction.connect(account); + } + + protected void remove_connection(Account account) { + stream_interaction.disconnect(account); + } + + private void restore() { + foreach (Account account in db.get_accounts()) { + if (account.enabled) add_connection(account); + } + } } diff --git a/libdino/src/service/conversation_manager.vala b/libdino/src/service/conversation_manager.vala index ada9ee9d..10c6ac51 100644 --- a/libdino/src/service/conversation_manager.vala +++ b/libdino/src/service/conversation_manager.vala @@ -9,6 +9,7 @@ public class ConversationManager : StreamInteractionModule, Object { public string id { get { return IDENTITY.id; } } public signal void conversation_activated(Conversation conversation); + public signal void conversation_deactivated(Conversation conversation); private StreamInteractor stream_interactor; private Database db; @@ -37,6 +38,16 @@ public class ConversationManager : StreamInteractionModule, Object { return null; } + public Gee.List get_active_conversations() { + ArrayList ret = new ArrayList(Conversation.equals_func); + foreach (Account account in conversations.keys) { + foreach (Conversation conversation in conversations[account].values) { + if(conversation.active) ret.add(conversation); + } + } + return ret; + } + public Conversation get_add_conversation(Jid jid, Account account) { ensure_add_conversation(jid, account, Conversation.Type.CHAT); return get_conversation(jid, account); @@ -52,7 +63,11 @@ public class ConversationManager : StreamInteractionModule, Object { conversation_activated(conversation); } } + } + public void close_conversation(Conversation conversation) { + conversation.active = false; + conversation_deactivated(conversation); } private void on_account_added(Account account) { diff --git a/libdino/src/service/stream_interactor.vala b/libdino/src/service/stream_interactor.vala index bdc1ac96..81ddfebd 100644 --- a/libdino/src/service/stream_interactor.vala +++ b/libdino/src/service/stream_interactor.vala @@ -72,7 +72,7 @@ public class ModuleIdentity : Object { } public T? cast(StreamInteractionModule module) { - return (T?) module; + return module.get_type().is_a(typeof(T)) ? (T?) module : null; } public bool matches(StreamInteractionModule module) { diff --git a/main/data/chat_input.ui b/main/data/chat_input.ui index 455286ab..f8a1c2f7 100644 --- a/main/data/chat_input.ui +++ b/main/data/chat_input.ui @@ -10,8 +10,10 @@ 300 True + True + True GTK_WRAP_WORD_CHAR diff --git a/main/data/conversation_summary/view.ui b/main/data/conversation_summary/view.ui index d455c888..d00314fc 100644 --- a/main/data/conversation_summary/view.ui +++ b/main/data/conversation_summary/view.ui @@ -2,9 +2,9 @@ diff --git a/main/src/ui/application.vala b/main/src/ui/application.vala index e6f01822..e78fa90a 100644 --- a/main/src/ui/application.vala +++ b/main/src/ui/application.vala @@ -13,13 +13,12 @@ public class Dino.Ui.Application : Dino.Application { notifications.start(); Environment.set_application_name("Dino"); IconTheme.get_default().add_resource_path("/org/dino-im/icons"); - } - public override void activate() { - create_set_app_menu(); - window = new UnifiedWindow(this, stream_interaction); - window.show_all(); - restore(); + activate.connect(() => { + create_set_app_menu(); + window = new UnifiedWindow(this, stream_interaction); + window.show(); + }); } private void show_accounts_window() { @@ -55,19 +54,5 @@ public class Dino.Ui.Application : Dino.Application { set_app_menu(menu); } - - private void restore() { - foreach (Account account in db.get_accounts()) { - if (account.enabled) add_connection(account); - } - } - - private void add_connection(Account account) { - stream_interaction.connect(account); - } - - private void remove_connection(Account account) { - stream_interaction.disconnect(account); - } } diff --git a/main/src/ui/conversation_selector/conversation_row.vala b/main/src/ui/conversation_selector/conversation_row.vala index c16da287..6930db67 100644 --- a/main/src/ui/conversation_selector/conversation_row.vala +++ b/main/src/ui/conversation_selector/conversation_row.vala @@ -125,7 +125,7 @@ public abstract class ConversationRow : ListBoxRow { main_revealer.set_reveal_child(false); closed(); main_revealer.notify["child-revealed"].connect(() => { - conversation.active = false; + stream_interactor.get_module(ConversationManager.IDENTITY).close_conversation(conversation); disappeared(); }); } diff --git a/main/src/ui/conversation_selector/list.vala b/main/src/ui/conversation_selector/list.vala index d95128f1..dee70e4b 100644 --- a/main/src/ui/conversation_selector/list.vala +++ b/main/src/ui/conversation_selector/list.vala @@ -76,6 +76,17 @@ public class List : ListBox { foreach (ConversationRow row in rows.values) row.update(); return true; }); + + foreach (Conversation conversation in stream_interactor.get_module(ConversationManager.IDENTITY).get_active_conversations()) { + add_conversation(conversation); + } + realize.connect(() => { + ListBoxRow? first_row = get_row_at_index(0); + if (first_row != null) { + select_row(first_row); + row_activated(first_row); + } + }); } public override void row_activated(ListBoxRow r) { @@ -117,15 +128,14 @@ public class List : ListBox { } rows[conversation] = row; add(row); - row.closed.connect(() => { on_conversation_closed(conversation); }); - row.disappeared.connect(() => { on_conversation_disappeared(conversation); }); + row.closed.connect(() => { select_next_conversation(conversation); }); + row.disappeared.connect(() => { remove_conversation(conversation); }); row.main_revealer.set_reveal_child(true); } invalidate_sort(); - queue_draw(); } - private void on_conversation_closed(Conversation conversation) { + private void select_next_conversation(Conversation conversation) { if (get_selected_row() == rows[conversation]) { int index = rows[conversation].get_index(); ListBoxRow? index_p1 = get_row_at_index(index + 1); @@ -142,7 +152,7 @@ public class List : ListBox { } } - private void on_conversation_disappeared(Conversation conversation) { + private void remove_conversation(Conversation conversation) { if (rows.has_key(conversation) && !conversation.active) { remove(rows[conversation]); rows.unset(conversation); diff --git a/main/src/ui/conversation_selector/view.vala b/main/src/ui/conversation_selector/view.vala index 5c1b4dfd..304c6bbe 100644 --- a/main/src/ui/conversation_selector/view.vala +++ b/main/src/ui/conversation_selector/view.vala @@ -15,7 +15,7 @@ public class View : Box { [GtkChild] private ScrolledWindow scrolled; public View(StreamInteractor stream_interactor) { - conversation_list = new List(stream_interactor); + conversation_list = new List(stream_interactor) { visible=true }; scrolled.add(conversation_list); search_entry.key_release_event.connect(search_key_release_event); search_entry.search_changed.connect(search_changed); diff --git a/main/src/ui/unified_window.vala b/main/src/ui/unified_window.vala index 8feb9a05..2eb4317a 100644 --- a/main/src/ui/unified_window.vala +++ b/main/src/ui/unified_window.vala @@ -7,16 +7,17 @@ namespace Dino.Ui { public class UnifiedWindow : Window { - private UnifiedWindowPlaceholder main_placeholder = new UnifiedWindowPlaceholder(); + private NoAccountsPlaceholder accounts_placeholder = new NoAccountsPlaceholder() { visible=true }; + private NoConversationsPlaceholder conversations_placeholder = new NoConversationsPlaceholder() { visible=true }; private ChatInput chat_input; private ConversationListTitlebar conversation_list_titlebar; private ConversationSelector.View filterable_conversation_list; private ConversationSummary.View conversation_frame; private ConversationTitlebar conversation_titlebar; - private Paned headerbar_paned = new Paned(Orientation.HORIZONTAL); - private Paned paned = new Paned(Orientation.HORIZONTAL); - private Stack headerbar_stack = new Stack(); - private Stack stack = new Stack(); + private Paned headerbar_paned = new Paned(Orientation.HORIZONTAL) { visible=true }; + private Paned paned = new Paned(Orientation.HORIZONTAL) { visible=true }; + private Stack headerbar_stack = new Stack() { visible=true }; + private Stack stack = new Stack() { visible=true }; private StreamInteractor stream_interactor; private Conversation? conversation; @@ -27,7 +28,7 @@ public class UnifiedWindow : Window { setup_headerbar(); setup_unified(); - setup_stacks(); + setup_stack(); conversation_list_titlebar.search_button.bind_property("active", filterable_conversation_list.search_bar, "search-mode-enabled", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); @@ -38,7 +39,11 @@ public class UnifiedWindow : Window { stream_interactor.account_added.connect((account) => { check_stack(true); }); stream_interactor.account_removed.connect((account) => { check_stack(); }); - main_placeholder.no_accounts_add.clicked.connect(() => { get_application().activate_action("accounts", null); }); + stream_interactor.get_module(ConversationManager.IDENTITY).conversation_activated.connect((conversation) => { check_stack(); }); + stream_interactor.get_module(ConversationManager.IDENTITY).conversation_deactivated.connect((conversation) => { check_stack(); }); + accounts_placeholder.primary_button.clicked.connect(() => { get_application().activate_action("accounts", null); }); + conversations_placeholder.primary_button.clicked.connect(() => { get_application().activate_action("add_chat", null); }); + conversations_placeholder.secondary_button.clicked.connect(() => { get_application().activate_action("add_conference", null); }); filterable_conversation_list.conversation_list.conversation_selected.connect(on_conversation_selected); conversation_list_titlebar.conversation_opened.connect(on_conversation_selected); @@ -46,54 +51,49 @@ public class UnifiedWindow : Window { } private void setup_unified() { - chat_input = new ChatInput(stream_interactor); - conversation_frame = new ConversationSummary.View(stream_interactor); - filterable_conversation_list = new ConversationSelector.View(stream_interactor); + chat_input = new ChatInput(stream_interactor) { visible=true }; + conversation_frame = new ConversationSummary.View(stream_interactor) { visible=true }; + filterable_conversation_list = new ConversationSelector.View(stream_interactor) { visible=true }; - Grid grid = new Grid() { orientation=Orientation.VERTICAL }; + Grid grid = new Grid() { orientation=Orientation.VERTICAL, visible=true }; grid.add(conversation_frame); - grid.add(new Separator(Orientation.HORIZONTAL)); + grid.add(new Separator(Orientation.HORIZONTAL) { visible=true }); grid.add(chat_input); paned.set_position(300); paned.add1(filterable_conversation_list); paned.add2(grid); - - conversation_frame.show_all(); } private void setup_headerbar() { - conversation_titlebar = new ConversationTitlebar(stream_interactor); - conversation_list_titlebar = new ConversationListTitlebar(this, stream_interactor); + conversation_titlebar = new ConversationTitlebar(stream_interactor) { visible=true }; + conversation_list_titlebar = new ConversationListTitlebar(this, stream_interactor) { visible=true }; headerbar_paned.add1(conversation_list_titlebar); headerbar_paned.add2(conversation_titlebar); } - private void setup_stacks() { + private void setup_stack() { stack.add_named(paned, "main"); - stack.add_named(main_placeholder, "placeholder"); + stack.add_named(accounts_placeholder, "accounts_placeholder"); + stack.add_named(conversations_placeholder, "conversations_placeholder"); add(stack); headerbar_stack.add_named(headerbar_paned, "main"); - headerbar_stack.add_named(new HeaderBar() { title="Dino", show_close_button=true, visible=true}, "placeholder"); + headerbar_stack.add_named(new HeaderBar() { title="Dino", show_close_button=true, visible=true }, "placeholder"); set_titlebar(headerbar_stack); } private void check_stack(bool know_exists = false) { ArrayList accounts = stream_interactor.get_accounts(); - bool exists_active = know_exists; - foreach (Account account in accounts) { - if (account.enabled) { - exists_active = true; - break; - } - } - if (exists_active) { + if (!know_exists && accounts.size == 0) { + stack.set_visible_child_name("accounts_placeholder"); + headerbar_stack.set_visible_child_name("placeholder"); + } else if (stream_interactor.get_module(ConversationManager.IDENTITY).get_active_conversations().size == 0) { + stack.set_visible_child_name("conversations_placeholder"); + headerbar_stack.set_visible_child_name("placeholder"); + } else { stack.set_visible_child_name("main"); headerbar_stack.set_visible_child_name("main"); - } else { - stack.set_visible_child_name("placeholder"); - headerbar_stack.set_visible_child_name("placeholder"); } } @@ -119,9 +119,28 @@ public class UnifiedWindow : Window { } } +public class NoAccountsPlaceholder : UnifiedWindowPlaceholder { + public NoAccountsPlaceholder() { + label.label = "No accounts active"; + primary_button.label = "Manage accounts"; + secondary_button.visible = false; + } +} + +public class NoConversationsPlaceholder : UnifiedWindowPlaceholder { + public NoConversationsPlaceholder() { + label.label = "No conversation active"; + primary_button.label = "Add Chat"; + secondary_button.label = "Join Conference"; + secondary_button.visible = true; + } +} + [GtkTemplate (ui = "/org/dino-im/unified_window_placeholder.ui")] public class UnifiedWindowPlaceholder : Box { - [GtkChild] public Button no_accounts_add; + [GtkChild] public Label label; + [GtkChild] public Button primary_button; + [GtkChild] public Button secondary_button; } } \ No newline at end of file diff --git a/qlite/src/row.vala b/qlite/src/row.vala index de10751f..ed0552a0 100644 --- a/qlite/src/row.vala +++ b/qlite/src/row.vala @@ -53,6 +53,7 @@ public class Row { } public class RowIterator { + private Database db; private Statement stmt; public RowIterator.from_query_builder(QueryBuilder query) throws DatabaseError { @@ -60,6 +61,7 @@ public class RowIterator { } public RowIterator(Database db, string sql, string[]? args = null) throws DatabaseError { + this.db = db; this.stmt = db.prepare(sql); if (args != null) { for (int i = 0; i < args.length; i++) { @@ -68,11 +70,11 @@ public class RowIterator { } } - public Row? next_value() { - if (stmt.step() == Sqlite.ROW) { - return new Row(stmt); - } - return null; + public Row? next_value() throws DatabaseError { + int r = stmt.step(); + if (r == Sqlite.ROW) return new Row(stmt); + if (r == Sqlite.DONE) return null; + throw new DatabaseError.EXEC_ERROR(@"SQLite error: $(db.errcode()) - $(db.errmsg())"); } } diff --git a/xmpp-vala/src/core/xmpp_stream.vala b/xmpp-vala/src/core/xmpp_stream.vala index f3be115d..0283920b 100644 --- a/xmpp-vala/src/core/xmpp_stream.vala +++ b/xmpp-vala/src/core/xmpp_stream.vala @@ -237,8 +237,8 @@ public class FlagIdentity : Object { this.id = id; } - public T? cast(XmppStreamFlag module) { - return (T?) module; + public T? cast(XmppStreamFlag flag) { + return flag.get_type().is_a(typeof(T)) ? (T?) flag : null; } public bool matches(XmppStreamFlag module) { @@ -246,7 +246,7 @@ public class FlagIdentity : Object { } } -public abstract class XmppStreamFlag { +public abstract class XmppStreamFlag : Object { public abstract string get_ns(); public abstract string get_id(); } @@ -261,7 +261,7 @@ public class ModuleIdentity : Object { } public T? cast(XmppStreamModule module) { - return (T?) module; + return module.get_type().is_a(typeof(T)) ? (T?) module : null; } public bool matches(XmppStreamModule module) {