Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions data/io.elementary.code.appdata.xml.in
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@
</ul>
</description>
<releases>
<release version="6.1.1" date="2021-12-17" urgency="medium">
<description>
<p>Improvements:</p>
<ul>
<li>Use the OpenURI portal for launching external applications</li>
</ul>
</description>
</release>
<release version="6.1.0" date="2021-11-23" urgency="medium">
<description>
<p>Improvements:</p>
Expand Down
2 changes: 2 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ glib_dep = dependency('glib-2.0', version: '>=2.30.0')
gio_unix_dep = dependency('gio-unix-2.0', version: '>=2.20')
gee_dep = dependency('gee-0.8', version: '>=0.8.5')
gtk_dep = dependency('gtk+-3.0', version: '>=3.6.0')
gdk_dep = [ dependency('gdk-x11-3.0'), dependency('gdk-wayland-3.0') ]
granite_dep = dependency('granite', version: '>=6.0.0')
handy_dep = dependency('libhandy-1', version: '>=0.90.0')
gtksourceview_dep = dependency('gtksourceview-4')
Expand All @@ -49,6 +50,7 @@ dependencies = [
gio_unix_dep,
gee_dep,
gtk_dep,
gdk_dep,
granite_dep,
handy_dep,
gtksourceview_dep,
Expand Down
37 changes: 2 additions & 35 deletions src/FolderManager/FileItem.vala
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,8 @@ namespace Scratch.FolderManager {
new_window.open_document (doc, true);
});

var files_appinfo = AppInfo.get_default_for_type ("inode/directory", true);

var files_item_icon = new Gtk.Image.from_gicon (files_appinfo.get_icon (), Gtk.IconSize.MENU);
files_item_icon.pixel_size = 16;

var files_item_grid = new Gtk.Grid ();
files_item_grid.add (files_item_icon);
files_item_grid.add (new Gtk.Label (files_appinfo.get_name ()));

var files_menuitem = new Gtk.MenuItem ();
files_menuitem.add (files_item_grid);
files_menuitem.activate.connect (() => launch_app_with_file (files_appinfo, file.file));
var files_menuitem = new Gtk.MenuItem.with_label (_("File Manager…"));
Comment thread
Marukesu marked this conversation as resolved.
Outdated
files_menuitem.activate.connect (() => launch_in_file_manager (file));

var other_menuitem = new Gtk.MenuItem.with_label (_("Other Application…"));
other_menuitem.activate.connect (() => show_app_chooser (file));
Expand All @@ -72,29 +62,6 @@ namespace Scratch.FolderManager {
if (info != null) {
var file_type = info.get_attribute_string (GLib.FileAttribute.STANDARD_CONTENT_TYPE);

List<AppInfo> external_apps = GLib.AppInfo.get_all_for_type (file_type);

foreach (AppInfo app_info in external_apps) {
if (app_info.get_id () == GLib.Application.get_default ().application_id + ".desktop") {
continue;
}

var menuitem_icon = new Gtk.Image.from_gicon (app_info.get_icon (), Gtk.IconSize.MENU);
menuitem_icon.pixel_size = 16;

var menuitem_grid = new Gtk.Grid ();
menuitem_grid.add (menuitem_icon);
menuitem_grid.add (new Gtk.Label (app_info.get_name ()));

var item_app = new Gtk.MenuItem ();
item_app.add (menuitem_grid);

item_app.activate.connect (() => {
launch_app_with_file (app_info, file.file);
});
open_in_menu.add (item_app);
}

try {
var contracts = Granite.Services.ContractorProxy.get_contracts_by_mime (file_type);
foreach (var contract in contracts) {
Expand Down
52 changes: 4 additions & 48 deletions src/FolderManager/FolderItem.vala
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ namespace Scratch.FolderManager {
}

public override Gtk.Menu? get_context_menu () {
var open_in_item = new Gtk.MenuItem.with_label (_("Open In…"));
Comment thread
Marukesu marked this conversation as resolved.
Outdated
open_in_item.activate.connect (() => show_app_chooser (file));

var contractor_menu = new Gtk.Menu ();

GLib.FileInfo info = null;
Expand Down Expand Up @@ -109,7 +112,7 @@ namespace Scratch.FolderManager {
};

var menu = new Gtk.Menu ();
menu.append (create_submenu_for_open_in (info, file_type));
menu.append (open_in_item);
menu.append (contractor_item);
menu.append (new Gtk.SeparatorMenuItem ());
menu.append (create_submenu_for_new ());
Expand All @@ -122,53 +125,6 @@ namespace Scratch.FolderManager {
return menu;
}

protected Gtk.MenuItem create_submenu_for_open_in (GLib.FileInfo? info, string? file_type) {
var other_menuitem = new Gtk.MenuItem.with_label (_("Other Application…"));
other_menuitem.activate.connect (() => show_app_chooser (file));

file_type = file_type ?? "inode/directory";

var open_in_menu = new Gtk.Menu ();

if (info != null) {
List<AppInfo> external_apps = GLib.AppInfo.get_all_for_type (file_type);

string this_id = GLib.Application.get_default ().application_id + ".desktop";

foreach (AppInfo app_info in external_apps) {
if (app_info.get_id () == this_id) {
continue;
}

var menuitem_icon = new Gtk.Image.from_gicon (app_info.get_icon (), Gtk.IconSize.MENU);
menuitem_icon.pixel_size = 16;

var menuitem_grid = new Gtk.Grid ();
menuitem_grid.add (menuitem_icon);
menuitem_grid.add (new Gtk.Label (app_info.get_name ()));

var item_app = new Gtk.MenuItem ();
item_app.add (menuitem_grid);

item_app.activate.connect (() => {
launch_app_with_file (app_info, file.file);
});
open_in_menu.add (item_app);
}
}

if (open_in_menu.get_children ().length () > 0) {
open_in_menu.add (new Gtk.SeparatorMenuItem ());
}

open_in_menu.add (other_menuitem);

var open_in_item = new Gtk.MenuItem.with_label (_("Open In"));
open_in_item.submenu = open_in_menu;

return open_in_item;
}

protected Gtk.MenuItem create_submenu_for_new () {
var new_folder_item = new Gtk.MenuItem.with_label (_("Folder"));
new_folder_item.activate.connect (() => on_add_new (true));
Expand Down
72 changes: 57 additions & 15 deletions src/FolderManager/Item.vala
Original file line number Diff line number Diff line change
Expand Up @@ -76,27 +76,69 @@ namespace Scratch.FolderManager {
}

public void show_app_chooser (File file) {
var dialog = new Gtk.AppChooserDialog (new Gtk.Window (), Gtk.DialogFlags.MODAL, file.file);
dialog.deletable = false;

if (dialog.run () == Gtk.ResponseType.OK) {
var app_info = dialog.get_app_info ();
if (app_info != null) {
launch_app_with_file (app_info, file.file);
var window = (MainWindow) ((Gtk.Application) GLib.Application.get_default ()).active_window;
try {
var portal = Portal.OpenURI.get ();
var options = new HashTable<string, Variant> (null, null);
options["token_handler"] = Portal.generate_token ();
options["writable"] = !file.is_valid_directory ();
Comment thread
Marukesu marked this conversation as resolved.
Outdated
options["ask"] = true;

var fd = Posix.open (file.path, (file.is_valid_directory () ? Posix.O_RDONLY : Posix.O_RDWR) | Posix.O_CLOEXEC);
if (fd == -1) {
critical ("OpenURI: cannot open file dscriptor for '%s'", file.path);
return;
}
}

dialog.destroy ();
window.export.begin ((obj, res) => {
var handle = window.export.end (res);
if (portal.version > 2) {
try {
portal.open_file (handle, new UnixInputStream (fd, true), options);
} catch (Error e) {
warning ("error calling portal: %s", e.message);
}
} else {
warning ("OpenURI: portal version is too old");
}

window.unexport ();
});
} catch (Error e) {
warning ("cannot connect to portal: %s", e.message);
}
}

public void launch_app_with_file (AppInfo app_info, GLib.File file) {
var file_list = new List<GLib.File> ();
file_list.append (file);

public void launch_in_file_manager (File file) {
var window = (MainWindow) ((Gtk.Application) GLib.Application.get_default ()).active_window;
try {
app_info.launch (file_list, null);
var portal = Portal.OpenURI.get ();
var options = new HashTable<string, Variant> (null, null);
options["token_handler"] = Portal.generate_token ();

var fd = Posix.open (file.path, Posix.O_RDONLY | Posix.O_CLOEXEC);
if (fd == -1) {
critical ("OpenURI: cannot open file dscriptor for '%s'", file.path);
return;
}

// the OpenDirectory method was added on version 3
Comment thread
Marukesu marked this conversation as resolved.
Outdated
window.export.begin ((obj, res) => {
try {
var handle = window.export.end (res);
if (portal.version > 2) {
portal.open_directory (handle, new UnixInputStream (fd, true), options);
} else {
portal.open_file (handle, new UnixInputStream (fd, true), options);
}
} catch (Error e) {
warning ("error calling portal: %s", e.message);
}

window.unexport ();
});
} catch (Error e) {
warning (e.message);
warning ("cannot connect to portal: %s", e.message);
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/FolderManager/ProjectFolderItem.vala
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ namespace Scratch.FolderManager {
}

public override Gtk.Menu? get_context_menu () {
var open_in_item = new Gtk.MenuItem.with_label (_("Open In…"));
open_in_item.activate.connect (() => show_app_chooser (file));

var close_item = new Gtk.MenuItem.with_label (_("Close Folder"));
close_item.activate.connect (() => {
closed ();
Expand Down Expand Up @@ -145,7 +148,7 @@ namespace Scratch.FolderManager {
}

var menu = new Gtk.Menu ();
menu.append (create_submenu_for_open_in (info, file_type));
menu.append (open_in_item);
menu.append (new Gtk.SeparatorMenuItem ());
menu.append (create_submenu_for_new ());

Expand Down
31 changes: 31 additions & 0 deletions src/MainWindow.vala
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ namespace Scratch {
// Plugins
private Scratch.Services.PluginsManager plugins;

private bool exported;

// Widgets for Plugins
public Gtk.Notebook bottombar;
public Code.Sidebar sidebar;
Expand Down Expand Up @@ -1023,5 +1025,34 @@ namespace Scratch {

return path;
}

public async string export () {
var window = get_window ();

if (window is Gdk.X11.Window) {
var xid = ((Gdk.X11.Window) window).get_xid ();
return "x11:%x".printf ((uint) xid);
} else if (window is Gdk.Wayland.Window) {
var handle = "wayland:";
((Gdk.Wayland.Window) window).export_handle ((w, h) => {
handle += h;
export.callback ();
});
yield;

exported = handle != "wayland:";
return exported ? handle : "";
} else {
warning ("Unknown windowing system, not exporting window");
return "";
}
}

public void unexport () {
if (exported) {
((Gdk.Wayland.Window) get_window ()).unexport_handle ();
exported = false;
}
}
}
}
49 changes: 49 additions & 0 deletions src/Services/Portal.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright (c) 2021 elementary, Inc. (https://elementary.io)
*
* 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 <http://www.gnu.org/licenses/>.
*
*/

namespace Portal {
const string DESKTOP_BUS_NAME = "org.freedesktop.portal.Desktop";
const string DESKTOP_BUS_PATH = "/org/freedesktop/portal/desktop";
OpenURI? open_uri_portal = null;

public static string generate_token () {
return "%s_%i".printf (
GLib.Application.get_default ().application_id.replace (".", "_"),
Random.int_range (0, int32.MAX)
);
}

[DBus (name = "org.freedesktop.portal.OpenURI")]
interface OpenURI : Object {
[DBus (name = "version")]
public abstract uint32 version { get; }

public static OpenURI @get () throws Error {
if (open_uri_portal == null) {
var connection = GLib.Application.get_default ().get_dbus_connection ();
open_uri_portal = connection.get_proxy_sync<OpenURI> (DESKTOP_BUS_NAME, DESKTOP_BUS_PATH);
}

return open_uri_portal;
}

public abstract ObjectPath open_uri (string parent_window, string uri, HashTable<string, Variant> options) throws DBusError, IOError;
public abstract ObjectPath open_file (string parent_window, UnixInputStream fd, HashTable<string, Variant> options) throws DBusError, IOError;
public abstract ObjectPath open_directory (string parent_window, UnixInputStream fd, HashTable<string, Variant> options) throws DBusError, IOError;
}
}
1 change: 1 addition & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ code_files = files(
'Services/GitManager.vala',
'Services/MonitoredRepository.vala',
'Services/PluginManager.vala',
'Services/Portal.vala',
'Services/Settings.vala',
'Services/TemplateManager.vala',
'Widgets/ChooseProjectButton.vala',
Expand Down
14 changes: 14 additions & 0 deletions vapi/gdk-wayland-3.0.vapi
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[CCode (cheader_filename = "gdk/gdkwayland.h")]
namespace Gdk.Wayland {
[CCode (type_id = "GDK_TYPE_WAYLAND_WINDOW", type_check_function = "GDK_IS_WAYLAND_WINDOW")]
public class Window : Gdk.Window {
protected Window ();

public bool export_handle (owned WindowExported callback);
public bool set_transient_for_exported (string parent_handle_str);
public void unexport_handle ();
}

[CCode (instance_pos = 2.9)]
public delegate void WindowExported (Gdk.Window window, string handle);
}