Skip to content

Commit 8b19498

Browse files
http: ability to copy communication to the clipboard
1 parent 6c5ae56 commit 8b19498

File tree

3 files changed

+104
-23
lines changed

3 files changed

+104
-23
lines changed

src/http/http_details_widget.rs

Lines changed: 102 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::http_body_widget;
22
use super::http_body_widget::HttpBodyWidget;
3-
use super::http_message_parser::HttpMessageData;
3+
use super::http_message_parser::{HttpMessageData, HttpRequestResponseData};
44
use crate::icons::Icon;
55
use crate::message_parser::{MessageData, MessageInfo};
66
use crate::tshark_communication::TcpStreamId;
@@ -12,13 +12,15 @@ use gtk::prelude::*;
1212
use itertools::Itertools;
1313
use relm::Widget;
1414
use relm_derive::{widget, Msg};
15+
use std::borrow::Cow;
1516
use std::net::IpAddr;
1617
use std::sync::mpsc;
1718

1819
#[derive(Msg, Debug)]
1920
pub enum Msg {
2021
DisplayDetails(mpsc::Sender<BgFunc>, MessageInfo),
2122
RemoveFormatToggled,
23+
CopyContentsClick,
2224
}
2325

2426
pub struct Model {
@@ -28,11 +30,62 @@ pub struct Model {
2830
client_ip: IpAddr,
2931
data: HttpMessageData,
3032

33+
options_popover: gtk::Popover,
34+
format_contents_btn: gtk::CheckButton,
35+
3136
format_request_response: bool,
3237
}
3338

3439
#[widget]
3540
impl Widget for HttpCommEntry {
41+
fn init_options_overlay(
42+
relm: &relm::Relm<Self>,
43+
overlay: &gtk::Overlay,
44+
format_contents_btn: &gtk::CheckButton,
45+
) -> gtk::Popover {
46+
let popover_box = gtk::BoxBuilder::new()
47+
.orientation(gtk::Orientation::Vertical)
48+
.margin(10)
49+
.spacing(10)
50+
.build();
51+
52+
relm::connect!(
53+
relm,
54+
format_contents_btn,
55+
connect_toggled(_),
56+
Msg::RemoveFormatToggled
57+
);
58+
popover_box.add(format_contents_btn);
59+
let copy_to_clipboard_lbl = gtk::ButtonBuilder::new().label("Copy to clipboard").build();
60+
popover_box.add(&copy_to_clipboard_lbl);
61+
popover_box.show_all();
62+
63+
relm::connect!(
64+
relm,
65+
copy_to_clipboard_lbl,
66+
connect_clicked(_),
67+
Msg::CopyContentsClick
68+
);
69+
70+
let options_popover = gtk::PopoverBuilder::new().child(&popover_box).build();
71+
72+
let options_btn = gtk::MenuButtonBuilder::new()
73+
.always_show_image(true)
74+
.image(&gtk::Image::from_icon_name(
75+
Some(Icon::COG.name()),
76+
gtk::IconSize::Menu,
77+
))
78+
.valign(gtk::Align::Start)
79+
.halign(gtk::Align::End)
80+
.margin_top(10)
81+
.margin_end(10)
82+
.build();
83+
options_btn.set_popover(Some(&options_popover));
84+
overlay.add_overlay(&options_btn);
85+
86+
options_popover
87+
}
88+
3689
fn model(
3790
relm: &relm::Relm<Self>,
3891
params: (
@@ -45,32 +98,20 @@ impl Widget for HttpCommEntry {
4598
),
4699
) -> Model {
47100
let (win_msg_sender, stream_id, client_ip, data, overlay, bg_sender) = params;
48-
49-
let disable_formatting_btn = gtk::ToggleButtonBuilder::new()
50-
.label("Disable formatting")
51-
.always_show_image(true)
52-
.image(&gtk::Image::from_icon_name(
53-
Some(Icon::REMOVE_FORMAT.name()),
54-
gtk::IconSize::Menu,
55-
))
56-
.valign(gtk::Align::Start)
57-
.halign(gtk::Align::End)
58-
.margin_top(10)
59-
.margin_end(10)
101+
let format_contents_btn = gtk::CheckButtonBuilder::new()
102+
.active(true)
103+
.label("Format contents")
60104
.build();
61-
overlay.add_overlay(&disable_formatting_btn);
62-
relm::connect!(
63-
relm,
64-
disable_formatting_btn,
65-
connect_clicked(_),
66-
Msg::RemoveFormatToggled
67-
);
105+
let options_popover = Self::init_options_overlay(relm, &overlay, &format_contents_btn);
106+
68107
Model {
69108
win_msg_sender,
70109
bg_sender,
71110
data,
72111
stream_id,
73112
client_ip,
113+
format_contents_btn,
114+
options_popover,
74115
format_request_response: true,
75116
}
76117
}
@@ -111,7 +152,7 @@ impl Widget for HttpCommEntry {
111152
});
112153
}
113154
Msg::RemoveFormatToggled => {
114-
self.model.format_request_response = !self.model.format_request_response;
155+
self.model.format_request_response = self.model.format_contents_btn.is_active();
115156
self.streams
116157
.request_body
117158
.emit(http_body_widget::Msg::FormatCodeChanged(
@@ -123,6 +164,46 @@ impl Widget for HttpCommEntry {
123164
self.model.format_request_response,
124165
));
125166
}
167+
Msg::CopyContentsClick => {
168+
if let Some(clip) =
169+
gtk::Clipboard::default(&self.widgets.comm_info_header.display())
170+
{
171+
let format_reqresp = |r: &HttpRequestResponseData| {
172+
format!(
173+
"{}\n{}\n\n{}",
174+
r.first_line,
175+
r.headers
176+
.iter()
177+
.map(|(k, v)| format!("{}: {}", k, v))
178+
.join("\n"),
179+
r.body_as_str().unwrap_or(Cow::Borrowed(""))
180+
)
181+
};
182+
let clip_contents = format!(
183+
"{}\n-------\n{}",
184+
self.model
185+
.data
186+
.request
187+
.as_ref()
188+
.map(format_reqresp)
189+
.as_deref()
190+
.unwrap_or(""),
191+
self.model
192+
.data
193+
.response
194+
.as_ref()
195+
.map(format_reqresp)
196+
.as_deref()
197+
.unwrap_or("")
198+
);
199+
clip.set_text(&clip_contents);
200+
self.model.win_msg_sender.emit(win::Msg::InfoBarShow(
201+
Some("Copied to the clipboard".to_string()),
202+
win::InfobarOptions::TimeLimitedWithCloseButton,
203+
))
204+
}
205+
self.model.options_popover.popdown();
206+
}
126207
_ => {}
127208
}
128209
}

src/icons.gresource

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515
<file alias="create-symbolic.svg">fontawesome-5.13.0/svgs/solid/folder-plus.svg</file>
1616
<file alias="alter-symbolic.svg">fontawesome-5.13.0/svgs/solid/wrench.svg</file>
1717
<file alias="plsql-symbolic.svg">fontawesome-5.13.0/svgs/solid/cogs.svg</file>
18+
<file alias="cog-symbolic.svg">fontawesome-5.13.0/svgs/solid/cog.svg</file>
1819
<file alias="login-symbolic.svg">fontawesome-5.13.0/svgs/solid/user-circle.svg</file>
1920
<file alias="copy-symbolic.svg">fontawesome-5.13.0/svgs/solid/truck-loading.svg</file>
2021
<file alias="other-symbolic.svg">fontawesome-5.13.0/svgs/solid/question-circle.svg</file>
2122
<file alias="bookmark-symbolic.svg">fontawesome-5.13.0/svgs/solid/bookmark.svg</file>
22-
<file alias="remove-format-symbolic.svg">fontawesome-5.13.0/svgs/solid/remove-format.svg</file>
2323
<file alias="angle-double-down-symbolic.svg">fontawesome-5.13.0/svgs/solid/angle-double-down.svg</file>
2424
<file alias="session-symbolic.svg">fontawesome-5.13.0/svgs/solid/exchange-alt.svg</file>
2525
<file alias="remote-host-symbolic.svg">fontawesome-5.13.0/svgs/solid/network-wired.svg</file>

src/icons.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ impl Icon {
77
}
88

99
pub const HTTP: Icon = Icon("http");
10+
pub const COG: Icon = Icon("cog");
1011
pub const DATABASE: Icon = Icon("database");
1112
pub const LOCK: Icon = Icon("lock");
1213
pub const INSERT: Icon = Icon("insert");
@@ -23,7 +24,6 @@ impl Icon {
2324
pub const LOGIN: Icon = Icon("login");
2425
pub const COPY: Icon = Icon("copy");
2526
pub const BOOKMARK: Icon = Icon("bookmark");
26-
pub const REMOVE_FORMAT: Icon = Icon("remove-format");
2727
pub const OTHER: Icon = Icon("other");
2828
pub const ANGLE_DOUBLE_DOWN: Icon = Icon("angle-double-down");
2929
pub const SESSION: Icon = Icon("session");

0 commit comments

Comments
 (0)