@@ -45,6 +45,10 @@ class LimitedScrolledText(scrolledtext.ScrolledText):
4545 def __init__ (self , master = None , max_lines = 1000 , ** kw ):
4646 super ().__init__ (master , ** kw )
4747 self .max_lines = max_lines
48+ # Hide the built-in vertical scrollbar while keeping scroll functionality.
49+ # ScrolledText stores it as self.vbar; setting width=0 makes it invisible.
50+ self .vbar .config (width = 0 , takefocus = False )
51+ self .vbar .pack_forget ()
4852
4953 def insert (self , index , chars , * args ):
5054 super ().insert (index , chars , * args )
@@ -979,6 +983,34 @@ class KickUser(BaseMessage):
979983registerMessage (KickUser )
980984# --- END KICKUSER PACKET ---
981985
986+ # --- INSTANT MESSAGE PACKETS ---
987+ class ImprovedInstantMessage (BaseMessage ):
988+ """
989+ Received from the simulator when another avatar sends us a private IM.
990+ Dialog types: 0 = plain IM, 9 = group notice, 19 = friendship offer, etc.
991+ """
992+ name = "ImprovedInstantMessage" ; id = 254 ; freq = 2 ; trusted = True ; zero_coded = True
993+ blocks = [("AgentData" , 1 ), ("MessageBlock" , 1 )]
994+ structure = {
995+ "AgentData" : [("AgentID" , "LLUUID" ), ("SessionID" , "LLUUID" )],
996+ "MessageBlock" : [
997+ ("FromGroup" , "BOOL" ),
998+ ("ToAgentID" , "LLUUID" ),
999+ ("ParentEstateID" , "U32" ),
1000+ ("RegionID" , "LLUUID" ),
1001+ ("Position" , "LLVector3" ),
1002+ ("Offline" , "U8" ),
1003+ ("Dialog" , "U8" ),
1004+ ("ID" , "LLUUID" ),
1005+ ("Timestamp" , "U32" ),
1006+ ("FromAgentName" , "Variable" , 1 ),
1007+ ("Message" , "Variable" , 2 ),
1008+ ("BinaryBucket" , "Variable" , 2 ),
1009+ ]
1010+ }
1011+ registerMessage (ImprovedInstantMessage )
1012+ # --- END INSTANT MESSAGE PACKETS ---
1013+
9821014
9831015# ==========================================
9841016# SECTION 5: PACKET HANDLING (packet.py)
@@ -2984,9 +3016,7 @@ def _fetch_parcel_name_task(self, parcel_uuid):
29843016 req = urllib .request .Request (
29853017 url ,
29863018 headers = {
2987- 'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
2988- 'AppleWebKit/537.36 (KHTML, like Gecko) '
2989- 'Chrome/120.0.0.0 Safari/537.36' ,
3019+ 'User-Agent' : 'BlackGlass' ,
29903020 'Accept' : 'text/html'
29913021 }
29923022 )
@@ -4754,6 +4784,7 @@ def _create_widgets(self):
47544784 insertbackground = 'white' ,
47554785 relief = tk .FLAT , highlightthickness = 1 , highlightbackground = '#444444' )
47564786 self .chat_display .grid (row = 0 , column = 0 , sticky = 'nsew' , padx = (0 , 0 ))
4787+ self .chat_display .bind ("<Button-3>" , self ._show_chat_context_menu )
47574788
47584789 # --- FIX: Configure tag for gray speaker name ---
47594790 self .chat_display .tag_config ('speaker_name' , foreground = '#AAAAAA' )
@@ -4806,6 +4837,7 @@ def _create_widgets(self):
48064837 insertbackground = 'white' , relief = tk .FLAT , highlightthickness = 1 , highlightbackground = '#555555' )
48074838 self .message_entry .pack (side = tk .LEFT , fill = tk .X , expand = True , padx = (0 , 5 ))
48084839 self .message_entry .bind ("<Return>" , self .send_message_event )
4840+ self .message_entry .bind ("<Button-3>" , self ._show_entry_context_menu )
48094841
48104842 self .send_button = ttk .Button (input_frame , text = "Send" , command = self .send_message , style = 'BlackGlass.TButton' )
48114843 self .send_button .pack (side = tk .RIGHT )
@@ -5708,6 +5740,96 @@ def _show_teleport_offer(self, offer_data):
57085740 self ._update_status ("Teleport offer declined." )
57095741 self ._append_notification (f"[INFO] Teleport offer to { region_name } declined." )
57105742
5743+ # ------------------------------------------------------------------ #
5744+ # Right-click context menus #
5745+ # ------------------------------------------------------------------ #
5746+
5747+ def _make_context_menu (self ):
5748+ """Create a themed popup menu matching the BlackGlass dark style."""
5749+ menu = tk .Menu (self , tearoff = 0 ,
5750+ bg = '#2C2C2C' , fg = '#E0E0E0' ,
5751+ activebackground = '#444444' , activeforeground = '#FFFFFF' ,
5752+ relief = tk .FLAT , bd = 1 )
5753+ return menu
5754+
5755+ def _show_chat_context_menu (self , event ):
5756+ """Right-click menu for the read-only chat display (copy / select-all)."""
5757+ menu = self ._make_context_menu ()
5758+
5759+ def do_copy ():
5760+ try :
5761+ selected = self .chat_display .get (tk .SEL_FIRST , tk .SEL_LAST )
5762+ self .master .clipboard_clear ()
5763+ self .master .clipboard_append (selected )
5764+ except tk .TclError :
5765+ pass # Nothing selected
5766+
5767+ def do_select_all ():
5768+ self .chat_display .tag_add (tk .SEL , '1.0' , tk .END )
5769+ self .chat_display .mark_set (tk .INSERT , '1.0' )
5770+ self .chat_display .see (tk .INSERT )
5771+
5772+ menu .add_command (label = 'Copy' , command = do_copy )
5773+ menu .add_separator ()
5774+ menu .add_command (label = 'Select All' , command = do_select_all )
5775+
5776+ try :
5777+ menu .tk_popup (event .x_root , event .y_root )
5778+ finally :
5779+ menu .grab_release ()
5780+
5781+ def _show_entry_context_menu (self , event ):
5782+ """Right-click menu for the message Entry (cut / copy / paste / select-all)."""
5783+ menu = self ._make_context_menu ()
5784+
5785+ def do_cut ():
5786+ try :
5787+ selected = self .message_entry .selection_get ()
5788+ self .master .clipboard_clear ()
5789+ self .master .clipboard_append (selected )
5790+ self .message_entry .delete (tk .SEL_FIRST , tk .SEL_LAST )
5791+ except tk .TclError :
5792+ pass
5793+
5794+ def do_copy ():
5795+ try :
5796+ selected = self .message_entry .selection_get ()
5797+ self .master .clipboard_clear ()
5798+ self .master .clipboard_append (selected )
5799+ except tk .TclError :
5800+ pass
5801+
5802+ def do_paste ():
5803+ try :
5804+ text = self .master .clipboard_get ()
5805+ # Insert at cursor, replacing any active selection
5806+ try :
5807+ self .message_entry .delete (tk .SEL_FIRST , tk .SEL_LAST )
5808+ except tk .TclError :
5809+ pass
5810+ self .message_entry .insert (tk .INSERT , text )
5811+ except tk .TclError :
5812+ pass
5813+
5814+ def do_select_all ():
5815+ self .message_entry .select_range (0 , tk .END )
5816+ self .message_entry .icursor (tk .END )
5817+
5818+ menu .add_command (label = 'Cut' , command = do_cut )
5819+ menu .add_command (label = 'Copy' , command = do_copy )
5820+ menu .add_command (label = 'Paste' , command = do_paste )
5821+ menu .add_separator ()
5822+ menu .add_command (label = 'Select All' , command = do_select_all )
5823+
5824+ # Focus the entry so clipboard operations target it
5825+ self .message_entry .focus_set ()
5826+ try :
5827+ menu .tk_popup (event .x_root , event .y_root )
5828+ finally :
5829+ menu .grab_release ()
5830+
5831+ # ------------------------------------------------------------------ #
5832+
57115833 def send_message_event (self , event ):
57125834 self .send_message ()
57135835 return "break"
0 commit comments