11use super :: http_body_widget;
22use super :: http_body_widget:: HttpBodyWidget ;
3- use super :: http_message_parser:: HttpMessageData ;
3+ use super :: http_message_parser:: { HttpMessageData , HttpRequestResponseData } ;
44use crate :: icons:: Icon ;
55use crate :: message_parser:: { MessageData , MessageInfo } ;
66use crate :: tshark_communication:: TcpStreamId ;
@@ -12,13 +12,15 @@ use gtk::prelude::*;
1212use itertools:: Itertools ;
1313use relm:: Widget ;
1414use relm_derive:: { widget, Msg } ;
15+ use std:: borrow:: Cow ;
1516use std:: net:: IpAddr ;
1617use std:: sync:: mpsc;
1718
1819#[ derive( Msg , Debug ) ]
1920pub enum Msg {
2021 DisplayDetails ( mpsc:: Sender < BgFunc > , MessageInfo ) ,
2122 RemoveFormatToggled ,
23+ CopyContentsClick ,
2224}
2325
2426pub 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]
3540impl 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 }
0 commit comments