213 lines
6.8 KiB
Zig
213 lines
6.8 KiB
Zig
/// Clipboard Confirmation Window
|
|
const ClipboardConfirmation = @This();
|
|
|
|
const std = @import("std");
|
|
const Allocator = std.mem.Allocator;
|
|
|
|
const gtk = @import("gtk");
|
|
const adw = @import("adw");
|
|
const gobject = @import("gobject");
|
|
const gio = @import("gio");
|
|
|
|
const apprt = @import("../../apprt.zig");
|
|
const CoreSurface = @import("../../Surface.zig");
|
|
const App = @import("App.zig");
|
|
const Builder = @import("Builder.zig");
|
|
const adw_version = @import("adw_version.zig");
|
|
|
|
const log = std.log.scoped(.gtk);
|
|
|
|
const DialogType = if (adw_version.supportsDialogs()) adw.AlertDialog else adw.MessageDialog;
|
|
|
|
app: *App,
|
|
dialog: *DialogType,
|
|
data: [:0]u8,
|
|
core_surface: *CoreSurface,
|
|
pending_req: apprt.ClipboardRequest,
|
|
text_view: *gtk.TextView,
|
|
text_view_scroll: *gtk.ScrolledWindow,
|
|
reveal_button: *gtk.Button,
|
|
hide_button: *gtk.Button,
|
|
remember_choice: if (adw_version.supportsSwitchRow()) ?*adw.SwitchRow else ?*anyopaque,
|
|
|
|
pub fn create(
|
|
app: *App,
|
|
data: []const u8,
|
|
core_surface: *CoreSurface,
|
|
request: apprt.ClipboardRequest,
|
|
is_secure_input: bool,
|
|
) !void {
|
|
if (app.clipboard_confirmation_window != null) return error.WindowAlreadyExists;
|
|
|
|
const alloc = app.core_app.alloc;
|
|
const self = try alloc.create(ClipboardConfirmation);
|
|
errdefer alloc.destroy(self);
|
|
|
|
try self.init(
|
|
app,
|
|
data,
|
|
core_surface,
|
|
request,
|
|
is_secure_input,
|
|
);
|
|
|
|
app.clipboard_confirmation_window = self;
|
|
}
|
|
|
|
/// Not public because this should be called by the GTK lifecycle.
|
|
fn destroy(self: *ClipboardConfirmation) void {
|
|
const alloc = self.app.core_app.alloc;
|
|
self.app.clipboard_confirmation_window = null;
|
|
alloc.free(self.data);
|
|
alloc.destroy(self);
|
|
}
|
|
|
|
fn init(
|
|
self: *ClipboardConfirmation,
|
|
app: *App,
|
|
data: []const u8,
|
|
core_surface: *CoreSurface,
|
|
request: apprt.ClipboardRequest,
|
|
is_secure_input: bool,
|
|
) !void {
|
|
var builder: Builder = switch (DialogType) {
|
|
adw.AlertDialog => switch (request) {
|
|
.osc_52_read => .init("ccw-osc-52-read", 1, 5),
|
|
.osc_52_write => .init("ccw-osc-52-write", 1, 5),
|
|
.paste => .init("ccw-paste", 1, 5),
|
|
},
|
|
adw.MessageDialog => switch (request) {
|
|
.osc_52_read => .init("ccw-osc-52-read", 1, 2),
|
|
.osc_52_write => .init("ccw-osc-52-write", 1, 2),
|
|
.paste => .init("ccw-paste", 1, 2),
|
|
},
|
|
else => unreachable,
|
|
};
|
|
defer builder.deinit();
|
|
|
|
const dialog = builder.getObject(DialogType, "clipboard_confirmation_window").?;
|
|
const text_view = builder.getObject(gtk.TextView, "text_view").?;
|
|
const reveal_button = builder.getObject(gtk.Button, "reveal_button").?;
|
|
const hide_button = builder.getObject(gtk.Button, "hide_button").?;
|
|
const text_view_scroll = builder.getObject(gtk.ScrolledWindow, "text_view_scroll").?;
|
|
const remember_choice = if (adw_version.supportsSwitchRow())
|
|
builder.getObject(adw.SwitchRow, "remember_choice")
|
|
else
|
|
null;
|
|
|
|
const copy = try app.core_app.alloc.dupeZ(u8, data);
|
|
errdefer app.core_app.alloc.free(copy);
|
|
self.* = .{
|
|
.app = app,
|
|
.dialog = dialog,
|
|
.data = copy,
|
|
.core_surface = core_surface,
|
|
.pending_req = request,
|
|
.text_view = text_view,
|
|
.text_view_scroll = text_view_scroll,
|
|
.reveal_button = reveal_button,
|
|
.hide_button = hide_button,
|
|
.remember_choice = remember_choice,
|
|
};
|
|
|
|
const buffer = gtk.TextBuffer.new(null);
|
|
errdefer buffer.unref();
|
|
buffer.insertAtCursor(copy.ptr, @intCast(copy.len));
|
|
text_view.setBuffer(buffer);
|
|
|
|
if (is_secure_input) {
|
|
text_view_scroll.as(gtk.Widget).setSensitive(@intFromBool(false));
|
|
self.text_view.as(gtk.Widget).addCssClass("blurred");
|
|
|
|
self.reveal_button.as(gtk.Widget).setVisible(@intFromBool(true));
|
|
|
|
_ = gtk.Button.signals.clicked.connect(
|
|
reveal_button,
|
|
*ClipboardConfirmation,
|
|
gtkRevealButtonClicked,
|
|
self,
|
|
.{},
|
|
);
|
|
_ = gtk.Button.signals.clicked.connect(
|
|
hide_button,
|
|
*ClipboardConfirmation,
|
|
gtkHideButtonClicked,
|
|
self,
|
|
.{},
|
|
);
|
|
}
|
|
|
|
_ = DialogType.signals.response.connect(
|
|
dialog,
|
|
*ClipboardConfirmation,
|
|
gtkResponse,
|
|
self,
|
|
.{},
|
|
);
|
|
|
|
switch (DialogType) {
|
|
adw.AlertDialog => {
|
|
const parent: ?*gtk.Widget = widget: {
|
|
const window = core_surface.rt_surface.container.window() orelse break :widget null;
|
|
break :widget window.window.as(gtk.Widget);
|
|
};
|
|
dialog.as(adw.Dialog).present(parent);
|
|
},
|
|
adw.MessageDialog => dialog.as(gtk.Window).present(),
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
fn handleResponse(self: *ClipboardConfirmation, response: [*:0]const u8) void {
|
|
const is_ok = std.mem.orderZ(u8, response, "ok") == .eq;
|
|
|
|
if (is_ok) {
|
|
self.core_surface.completeClipboardRequest(
|
|
self.pending_req,
|
|
self.data,
|
|
true,
|
|
) catch |err| {
|
|
log.err("Failed to requeue clipboard request: {}", .{err});
|
|
};
|
|
}
|
|
|
|
if (self.remember_choice) |remember| remember: {
|
|
if (!adw_version.supportsSwitchRow()) break :remember;
|
|
if (remember.getActive() == 0) break :remember;
|
|
|
|
switch (self.pending_req) {
|
|
.osc_52_read => self.core_surface.config.clipboard_read = if (is_ok) .allow else .deny,
|
|
.osc_52_write => self.core_surface.config.clipboard_write = if (is_ok) .allow else .deny,
|
|
.paste => {},
|
|
}
|
|
}
|
|
|
|
self.destroy();
|
|
}
|
|
fn gtkChoose(dialog_: ?*gobject.Object, result: *gio.AsyncResult, ud: ?*anyopaque) callconv(.c) void {
|
|
const dialog = gobject.ext.cast(DialogType, dialog_.?).?;
|
|
const self: *ClipboardConfirmation = @ptrCast(@alignCast(ud.?));
|
|
const response = dialog.chooseFinish(result);
|
|
self.handleResponse(response);
|
|
}
|
|
|
|
fn gtkResponse(_: *DialogType, response: [*:0]u8, self: *ClipboardConfirmation) callconv(.c) void {
|
|
self.handleResponse(response);
|
|
}
|
|
|
|
fn gtkRevealButtonClicked(_: *gtk.Button, self: *ClipboardConfirmation) callconv(.c) void {
|
|
self.text_view_scroll.as(gtk.Widget).setSensitive(@intFromBool(true));
|
|
self.text_view.as(gtk.Widget).removeCssClass("blurred");
|
|
|
|
self.hide_button.as(gtk.Widget).setVisible(@intFromBool(true));
|
|
self.reveal_button.as(gtk.Widget).setVisible(@intFromBool(false));
|
|
}
|
|
|
|
fn gtkHideButtonClicked(_: *gtk.Button, self: *ClipboardConfirmation) callconv(.c) void {
|
|
self.text_view_scroll.as(gtk.Widget).setSensitive(@intFromBool(false));
|
|
self.text_view.as(gtk.Widget).addCssClass("blurred");
|
|
|
|
self.hide_button.as(gtk.Widget).setVisible(@intFromBool(false));
|
|
self.reveal_button.as(gtk.Widget).setVisible(@intFromBool(true));
|
|
}
|