diff --git a/.gitmodules b/.gitmodules index 68a610eb4977df35eb8718a0e9b47dc5ddf7b994..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +0,0 @@ -[submodule "subprojects/gtk-frdp"] - path = subprojects/gtk-frdp - url = https://gitlab.gnome.org/gnome/gtk-frdp.git - branch = master diff --git a/src/actions-popover.vala b/src/actions-popover.vala index b06687babf3d857442b5f9be8522a7a08bab5c8d..4f3f9a77290509897f01a3ce042a813bfaa638d8 100644 --- a/src/actions-popover.vala +++ b/src/actions-popover.vala @@ -20,8 +20,8 @@ */ namespace Connections { - private class ActionsPopover : Gtk.Popover { - private GLib.SimpleActionGroup action_group; + private class ActionsPopover { + public GLib.SimpleActionGroup action_group; private const GLib.ActionEntry[] action_entries = { {"delete", delete_activated}, {"properties", properties_activated} @@ -29,26 +29,13 @@ namespace Connections { private Connections.Connection connection; - construct { + public ActionsPopover() { action_group = new GLib.SimpleActionGroup (); action_group.add_action_entries (action_entries, this); - this.insert_action_group ("connection", action_group); } public void update_for_item (Connection connection) { this.connection = connection; - - var menu = new GLib.Menu (); - - var action = action_group.lookup_action ("delete") as GLib.SimpleAction; - menu.append (_("Delete"), "connection.delete"); - action.set_enabled (true); - - action = action_group.lookup_action ("properties") as GLib.SimpleAction; - menu.append (_("Properties"), "connection.properties"); - action.set_enabled (true); - - bind_model (menu, null); } private void delete_activated () { diff --git a/src/application.vala b/src/application.vala index ce2dee9f8c6a282515f7e34676def89eac38d75a..dd2aff27383338a45b250c69626c9a670c6a5954 100644 --- a/src/application.vala +++ b/src/application.vala @@ -22,7 +22,7 @@ using Config; namespace Connections { - private class Application : Gtk.Application { + private class Application : Adw.Application { public static Application application; public ListStore model; @@ -38,6 +38,9 @@ namespace Connections { set { settings.set_boolean ("first-run", value); } } + private Connection pending_connection = null; + public string latest_screenshot; + construct { windows = new List (); model = new GLib.ListStore (typeof (Connections.Connection)); @@ -62,44 +65,78 @@ namespace Connections { action = new GLib.SimpleAction ("quit", null); action.activate.connect (quit_app); add_action (action); + + set_accels_for_action("app.help", { "F1" }); + set_accels_for_action("app.quit", { "q" }); + set_accels_for_action("win.toggle-fullscreen", { "F11" }); + set_accels_for_action("win.toggle-search-mode", { "f" }); + set_accels_for_action("win.assistant-popdown", { "n" }); + + action = new GLib.SimpleAction ("undo-delete", null); + action.activate.connect (() => + { + debug ("Connection deletion cancelled by user. Re-adding to view"); + var conn = pending_connection; + pending_connection = null; + if (conn != null) + { + Application.application.model.insert (0, conn); + } + }); + add_action (action); + + action = new GLib.SimpleAction ("open-screenshot", null); + action.activate.connect (() => + { + debug ("Opening screenshot file"); + try { + Gtk.show_uri (Application.application.main_window, + File.new_for_path (latest_screenshot).get_uri () + ".png", + Gdk.CURRENT_TIME); + } catch (GLib.Error error) { + warning ("Failed to open screenshot: %s", error.message); + } + }); + add_action (action); } private void show_help () { try { - Gtk.show_uri_on_window (main_window, + Gtk.show_uri (main_window, "help:gnome-connections", - Gtk.get_current_event_time ()); + Gdk.CURRENT_TIME); } catch (GLib.Error error) { warning ("Failed to display help: %s", error.message); } } private void show_about_dialog () { - string[] authors = { + string[] developers = { "Felipe Borges " }; - string[] artists = { + string[] designers = { "Jakub Steiner " }; - Gtk.show_about_dialog (main_window, - "artists", artists, - "authors", authors, - "translator-credits", _("translator-credits"), - "comments", _("A remote desktop client for the GNOME desktop environment"), - "copyright", "\xc2\xa9 2020 Red Hat, Inc.", - "license-type", Gtk.License.GPL_3_0, - "program-name", _("Connections"), - "wrap-license", true, - "logo-icon-name", application_id, - "version", Config.VERSION); + var about = new Adw.AboutWindow() { + designers = designers, + developers = developers, + translator_credits = _("translator-credits"), + copyright = "\xc2\xa9 2020 Red Hat, Inc.", + license_type = Gtk.License.GPL_3_0, + application_name = _("Connections"), + application_icon = application_id, + version = Config.VERSION + }; + about.show(); } public override void startup () { + set_resource_base_path ("/org/gnome/Connections/"); base.startup (); - Hdy.init (); - Hdy.StyleManager.get_default ().color_scheme = PREFER_DARK; + Adw.init (); + Adw.StyleManager.get_default ().color_scheme = PREFER_DARK; } public override void activate () { @@ -159,17 +196,7 @@ namespace Connections { } public void remove_connection (Connection connection) { - Notification.OKFunc undo = () => { - debug ("Connection deletion cancelled by user. Re-adding to view"); - model.insert (0, connection); - }; - - Notification.DismissFunc really_remove = () => { - debug ("User did not cancel deletion. Deleting now..."); - connection.disconnect_it (); - connection.delete_auth_credentials.begin (); - Database.get_default ().delete_connection (connection); - }; + pending_connection = connection; for (int i = 0; i < model.get_n_items (); i++) { if ((model.get_item (i) as Connection) == connection) { @@ -181,10 +208,22 @@ namespace Connections { var message = _("Connection to “%s” has been deleted").printf (connection.display_name != "" && connection.display_name != null ? connection.display_name : connection.uri); - main_window.notifications_bar.display_for_action (message, - _("Undo"), - (owned) undo, - (owned) really_remove); + + var toast = new Adw.Toast(message); + toast.set_button_label(_("Undo")); + toast.set_action_name("app.undo-delete"); + toast.dismissed.connect(() => + { + var conn = pending_connection; + if (conn != null) + { + debug ("User did not cancel deletion. Deleting now..."); + connection.disconnect_it (); + connection.delete_auth_credentials.begin (); + Database.get_default ().delete_connection (connection); + } + }); + main_window.add_toast(toast); } public void load_connections () { @@ -277,3 +316,4 @@ namespace Connections { } } } + diff --git a/src/assistant.vala b/src/assistant.vala index b4e5e71bb4021704045f1c90ad280a9c02f416dc..6ed976aef710c595cb6c2e5a4bfb0295b2d958c8 100644 --- a/src/assistant.vala +++ b/src/assistant.vala @@ -27,9 +27,9 @@ namespace Connections { [GtkChild] private unowned Gtk.Button create_button; [GtkChild] - private unowned Gtk.RadioButton rdp_radio_button; + private unowned Gtk.ToggleButton rdp_radio_button; [GtkChild] - private unowned Gtk.RadioButton vnc_radio_button; + private unowned Gtk.ToggleButton vnc_radio_button; private bool uri_has_supported_scheme (string uri) { return uri.has_prefix ("rdp://") || uri.has_prefix ("vnc://"); @@ -92,9 +92,8 @@ namespace Connections { [GtkCallback] private void on_help_button_clicked () { try { - Gtk.show_uri_on_window (Application.application.main_window, - "help:gnome-connections/connect", - Gtk.get_current_event_time ()); + var launcher = new Gtk.UriLauncher("help:gnome-connections/connect"); + launcher.launch (Application.application.main_window, null); } catch (GLib.Error error) { warning ("Failed to display help: %s", error.message); } diff --git a/src/auth-widget.vala b/src/auth-widget.vala new file mode 100644 index 0000000000000000000000000000000000000000..b5378383286f700b61d30b047f7d1c2f02b4fb1a --- /dev/null +++ b/src/auth-widget.vala @@ -0,0 +1,38 @@ +/* auth-widget.vala + * + * Copyright (C) Red Hat, Inc + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Author: Felipe Borges + * + */ + +namespace Connections { + + [GtkTemplate (ui = "/org/gnome/Connections/ui/auth-widget.ui")] + private class AuthWidget : Adw.Bin { + + [GtkChild] + public unowned Gtk.Entry username; + + [GtkChild] + public unowned Gtk.Entry password; + + public AuthWidget () { + + } + + } +} diff --git a/src/collection-view-child.vala b/src/collection-view-child.vala index 1d4c0c5849d0d7956426845ebb24e9a406c3b5e6..6f4d9da5d119a5b2951e8a7b350dc1c972ed6784 100644 --- a/src/collection-view-child.vala +++ b/src/collection-view-child.vala @@ -21,14 +21,17 @@ namespace Connections { [GtkTemplate (ui = "/org/gnome/Connections/ui/collection-view-child.ui")] - private class CollectionViewChild : Gtk.Box { + private class CollectionViewChild : Adw.Bin { public Connection connection; [GtkChild] private unowned Gtk.Label connection_name; [GtkChild] - public unowned Gtk.Image thumbnail; + public unowned Gtk.Image thumbnail; + + [GtkChild] + public unowned Gtk.GestureClick child_click; private uint thumbnail_id = 0; @@ -50,8 +53,7 @@ namespace Connections { thumbnail_id = 0; if (!connection.connected) { - thumbnail.set_from_icon_name ("org.gnome.Connections-symbolic", - Gtk.IconSize.LARGE_TOOLBAR); + thumbnail.set_from_icon_name ("org.gnome.Connections-symbolic"); return Source.REMOVE; } diff --git a/src/collection-view-filter.vala b/src/collection-view-filter.vala new file mode 100644 index 0000000000000000000000000000000000000000..8cfa264dbebf7b5510920ad396a525756b762674 --- /dev/null +++ b/src/collection-view-filter.vala @@ -0,0 +1,46 @@ +/* collection-view.vala + * + * Copyright (C) Red Hat, Inc + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Author: Felipe Borges + * + */ + +namespace Connections { + private class CollectionViewFilter : Gtk.Filter { + + private string filter; + + public void change_filter(string filter) { + this.filter = filter; + changed(Gtk.FilterChange.DIFFERENT); + } + + public override bool match (Object? item) { + var child = item as Connection; + + if (child != null) { + return filter == null + || filter == "" + || child.uri.contains (filter) + || child.display_name.contains (filter); + } + + return false; + } + + } +} diff --git a/src/collection-view.vala b/src/collection-view.vala index bb5e1d1a6909e8fad565f20504df5ce171c5ddb0..791819946c3f173833ab186a17d89eb981dfa020 100644 --- a/src/collection-view.vala +++ b/src/collection-view.vala @@ -21,63 +21,69 @@ namespace Connections { [GtkTemplate (ui = "/org/gnome/Connections/ui/collection-view.ui")] - public class CollectionView : Gtk.ScrolledWindow { + public class CollectionView : Adw.Bin { [GtkChild] - private unowned Gtk.FlowBox flowbox; + private unowned Gtk.GridView gridview; + [GtkChild] + private unowned Gtk.PopoverMenu gridview_popover; [GtkChild] public unowned Gtk.SearchBar search_bar; [GtkChild] private unowned Gtk.SearchEntry search_entry; - private Connections.ActionsPopover popover; + private Connections.ActionsPopover popover_wrapper; + private ListModel model; + private CollectionViewFilter filter; construct { - popover = new Connections.ActionsPopover (); + popover_wrapper = new Connections.ActionsPopover (); + this.insert_action_group ("connection", popover_wrapper.action_group); + + var factory = new Gtk.SignalListItemFactory(); + factory.bind.connect(bind_cb); + gridview.set_factory(factory); - flowbox.set_filter_func (model_filter); + filter = new CollectionViewFilter(); } public void bind_model (ListModel model) { - flowbox.bind_model (model, create_child); + this.model = model; + var filter_model = new Gtk.FilterListModel(model, filter); + var no_select_model = new Gtk.NoSelection(filter_model); + gridview.set_model (no_select_model); } - private Gtk.Widget create_child (Object item) { - var child = new Gtk.FlowBoxChild (); - child.halign = Gtk.Align.START; + public void bind_cb(Gtk.ListItem item) { + var conn = item.get_item(); + var box = new CollectionViewChild (conn as Connection); + box.child_click.released.connect(on_button_release_event); + + item.set_child(box); + } - var box = new CollectionViewChild (item as Connection); - child.add (box); + private void on_button_release_event (Gtk.GestureClick source, int n_press, double x, double y) { + var child = source.get_widget() as CollectionViewChild; - return child; + if (child != null) { + launch_context_popover_for_child (child); + } } [GtkCallback] - private void on_child_activated (Gtk.FlowBoxChild child) { - var item = child.get_child () as CollectionViewChild; + private void on_child_activated (uint position) { + var item = model.get_item(position) as Connection; if (item == null) return; - Application.application.open_connection (item.connection); - } - - [GtkCallback] - private bool on_button_release_event (Gdk.EventButton event) { - if (event.type != Gdk.EventType.BUTTON_RELEASE || event.button != 3) - return false; - - var child = flowbox.get_child_at_pos ((int) event.x, (int) event.y); - - return launch_context_popover_for_child (child); + Application.application.open_connection (item); } - private bool launch_context_popover_for_child (Gtk.FlowBoxChild child) { - var item = child.get_child () as CollectionViewChild; - if (item == null) - return false; - - popover.update_for_item (item.connection); - popover.set_relative_to (item.thumbnail); - popover.show (); + internal bool launch_context_popover_for_child (CollectionViewChild child) { + popover_wrapper.update_for_item (child.connection); + Graphene.Rect rect; + child.thumbnail.compute_bounds (this, out rect); + gridview_popover.set_pointing_to ({ (int) rect.get_x(), (int) rect.get_y(), (int) rect.get_width(), (int) rect.get_height() }); + gridview_popover.show (); return true; } @@ -96,7 +102,7 @@ namespace Connections { [GtkCallback] private void on_search_changed () { - flowbox.invalidate_filter (); + filter.change_filter(search_entry.text); } } } diff --git a/src/connection.vala b/src/connection.vala index eb90c07661e89d715af026ff1f62d33fc1d0273e..e9df33162a961bd493644d800ea6028aa8b8dd4b 100644 --- a/src/connection.vala +++ b/src/connection.vala @@ -21,6 +21,16 @@ namespace Connections { private abstract class Connection : GLib.Object { + public enum Bandwidth { + HIGH_QUALITY, + FAST_REFRESH + } + + public enum ScaleMode { + FIT_WINDOW, + ORIGINAL_SIZE + } + public string uuid; public signal void show (); @@ -71,23 +81,15 @@ namespace Connections { var path = Path.build_filename (Environment.get_user_special_dir (GLib.UserDirectory.PICTURES), filename); thumbnail.save (path + ".png", "png"); + Application.application.latest_screenshot = path; - Notification.OKFunc open = () => { - debug ("Opening screenshot file"); - try { - Gtk.show_uri_on_window (Application.application.main_window, - File.new_for_path (path).get_uri () + ".png", - Gdk.CURRENT_TIME); - } catch (GLib.Error error) { - warning ("Failed to open screenshot: %s", error.message); - } - }; var message = _("Screenshot taken"); + + var toast = new Adw.Toast(message); // Translators: Open is a verb - Application.application.main_window.notifications_bar.display_for_action (message, - _("Open"), - (owned) open, - null); + toast.set_button_label(_("Open")); + toast.set_action_name("app.open-screenshot"); + Application.application.main_window.add_toast(toast); } catch (GLib.Error error) { warning ("Failed to take screenshot %s", error.message); } @@ -105,7 +107,7 @@ namespace Connections { }); } - private AuthNotification auth_notification = null; + private Adw.MessageDialog auth_notification = null; public bool need_password; public bool need_username; public string? username { get; set; } @@ -117,19 +119,6 @@ namespace Connections { if (!need_username && !need_password) return; - AuthNotification.AuthFunc auth_func = (username, password) => { - if (username != "") - this.username = username; - if (password != "") - this.password = password; - - Application.application.open_connection (this); - }; - - Notification.DismissFunc dismiss_func = () => { - auth_notification = null; - }; - Secret.password_lookup.begin (secret_auth_schema, auth_cancellable, (obj, res) => { try { var parsing_error = new IOError.FAILED("couldn't unpack a string for the connection credentials"); @@ -159,10 +148,33 @@ namespace Connections { debug ("No credentials found in keyring. Prompting user."); var auth_string = _("“%s” requires authentication").printf (get_visible_name ()); - auth_notification = Application.application.main_window.notifications_bar.display_for_auth (auth_string, - (owned) auth_func, - (owned) dismiss_func, - need_username); + + auth_notification = new Adw.MessageDialog(Application.application.main_window, + _("Authenticate"), + auth_string); + var auth_widget = new AuthWidget(); + auth_notification.set_extra_child(auth_widget); + auth_notification.add_response("ok", _("Ok")); + auth_notification.add_response("cancel", _("Cancel")); + auth_notification.response.connect((response) => { + if (response == "ok") { + var user = auth_widget.username.get_text(); + var pass = auth_widget.password.get_text(); + if (user != "" && pass != "") { + username = user; + password = pass; + } + + auth_notification.close(); + auth_notification = null; + Application.application.open_connection (this); + } else { + auth_notification.close(); + auth_notification = null; + Application.application.main_window.back_button_clicked(); + } + }); + auth_notification.show(); } }, "gnome-connections-connection-uuid", uuid); } @@ -177,7 +189,7 @@ namespace Connections { "gnome-connections-connection-uuid", uuid); if (auth_notification != null) { - auth_notification.dismiss (); + auth_notification.close (); auth_notification = null; } } catch (GLib.Error error) { @@ -186,7 +198,7 @@ namespace Connections { } protected void on_connection_error_cb (string reason) { - Application.application.main_window.notifications_bar.display_for_error (reason); + Application.application.main_window.add_toast(new Adw.Toast(reason)); Application.application.main_window.show_collection_view (); } @@ -199,7 +211,7 @@ namespace Connections { auth_notification = null; var message = _("Authentication failed: %s").printf (reason); Application.application.main_window.show_collection_view (); - Application.application.main_window.notifications_bar.display_for_error (message); + Application.application.main_window.add_toast(new Adw.Toast(message)); } construct { @@ -259,3 +271,4 @@ namespace Connections { public abstract void dispose_display (); } } + diff --git a/src/connections.gresource.xml b/src/connections.gresource.xml index 5304a1cfac6221ba2932fccd417dd0f1cea71da0..357990c68587c4c074f2b12954336789af059a86 100644 --- a/src/connections.gresource.xml +++ b/src/connections.gresource.xml @@ -1,16 +1,15 @@ - ui/auth-notification.ui + ui/auth-widget.ui ui/assistant.ui ui/collection-view.ui ui/collection-view-child.ui ui/display-view.ui - ui/notification.ui ui/rdp-preferences.ui ui/vnc-preferences.ui - ui/style.css - ui/topbar.ui + ui/style.css + ui/shortcuts.ui ui/window.ui diff --git a/src/database.vala b/src/database.vala index 30e10e84b48f18ce293ff8c4f64f7b79c4b3262e..c43228bea9049e5c113d8d2170b9261e85a07f07 100644 --- a/src/database.vala +++ b/src/database.vala @@ -129,6 +129,8 @@ namespace Connections { keyfile.set_boolean (connection.uuid, property_name, value.get_boolean ()); else if (value.type () == typeof (int)) keyfile.set_integer (connection.uuid, property_name, value.get_int ()); + else if (value.type () == typeof (uint)) + keyfile.set_uint64 (connection.uuid, property_name, value.get_uint ()); else if (value.type () == typeof (uint64)) keyfile.set_uint64 (connection.uuid, property_name, value.get_uint64 ()); else if (value.type () == typeof (int64)) diff --git a/src/display-view.vala b/src/display-view.vala index 2bc413bdf4237b20e275e0440b032c81a1428583..4e6378c2f13b5e473e83de1b2ed59047433df9fe 100644 --- a/src/display-view.vala +++ b/src/display-view.vala @@ -24,9 +24,9 @@ using Gdk; namespace Connections { [GtkTemplate (ui = "/org/gnome/Connections/ui/display-view.ui")] - private class DisplayView : Gtk.Box { + private class DisplayView : Adw.Bin { [GtkChild] - private unowned EventBox event_box; + private unowned Adw.Bin event_box; [GtkChild] private unowned Stack stack; @@ -41,13 +41,18 @@ namespace Connections { private ulong show_display_id; construct { - event_box.set_events (EventMask.POINTER_MOTION_MASK | EventMask.SCROLL_MASK); + var motion_controller = new Gtk.EventControllerMotion(); + motion_controller.motion.connect(on_motion); + add_controller(motion_controller); + + notify["default-width"].connect(on_size_changed); + notify["default-height"].connect(on_size_changed); } private void remove_display () { var widget = event_box.get_child (); if (widget != null) - event_box.remove (widget); + event_box.set_child (null); if (connection == null) return; @@ -65,20 +70,8 @@ namespace Connections { this.connection = connection; var display = connection.widget; - display.set_events (display.get_events () & ~EventMask.POINTER_MOTION_MASK); - event_box.add (display); - event_box.show_all (); - - ulong draw_id = 0; - draw_id = display.draw.connect (() => { - display.disconnect (draw_id); - - /*cursor_id = display.get_window ().notify["cursor"].connect (() => { - event_box.get_window ().set_cursor (display.get_window ().cursor); - });*/ - - return false; - }); + event_box.set_child (display); + event_box.show (); } public void connect_to (Connection connection) { @@ -99,31 +92,19 @@ namespace Connections { stack.set_visible_child_name ("display"); } - [GtkCallback] - private bool on_event_box_event (Gdk.Event event) { - if (event.type == EventType.GRAB_BROKEN) - return false; - - if (event_box.get_child () != null) - event_box.get_child ().event (event); - - return false; - } - private uint size_label_timeout; private int width = -1; private int height = -1; - [GtkCallback] - private void on_size_allocate (Gtk.Allocation allocation) { - if (width == allocation.width && height == allocation.height) { + private void on_size_changed () { + if (width == get_width() && height == get_width()) { return; } - width = allocation.width; - height = allocation.height; + width = get_width(); + height = get_height(); // Translators: Showing size of widget as WIDTH×HEIGHT here. - size_label.label = _("%d×%d").printf (allocation.width, allocation.height); + size_label.label = _("%d×%d").printf (width, height); Idle.add (() => { size_label.visible = true; @@ -150,13 +131,21 @@ namespace Connections { escape_fullscreen_button.visible = false; } - public override bool motion_notify_event (Gdk.EventMotion event) { - if (!Application.application.main_window.fullscreened) - return false; + public void on_motion (double x, double y) { + if (Application.application.main_window.fullscreened) + escape_fullscreen_button.visible = (y < 40); + + if (event_box.get_child () != null) { + var child = event_box.get_child (); + var offset_x = (get_allocated_width () - child.get_allocated_width ()) / 2.0; + var offset_y = (get_allocated_height () - child.get_allocated_height ()) / 2.0; - escape_fullscreen_button.visible = (event.y < 40); + x -= offset_x; + y -= offset_y; - return base.motion_notify_event (event); + // TODO how to forward event to child? + // child.event (event); + } } } } diff --git a/src/meson.build b/src/meson.build index b9bf0b463c8a7defa5f86b8b8ff72e634ab7ead1..eae1393faf370352f838bc4990b102e9f8954e4f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,20 +1,20 @@ connections_sources = [ 'actions-popover.vala', 'assistant.vala', + 'auth-widget.vala', 'application.vala', 'connection.vala', 'collection-view.vala', 'collection-view-child.vala', + 'collection-view-filter.vala', 'database.vala', 'display-view.vala', 'main.vala', - 'notifications.vala', 'onboarding-dialog.vala', 'onboarding-dialog-page.vala', 'preferences-widgets.vala', 'rdp-connection.vala', 'rdp-preferences-window.vala', - 'topbar.vala', 'vnc-connection.vala', 'vnc-preferences-window.vala', 'window.vala', @@ -27,26 +27,18 @@ connections_deps = [ valac.find_library ('config', dirs: src_dir), cc.find_library('m'), dependency('gio-2.0', version: '>= 2.50'), - dependency('gtk+-3.0', version: '>= 3.22'), - dependency('gtk-vnc-2.0', version: '> 0.4.4'), + dependency('atk'), + dependency('gtk4'), dependency('gvncpulse-1.0'), - dependency('libhandy-1', version: '>= 1.6.0'), + dependency('libadwaita-1', version: '>= 1.3'), dependency ('libxml-2.0', version: '>= 2.7.8'), dependency ('libsecret-1'), + dependency ('freerdp2'), + dependency ('rdw4'), + dependency ('rdw4-rdp'), + dependency ('rdw4-vnc'), ] -gtk_frdp_dep = dependency('gtk-frdp-0.2', required: false) - -if gtk_frdp_dep.found() - connections_deps += gtk_frdp_dep -else - gtk_frdp_dep = subproject('gtk-frdp', default_options: [ - 'package_subdir=' + meson.project_name() - ]) - - connections_deps += gtk_frdp_dep.get_variable('gtk_frdp_vapi') -endif - gnome = import('gnome') connections_sources += gnome.compile_resources('connections-resources', @@ -63,9 +55,9 @@ vala_args = [ '--disable-since-check', '--target-glib=2.50', ] - +new_inc = include_directories('/usr/include/freerdp2', '/usr/include/winpr2') executable('gnome-connections', connections_sources, - include_directories: config_h_dir, + include_directories: new_inc, vala_args: vala_args, c_args: c_args, dependencies: connections_deps, diff --git a/src/notifications.vala b/src/notifications.vala deleted file mode 100644 index 5b9b63faf21ead00398b93affad199919f04c73e..0000000000000000000000000000000000000000 --- a/src/notifications.vala +++ /dev/null @@ -1,237 +0,0 @@ -/* notifications.vala - * - * Copyright (C) Red Hat, Inc - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Author: Felipe Borges - * - */ - -namespace Connections { - private class NotificationsBar : Gtk.Bin { - public const int DEFAULT_TIMEOUT = 6; - private const int MAX_NOTIFICATIONS = 5; - - private Gtk.Widget? _active_notification; - public Gtk.Widget? active_notification { - get { - return _active_notification; - } - - set { - if (_active_notification != null) { - remove (_active_notification); - } - - _active_notification = value; - add (_active_notification); - } - } - - construct { - valign = Gtk.Align.START; - halign = Gtk.Align.CENTER; - get_style_context ().add_class ("transparent-bg"); - } - - public void display_for_error (string message) { - var notification = new Notification (message, null, null, null); - - active_notification = notification; - } - - public void display_for_action (string message, - string? ok_label, - owned Notification.OKFunc? ok_func, - owned Notification.DismissFunc? dismiss_func) { - var notification = new Notification (message, ok_label, (owned) ok_func, (owned) dismiss_func); - notification.dismissed.connect (() => { - active_notification = null; - }); - - active_notification = notification; - } - - public AuthNotification display_for_auth (string auth_string, - owned AuthNotification.AuthFunc? auth_func, - owned Notification.DismissFunc? dismiss_func, - bool need_username = true) { - var notification = new Connections.AuthNotification (auth_string, - (owned) auth_func, - (owned) dismiss_func, - need_username); - active_notification = notification; - - return notification; - } - - public void dismiss () { - if (active_notification != null) { - // TODO: Make AuthNotification derived from Notification - if (active_notification is Notification) { - Notification? notification = active_notification as Notification; - - notification.dismiss (); - } else if (active_notification is AuthNotification) { - AuthNotification? notification = active_notification as AuthNotification; - - notification.dismiss (); - } - - active_notification = null; - } - } - } - - [GtkTemplate (ui = "/org/gnome/Connections/ui/notification.ui")] - private class Notification : Gtk.Revealer { - public signal void dismissed (); - - public delegate void OKFunc (); - public delegate void DismissFunc (); - - private DismissFunc? dismiss_func; - - [GtkChild] - private unowned Gtk.Label message_label; - [GtkChild] - private unowned Gtk.Button ok_button; - - private uint notification_timeout_id = 0; - - public Notification (string message, - string? ok_label, - owned OKFunc? ok_func, - owned DismissFunc? dismiss_func, - int timeout = NotificationsBar.DEFAULT_TIMEOUT) { - this.dismiss_func = (owned)dismiss_func; - set_reveal_child (true); - - message_label.label = message; - - notification_timeout_id = Timeout.add_seconds (timeout, () => { - notification_timeout_id = 0; - dismiss (); - return Source.REMOVE; - }); - - if (ok_label != null) { - ok_button.label = ok_label; - - ok_button.clicked.connect (() => { - if (ok_func != null) - ok_func (); - - set_reveal_child (false); - dismissed (); - - if (notification_timeout_id != 0) { - Source.remove (notification_timeout_id); - notification_timeout_id = 0; - } - }); - - ok_button.show_all (); - } - } - - [GtkCallback] - public void dismiss () { - dismissed (); - set_reveal_child (false); - if (dismiss_func != null) - dismiss_func (); - - if (notification_timeout_id != 0) { - Source.remove (notification_timeout_id); - notification_timeout_id = 0; - } - } - } - - [GtkTemplate (ui = "/org/gnome/Connections/ui/auth-notification.ui")] - private class AuthNotification : Gtk.Revealer { - public delegate void AuthFunc (string username, string password); - private bool auth_pressed; - - public signal void dismissed (); - - [GtkChild] - private unowned Gtk.Label title_label; - [GtkChild] - private unowned Gtk.Label username_label; - [GtkChild] - private unowned Gtk.Entry username_entry; - [GtkChild] - private unowned Gtk.Entry password_entry; - [GtkChild] - private unowned Gtk.Button auth_button; - - private AuthFunc? auth_func; - - public AuthNotification (string auth_string, - owned AuthFunc? auth_func, - owned Notification.DismissFunc? dismiss_func, - bool need_username) { - set_reveal_child (true); - - title_label.label = auth_string; - - username_label.visible = need_username; - username_entry.visible = need_username; - - this.auth_func = (owned) auth_func; - - dismissed.connect (() => { - if (!auth_pressed && dismiss_func != null) - dismiss_func (); - }); - } - - [GtkCallback] - private void on_username_entry_map () { - username_entry.grab_focus (); - } - - [GtkCallback] - private void on_username_entry_activated () { - password_entry.grab_focus (); - } - - [GtkCallback] - private void on_password_entry_map () { - password_entry.grab_focus (); - } - - [GtkCallback] - private void on_password_entry_activated () { - auth_button.activate (); - } - - [GtkCallback] - private void on_auth_button_clicked () { - if (auth_func != null) - auth_func (username_entry.get_text (), password_entry.get_text ()); - auth_pressed = true; - - dismiss (); - } - - public void dismiss () { - set_reveal_child (false); - dismissed (); - } - } -} diff --git a/src/onboarding-dialog-page.vala b/src/onboarding-dialog-page.vala index 69b8ab76334677e4d81405b3397a84b5f491913e..2fdd83633a85c8162193e5d366edd5a2f4954f88 100644 --- a/src/onboarding-dialog-page.vala +++ b/src/onboarding-dialog-page.vala @@ -3,7 +3,7 @@ using Gtk; namespace Connections { [GtkTemplate (ui = "/org/gnome/Connections/ui/onboarding-dialog-page.ui")] - private class OnboardingDialogPage : Gtk.Box { + private class OnboardingDialogPage : Adw.Bin { [GtkChild] private unowned Label title_label; [GtkChild] @@ -38,7 +38,7 @@ namespace Connections { """.printf (image); try { - provider.load_from_data (css); + provider.load_from_data (css.data); get_style_context ().add_provider (provider, STYLE_PROVIDER_PRIORITY_APPLICATION); } catch (GLib.Error error) { warning ("Failed to load CSS: %s", error.message); diff --git a/src/onboarding-dialog.vala b/src/onboarding-dialog.vala index bd6780c76494aeef3896fbbe12224a1e3eb6695b..4fe88893ca2a1ce420ce2a501de30da7ff7a7e1e 100644 --- a/src/onboarding-dialog.vala +++ b/src/onboarding-dialog.vala @@ -20,11 +20,11 @@ */ using Gtk; -using Hdy; +using Adw; namespace Connections { [GtkTemplate (ui = "/org/gnome/Connections/ui/onboarding-dialog.ui")] - private class OnboardingDialog : Hdy.Window { + private class OnboardingDialog : Adw.Window { [GtkChild] private unowned Carousel paginator; [GtkChild] @@ -40,7 +40,8 @@ namespace Connections { pages = new GLib.List (); OnboardingDialogPage? onboarding_page = null; - foreach (var page in paginator.get_children ()) { + for (var i = 0; i < paginator.get_n_pages(); i++) { + var page = paginator.get_nth_page(i); assert (page is OnboardingDialogPage); onboarding_page = page as OnboardingDialogPage; @@ -60,7 +61,7 @@ namespace Connections { if (index >= pages.length ()) return; - paginator.scroll_to (pages.nth_data (index)); + paginator.scroll_to (pages.nth_data (index), true); } [GtkCallback] @@ -69,7 +70,7 @@ namespace Connections { if (index < 0) return; - paginator.scroll_to (pages.nth_data (index)); + paginator.scroll_to (pages.nth_data (index), true); } [GtkCallback] diff --git a/src/preferences-widgets.vala b/src/preferences-widgets.vala index 48cf4705e6c0ccc0c83976fcf0520251c5de92f9..b30be50beda60a559347b37f38b52a9f2fb51e5b 100644 --- a/src/preferences-widgets.vala +++ b/src/preferences-widgets.vala @@ -20,7 +20,7 @@ */ namespace Connections { - private class PreferencesWindow: Hdy.PreferencesWindow { + private class PreferencesWindow : Adw.PreferencesWindow { protected weak Connection connection; construct { @@ -36,7 +36,7 @@ namespace Connections { } } - private class BooleanProperty: Hdy.ActionRow { + private class BooleanProperty : Adw.ActionRow { public bool active { get; set; } construct { @@ -47,7 +47,7 @@ namespace Connections { bind_property ("active", widget, "active", BindingFlags.BIDIRECTIONAL); - add (widget); + add_suffix (widget); set_activatable_widget (widget); } } diff --git a/src/rdp-connection.vala b/src/rdp-connection.vala index b02841a49e15df43c85d7685cdc8dbd0bad80489..b644ad0f00d03e4cc749b8c94a2af4885398457b 100644 --- a/src/rdp-connection.vala +++ b/src/rdp-connection.vala @@ -21,10 +21,10 @@ namespace Connections { private class RdpConnection : Connection { - private FrdpDisplay display; + private RdpDisplay display; public override Gtk.Widget widget { set { - display = value as FrdpDisplay; + display = value as RdpDisplay; } get { @@ -43,7 +43,7 @@ namespace Connections { var surface = new Cairo.ImageSurface (ARGB32, alloc.width, alloc.height); var context = new Cairo.Context (surface); - widget.draw (context); + // widget.draw (context); return Gdk.pixbuf_get_from_surface (surface, 0, 0, alloc.width, alloc.height); } @@ -51,30 +51,26 @@ namespace Connections { public override bool scaling { set { - display.set_scaling (value); + // display.set_scaling (value); } get { if (!connected) return true; - return display.scaling; + return false; //display.scaling; } } public override int port { get; protected set; default = 3389; } construct { - display = new FrdpDisplay (); - display.hexpand = display.vexpand = true; - display.bind_property ("username", this, "username", BindingFlags.BIDIRECTIONAL); - display.bind_property ("password", this, "password", BindingFlags.BIDIRECTIONAL); + display = new RdpDisplay (); - display.rdp_error.connect (on_rdp_connection_error_cb); - display.rdp_connected.connect (on_rdp_connection_connected_cb); - //display.rdp_needs_authentication.connect (on_rdp_auth_credential_cb); - display.rdp_auth_failure.connect (auth_failed); - //display.size_allocate.connect (scale); + display.rdp_authenticate.connect (on_rdp_authenticate_cb); + display.notify["rdp-connected"].connect (on_rdp_connection_connected_cb); + // display.rdp_auth_failure.connect (auth_failed); + // display.size_allocate.connect (scale); need_username = need_password = true; } @@ -108,12 +104,24 @@ namespace Connections { } display.open_host (host, port); + + try { + display.connect_async(null, (obj, res) => { + on_rdp_connection_connected_cb (); + }); + } catch (GLib.Error err) { + on_connection_error_cb ("connect_async failed: " + err.message); + } } public override void disconnect_it () { - if (connected) - display.close (); - connected = false; + if (display.rdp_connected) { + try { + display.disconnect_async (null, (obj, res) => {}); + } catch (GLib.Error err) { + on_connection_error_cb ("disconnect_async failed: " + err.message); + } + } } public override void dispose_display () { @@ -122,10 +130,17 @@ namespace Connections { public override void send_keys (uint[] keyvals) { } - private void on_rdp_auth_credential_cb () { - need_username = need_password = true; + private bool on_rdp_authenticate_cb () { + // handle_auth should not be necessary here, has been called before in connect_it + if (username == null || username.length == 0 || password == null || password.length == 0) { + need_username = need_password = true; + + handle_auth (); + } + + display.set_creds(username, password); - handle_auth (); + return true; } private void on_rdp_connection_error_cb (string reason) { @@ -138,21 +153,41 @@ namespace Connections { } private void on_rdp_connection_connected_cb () { - connected = true; - scaling = true; + connected = display.rdp_connected; + if (connected) { + scaling = true; - display.grab_focus (); - show (); + display.grab_focus (); + show (); + } else { + var error_code = display.get_last_error(); + disconnect_it (); + var error_string = "connection closed"; + if (error_code > 0) { + error_string += ", error: " + error_code.to_string(); + } + on_connection_error_cb (error_string); + } } } - private class FrdpDisplay : Frdp.Display { - public override bool authenticate (out string username, out string password, out string domain) { - username = this.username; - password = this.password; - domain = null; + private class RdpDisplay : RdwRdp.Display { + + public RdpDisplay() { - return true; } + + public void open_host(string host, int port) { + unowned var settings = get_settings(); + settings.set_string(FreeRDP.Settings.Id.ServerHostname, host); + settings.set_uint32(FreeRDP.Settings.Id.ServerPort, port); + } + + public void set_creds(string username, string password) { + unowned var settings = get_settings(); + settings.set_string(FreeRDP.Settings.Id.Username, username); + settings.set_string(FreeRDP.Settings.Id.Password, password); + } + } } diff --git a/src/topbar.vala b/src/topbar.vala deleted file mode 100644 index 2afc5cd5214608306044586f17fc764fbfc94def..0000000000000000000000000000000000000000 --- a/src/topbar.vala +++ /dev/null @@ -1,156 +0,0 @@ -/* topbar.vala - * - * Copyright (C) Red Hat, Inc - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Author: Felipe Borges - * - */ - -namespace Connections { - [GtkTemplate (ui = "/org/gnome/Connections/ui/topbar.ui")] - private class Topbar : Gtk.Stack { - [GtkChild] - private unowned Hdy.HeaderBar collection_toolbar; - [GtkChild] - public unowned Gtk.Button search_button; - [GtkChild] - private unowned Gtk.HeaderBar display_toolbar; - [GtkChild] - public unowned Connections.Assistant assistant; - - private weak Connections.Connection connection; - private const GLib.ActionEntry[] action_entries = { - {"take_screenshot", take_screenshot_activated}, - {"fullscreen", fullscreen_activated}, - {"properties", properties_activated}, - }; - - private const GLib.ActionEntry[] key_input_action_entries = { - {"ctrl+alt+backspace", ctrl_alt_backspace_activated}, - {"ctrl+alt+del", ctrl_alt_del_activated}, - {"ctrl+alt+f1", ctrl_alt_fn_activated}, - {"ctrl+alt+f2", ctrl_alt_fn_activated}, - {"ctrl+alt+f3", ctrl_alt_fn_activated}, - {"ctrl+alt+f7", ctrl_alt_fn_activated}, - }; - - construct { - var action_group = new GLib.SimpleActionGroup (); - action_group.add_action_entries (action_entries, this); - this.insert_action_group ("display", action_group); - - var menu = new GLib.Menu (); - var section = new GLib.Menu (); - - section.append (_("Take Screenshot"), "display.take_screenshot"); - var action = action_group.lookup_action ("take_screenshot") as GLib.SimpleAction; - action.set_enabled (true); - - section.append (_("Fullscreen"), "display.fullscreen"); - action = action_group.lookup_action ("fullscreen") as GLib.SimpleAction; - action.set_enabled (true); - - section.append (_("Properties"), "display.properties"); - action = action_group.lookup_action ("properties") as GLib.SimpleAction; - action.set_enabled (true); - - var key_input_action_group = new GLib.SimpleActionGroup (); - key_input_action_group.add_action_entries (key_input_action_entries, this); - this.insert_action_group ("key", key_input_action_group); - } - - private void properties_activated () { - Application.application.main_window.show_preferences_window (connection); - } - - private void take_screenshot_activated () { - if (connection != null) - connection.take_screenshot.begin (); - } - - private void fullscreen_activated () { - Application.application.main_window.fullscreened = - !Application.application.main_window.fullscreened; - } - - [GtkCallback] - private void back_button_clicked () { - Application.application.main_window.show_collection_view (); - set_visible_child (collection_toolbar); - } - - [GtkCallback] - public void disconnect_button_clicked () { - connection.disconnect_it (); - - back_button_clicked (); - } - - public void show_collection_view () { - this.connection = null; - - set_visible_child (collection_toolbar); - } - - public void show_display_view (Connection connection) { - this.connection = connection; - - set_visible_child (display_toolbar); - - display_toolbar.set_title (connection.get_visible_name ()); - } - - public void set_title (string title) { - collection_toolbar.set_title (title); - } - - private void ctrl_alt_backspace_activated () { - uint[] keyvals = { Gdk.Key.Control_L, Gdk.Key.Alt_L, Gdk.Key.BackSpace }; - - send_keys (keyvals); - } - - private void ctrl_alt_del_activated () { - uint[] keyvals = { Gdk.Key.Control_L, Gdk.Key.Alt_L, Gdk.Key.Delete }; - - send_keys (keyvals); - } - - private void ctrl_alt_fn_activated (GLib.SimpleAction action) { - uint[] keyvals = { Gdk.Key.Control_L, Gdk.Key.Alt_L, 0 }; - - if (action.name[action.name.length - 1] == '1') - keyvals[2] = Gdk.Key.F1; - else if (action.name[action.name.length - 1] == '2') - keyvals[2] = Gdk.Key.F2; - else if (action.name[action.name.length - 1] == '3') - keyvals[3] = Gdk.Key.F3; - else if (action.name[action.name.length - 1] == '7') - keyvals[2] = Gdk.Key.F7; - else { - warn_if_reached (); - - return; - } - - send_keys (keyvals); - } - - private void send_keys (uint[] keyvals) { - connection.send_keys (keyvals); - } - } -} diff --git a/src/ui/assistant.ui b/src/ui/assistant.ui index a6a25ebefdedfbc7537a50cdb70c2bc4fed44af5..783eb08c11de232fb1e54b9db12531e879ccb0ec 100644 --- a/src/ui/assistant.ui +++ b/src/ui/assistant.ui @@ -1,140 +1,127 @@ - - - - - True - False - 0 - url_completion_model - - - - - - - - - vnc:// - - - rdp:// - - - - - - - - - - - + + + + 1 + 0 + 0 + url_completion_model + + + + + + + + vnc:// + + + rdp:// + + + + + + + + + + diff --git a/src/ui/auth-notification.ui b/src/ui/auth-notification.ui deleted file mode 100644 index 4a10443410bcc10db72fb2dabd5e5c4e620272eb..0000000000000000000000000000000000000000 --- a/src/ui/auth-notification.ui +++ /dev/null @@ -1,124 +0,0 @@ - - - - - diff --git a/src/ui/auth-widget.ui b/src/ui/auth-widget.ui new file mode 100644 index 0000000000000000000000000000000000000000..cc9e23c3aebd7a5135bd1a535d65b59473031121 --- /dev/null +++ b/src/ui/auth-widget.ui @@ -0,0 +1,48 @@ + + + + + diff --git a/src/ui/collection-view-child.ui b/src/ui/collection-view-child.ui index aa0bf678e77fc0a63a8c0c395c8e3856c5c3ff82..3961082433f0e00d05ced34b78c0144ede64a444 100644 --- a/src/ui/collection-view-child.ui +++ b/src/ui/collection-view-child.ui @@ -1,44 +1,47 @@ - - - + + + diff --git a/src/ui/collection-view.ui b/src/ui/collection-view.ui index f689ebbc7b8ddd7ba1bd503b8cac1c67aa4267a5..65e41b80de7f0566c4d710889c525fb13424f351 100644 --- a/src/ui/collection-view.ui +++ b/src/ui/collection-view.ui @@ -1,47 +1,56 @@ - - + +
+ + Delete + connection.delete + + + Properties + connection.properties + +
+
+ diff --git a/src/ui/display-view.ui b/src/ui/display-view.ui index f2239ba94a9e647d3a4f6fe46842959160c927a9..a2d090562426a414cad3aaa799fa9abaaae9219e 100644 --- a/src/ui/display-view.ui +++ b/src/ui/display-view.ui @@ -1,89 +1,80 @@ - - - + + + diff --git a/src/ui/notification.ui b/src/ui/notification.ui deleted file mode 100644 index 91b52662c5cb2b522cdd1c832faf821d3faeb0a9..0000000000000000000000000000000000000000 --- a/src/ui/notification.ui +++ /dev/null @@ -1,52 +0,0 @@ - - - - - diff --git a/src/ui/onboarding-dialog-page.ui b/src/ui/onboarding-dialog-page.ui index fc8e8833f406b04c61eccd8c9f08b10e3bf774f5..3f09073f4ed01e024bb8ee4d5341bc65ceb566a2 100644 --- a/src/ui/onboarding-dialog-page.ui +++ b/src/ui/onboarding-dialog-page.ui @@ -1,43 +1,41 @@ -