@@ -208,6 +208,8 @@ def __init__(self, parser, tk, ttk, scrolledtext, messagebox, filedialog, font):
208208 self .inners = {} # name -> scrollable inner frame (populated lazily)
209209 self .builders = {} # name -> callable that populates the inner frame
210210 self .built = set () # names whose content has been built
211+ self .badges = {} # name -> sidebar count badge label
212+ self .sectionDests = {} # name -> [option dests in that section]
211213 self .paneOrder = [] # nav order, for Up/Down navigation
212214 self .currentPane = None
213215 self .process = None
@@ -329,6 +331,7 @@ def _buildLayout(self):
329331 cmdBar = self .ttk .Frame (self .window , style = "Bar.TFrame" , padding = (20 , 8 ))
330332 cmdBar .pack (fill = tk .X )
331333 self .ttk .Label (cmdBar , text = "Command:" , style = "Hint.TLabel" ).pack (side = tk .LEFT , padx = (0 , 8 ))
334+ self .ttk .Button (cmdBar , text = "Copy" , command = self ._copyCommand , takefocus = False ).pack (side = tk .RIGHT , padx = (8 , 0 ))
332335 self .command = tk .StringVar (value = "sqlmap.py" )
333336 cmdEntry = tk .Entry (cmdBar , textvariable = self .command , font = self .fonts ["mono" ],
334337 bg = "#ffffff" , fg = PALETTE ["blue" ], readonlybackground = "#ffffff" ,
@@ -352,6 +355,12 @@ def _buildLayout(self):
352355 self .window .bind ("<Up>" , lambda e : self ._navKey (- 1 ))
353356 for seq in ("<MouseWheel>" , "<Button-4>" , "<Button-5>" ):
354357 self .window .bind_all (seq , self ._onWheel )
358+ self .window .bind ("<F5>" , lambda e : self .run ())
359+ self .window .bind ("<Control-r>" , lambda e : self .run ())
360+ self .window .bind ("<Control-Return>" , lambda e : self .run ())
361+ self .window .bind ("<Control-l>" , lambda e : self ._focusTarget ())
362+ self .window .bind ("<Control-s>" , lambda e : self .saveConfigDialog ())
363+ self .window .bind ("<Control-o>" , lambda e : self .loadConfig ())
355364 self ._enableSelectAll ()
356365 self ._tickStats ()
357366 self ._prebuildPanes ()
@@ -586,14 +595,17 @@ def _addPane(self, name, navText):
586595 icon = tk .Canvas (row , width = 22 , height = 22 , highlightthickness = 0 , borderwidth = 0 , background = p ["mantle" ])
587596 icon .pack (side = tk .LEFT , padx = (13 , 0 ), pady = 8 )
588597 self ._drawIcon (icon , name , self ._iconColor (name ))
598+ badge = tk .Label (row , text = "" , background = p ["mantle" ], foreground = p ["blue" ], font = self .fonts ["small" ])
599+ badge .pack (side = tk .RIGHT , padx = (0 , 12 ))
600+ self .badges [name ] = badge
589601 lab = tk .Label (row , text = navText , background = p ["mantle" ], foreground = p ["subtext" ],
590602 font = self .fonts ["nav" ], anchor = "w" , padx = 10 , pady = 9 )
591603 lab .pack (side = tk .LEFT , fill = tk .X , expand = True )
592- for w in (row , lab , strip , icon ):
604+ for w in (row , lab , strip , icon , badge ):
593605 w .bind ("<Button-1>" , lambda e , n = name : self ._selectPane (n ))
594606 w .bind ("<Enter>" , lambda e , n = name : self ._navHover (n , True ))
595607 w .bind ("<Leave>" , lambda e , n = name : self ._navHover (n , False ))
596- self .navItems [name ] = (row , strip , icon , lab )
608+ self .navItems [name ] = (row , strip , icon , lab , badge )
597609 self .paneOrder .append (name )
598610
599611 outer = self .ttk .Frame (self .content , style = "Card.TFrame" )
@@ -618,8 +630,8 @@ def _navHover(self, name, entering):
618630 if name == self .currentPane :
619631 return
620632 bg = PALETTE ["surface2" ] if entering else PALETTE ["mantle" ]
621- row , strip , icon , lab = self .navItems [name ]
622- for w in (row , strip , icon , lab ):
633+ row , strip , icon , lab , badge = self .navItems [name ]
634+ for w in (row , strip , icon , lab , badge ):
623635 w .configure (background = bg )
624636
625637 def _navKey (self , delta ):
@@ -643,16 +655,18 @@ def _selectPane(self, name):
643655 p = PALETTE
644656 if self .currentPane :
645657 self .panes [self .currentPane ].pack_forget ()
646- row , strip , icon , lab = self .navItems [self .currentPane ]
658+ row , strip , icon , lab , badge = self .navItems [self .currentPane ]
647659 for w in (row , strip , icon ):
648660 w .configure (background = p ["mantle" ])
649661 lab .configure (background = p ["mantle" ], foreground = p ["text" ], font = self .fonts ["nav" ])
662+ badge .configure (background = p ["mantle" ], foreground = p ["blue" ])
650663 self ._drawIcon (icon , self .currentPane , self ._iconColor (self .currentPane ))
651664 self .panes [name ].pack (expand = True , fill = self .tk .BOTH )
652- row , strip , icon , lab = self .navItems [name ]
665+ row , strip , icon , lab , badge = self .navItems [name ]
653666 for w in (row , strip , icon ):
654667 w .configure (background = p ["blue" ])
655668 lab .configure (background = p ["blue" ], foreground = "#ffffff" , font = self .fonts ["bodyBold" ])
669+ badge .configure (background = p ["blue" ], foreground = "#ffffff" )
656670 self ._drawIcon (icon , name , "#ffffff" )
657671 self .currentPane = name
658672 self ._ensureNavVisible (name )
@@ -704,6 +718,7 @@ def _onWheel(self, event):
704718 def _buildQuickStartPane (self ):
705719 name = "Quick start"
706720 self ._addPane (name , name )
721+ self .sectionDests [name ] = [_ for _ in QUICK_START_DESTS if _ in self .optionByDest ]
707722
708723 def build (inner ):
709724 self .ttk .Label (inner , text = "Quick start" , style = "Pane.TLabel" ).grid (row = 0 , column = 0 , columnspan = 2 , sticky = "w" )
@@ -721,6 +736,7 @@ def build(inner):
721736 def _buildGroupPane (self , group ):
722737 title = _groupTitle (group )
723738 self ._addPane (title , NAV_ALIASES .get (title , title ))
739+ self .sectionDests [title ] = [_optDest (_ ) for _ in _groupOptions (group ) if _optDest (_ )]
724740
725741 def build (inner , group = group , title = title ):
726742 self .ttk .Label (inner , text = title , style = "Pane.TLabel" ).grid (row = 0 , column = 0 , columnspan = 2 , sticky = "w" )
@@ -806,19 +822,25 @@ def _center(self, window, width=None, height=None):
806822 window .geometry ("%dx%d+%d+%d" % (width , height , x , y ))
807823
808824 def _updateStats (self ):
809- count = 0
825+ setDests = set ()
810826 for dest , (otype , var ) in self .widgets .items ():
811827 try :
812828 if otype == "bool" :
813829 if var .get ():
814- count += 1
830+ setDests . add ( dest )
815831 else :
816832 raw = var .get ()
817833 if raw not in (None , "" ) and str (raw ) != str (defaults .get (dest , "" )):
818- count += 1
834+ setDests . add ( dest )
819835 except Exception :
820836 pass
837+ count = len (setDests )
821838 self .stat .set ("%d option%s set" % (count , "" if count == 1 else "s" ))
839+ for name , dests in self .sectionDests .items ():
840+ badge = self .badges .get (name )
841+ if badge is not None :
842+ hits = sum (1 for _ in dests if _ in setDests )
843+ badge .configure (text = (str (hits ) if hits else "" ))
822844
823845 def _buildCommandString (self ):
824846 parts = ["sqlmap.py" ]
@@ -850,6 +872,22 @@ def _tickStats(self):
850872 self .command .set (self ._buildCommandString ())
851873 self .window .after (1200 , self ._tickStats )
852874
875+ def _copyCommand (self ):
876+ try :
877+ self .window .clipboard_clear ()
878+ self .window .clipboard_append (self .command .get ())
879+ self .hint .set ("Command copied to clipboard" )
880+ except Exception :
881+ pass
882+
883+ def _focusTarget (self ):
884+ try :
885+ self .targetEntry .focus_set ()
886+ self .targetEntry .select_range (0 , "end" )
887+ except Exception :
888+ pass
889+ return "break"
890+
853891 def _collectConfig (self ):
854892 config = {}
855893 for dest , (otype , var ) in self .widgets .items ():
0 commit comments