Convert ListProcesses from TSortListView to VirtualTree.

- Don't replace "ListProcessesSelectItem" by "ListProcessesClick". Do the menuitem stuff in popupHost.popup()
- Avoid potential AV when exceeding the captions-list of a node in GetNodeText.
This commit is contained in:
Ansgar Becker
2007-08-21 23:19:51 +00:00
parent 3ad1b57608
commit 43f6d11cc2
3 changed files with 94 additions and 68 deletions

View File

@ -157,59 +157,83 @@ object MDIChild: TMDIChild
object tabProcessList: TTabSheet object tabProcessList: TTabSheet
Caption = 'Process-List' Caption = 'Process-List'
ImageIndex = 1 ImageIndex = 1
object ListProcesses: TSortListView object ListProcesses: TVirtualStringTree
Tag = -1
Left = 0 Left = 0
Top = 0 Top = 0
Width = 488 Width = 488
Height = 175 Height = 175
Align = alClient Align = alClient
Header.AutoSizeIndex = 7
Header.Font.Charset = DEFAULT_CHARSET
Header.Font.Color = clWindowText
Header.Font.Height = -11
Header.Font.Name = 'Tahoma'
Header.Font.Style = []
Header.Options = [hoAutoResize, hoColumnResize, hoDblClickResize, hoDrag, hoShowSortGlyphs, hoVisible]
Header.SortColumn = 0
Header.SortDirection = sdDescending
Images = MainForm.ImageList1
IncrementalSearch = isInitializedOnly
PopupMenu = popupHost
TabOrder = 0
TreeOptions.AutoOptions = [toAutoDropExpand, toAutoScrollOnExpand, toAutoSort, toAutoTristateTracking, toAutoDeleteMovedNodes]
TreeOptions.MiscOptions = [toFullRepaintOnResize, toInitOnSave, toToggleOnDblClick, toWheelPanning]
TreeOptions.PaintOptions = [toShowDropmark, toShowHorzGridLines, toShowVertGridLines, toThemeAware, toUseBlendedImages]
TreeOptions.SelectionOptions = [toFullRowSelect, toRightClickSelect]
OnCompareNodes = vstCompareNodes
OnFreeNode = vstFreeNode
OnGetText = vstGetText
OnGetImageIndex = vstGetImageIndex
OnGetNodeDataSize = vstGetNodeDataSize
OnHeaderClick = vstHeaderClick
OnInitNode = vstInitNode
Columns = < Columns = <
item item
Caption = 'id' Alignment = taRightJustify
Width = -1 Options = [coAllowClick, coEnabled, coParentBidiMode, coParentColor, coResizable, coShowDropMark, coVisible]
WidthType = ( Position = 0
-1) WideText = 'id'
end end
item item
Caption = 'User' Options = [coAllowClick, coEnabled, coParentBidiMode, coParentColor, coResizable, coShowDropMark, coVisible]
Position = 1
Width = 80 Width = 80
WideText = 'User'
end end
item item
Caption = 'Host' Options = [coAllowClick, coEnabled, coParentBidiMode, coParentColor, coResizable, coShowDropMark, coVisible]
Tag = -1 Position = 2
Width = 80 Width = 80
WideText = 'Host'
end end
item item
Caption = 'DB' Options = [coAllowClick, coEnabled, coParentBidiMode, coParentColor, coResizable, coShowDropMark, coVisible]
Width = -1 Position = 3
WidthType = (
-1)
end
item
Caption = 'Command'
Width = 80 Width = 80
WideText = 'DB'
end end
item item
Caption = 'Time' Options = [coAllowClick, coEnabled, coParentBidiMode, coParentColor, coResizable, coShowDropMark, coVisible]
Position = 4
Width = 80
WideText = 'Command'
end end
item item
Caption = 'State' Options = [coAllowClick, coEnabled, coParentBidiMode, coParentColor, coResizable, coShowDropMark, coVisible]
Position = 5
WideText = 'Time'
end end
item item
Caption = 'Info' Options = [coAllowClick, coEnabled, coParentBidiMode, coParentColor, coResizable, coShowDropMark, coVisible]
Width = 126 Position = 6
WideText = 'State'
end
item
Options = [coAllowClick, coEnabled, coParentBidiMode, coParentColor, coResizable, coShowDropMark, coVisible]
Position = 7
Width = 14
WideText = 'Info'
end> end>
GridLines = True
ReadOnly = True
RowSelect = True
PopupMenu = popupHost
SmallImages = MainForm.ImageList1
TabOrder = 0
ViewStyle = vsReport
OnSelectItem = ListProcessesSelectItem
ImageIndexSortAsc = 95
ImageIndexSortDesc = 94
end end
end end
object tabCommandStats: TTabSheet object tabCommandStats: TTabSheet

View File

@ -61,7 +61,7 @@ type
tabVariables: TTabSheet; tabVariables: TTabSheet;
tabProcessList: TTabSheet; tabProcessList: TTabSheet;
ListVariables: TVirtualStringTree; ListVariables: TVirtualStringTree;
ListProcesses: TSortListView; ListProcesses: TVirtualStringTree;
popupHost: TPopupMenu; popupHost: TPopupMenu;
Kill1: TMenuItem; Kill1: TMenuItem;
NewDatabase1: TMenuItem; NewDatabase1: TMenuItem;
@ -329,8 +329,6 @@ type
Selected: Boolean); Selected: Boolean);
procedure ListColumnsSelectItem(Sender: TObject; Item: TListItem; procedure ListColumnsSelectItem(Sender: TObject; Item: TListItem;
Selected: Boolean); Selected: Boolean);
procedure ListProcessesSelectItem(Sender: TObject; Item: TListItem;
Selected: Boolean);
procedure DBMemo1Exit(Sender: TObject); procedure DBMemo1Exit(Sender: TObject);
procedure btnUnsafeEditClick(Sender: TObject); procedure btnUnsafeEditClick(Sender: TObject);
procedure gridMouseDown(Sender: TObject; Button: TMouseButton; procedure gridMouseDown(Sender: TObject; Button: TMouseButton;
@ -538,6 +536,7 @@ type
CachedTableLists : TStringList; CachedTableLists : TStringList;
QueryHelpersSelectedItems : Array[0..3] of Integer; QueryHelpersSelectedItems : Array[0..3] of Integer;
VTRowDataListVariables, VTRowDataListVariables,
VTRowDataListProcesses,
VTRowDataListCommandStats : Array of TVTreeData; VTRowDataListCommandStats : Array of TVTreeData;
function GetQueryRunning: Boolean; function GetQueryRunning: Boolean;
@ -1852,7 +1851,7 @@ begin
ValidateControls; ValidateControls;
// Show processlist if it's visible now but empty yet // Show processlist if it's visible now but empty yet
if ListProcesses.Items.Count = 0 then if ListProcesses.RootNodeCount = 0 then
ShowProcessList( self ); ShowProcessList( self );
end; end;
@ -2669,7 +2668,6 @@ end;
procedure TMDIChild.ShowProcessList(sender: TObject); procedure TMDIChild.ShowProcessList(sender: TObject);
var var
i,j : Integer; i,j : Integer;
n : TListItem;
ds : TDataSet; ds : TDataSet;
begin begin
// No need to update if it's not visible. // No need to update if it's not visible.
@ -2677,57 +2675,49 @@ begin
if PageControlHost.ActivePage <> tabProcesslist then exit; if PageControlHost.ActivePage <> tabProcesslist then exit;
Screen.Cursor := crSQLWait; Screen.Cursor := crSQLWait;
try try
ListProcesses.Items.BeginUpdate; ListProcesses.BeginUpdate;
ListProcesses.Items.Clear; ListProcesses.Clear;
debug('ShowProcessList()'); debug('ShowProcessList()');
ds := GetResults('SHOW FULL PROCESSLIST', false, false); ds := GetResults('SHOW FULL PROCESSLIST', false, false);
SetLength(VTRowDataListProcesses, ds.RecordCount);
for i:=1 to ds.RecordCount do for i:=1 to ds.RecordCount do
begin begin
n := ListProcesses.Items.Add; VTRowDataListProcesses[i-1].Captions := TStringList.Create;
n.Caption := ds.Fields[0].AsString; VTRowDataListProcesses[i-1].Captions.Add( ds.Fields[0].AsString );
if CompareText( ds.Fields[4].AsString, 'Killed') = 0 then if CompareText( ds.Fields[4].AsString, 'Killed') = 0 then
n.ImageIndex := 83 // killed VTRowDataListProcesses[i-1].ImageIndex := 83 // killed
else else
n.ImageIndex := 82; // running VTRowDataListProcesses[i-1].ImageIndex := 82; // running
for j := 1 to 7 do for j := 1 to 7 do
n.Subitems.Add(ds.Fields[j].AsString); VTRowDataListProcesses[i-1].Captions.Add(ds.Fields[j].AsString);
ds.Next; ds.Next;
end; end;
ds.Close; ds.Close;
ListProcesses.Items.EndUpdate; tabProcessList.Caption := 'Process-List (' + IntToStr(Length(VTRowDataListProcesses)) + ')';
// Remove existing column-sort-images
// (TODO: auomatically invoke this method in TSortListView itself)
ListProcesses.ClearSortColumnImages;
tabProcessList.Caption := 'Process-List (' + IntToStr(ListProcesses.Items.Count) + ')';
except except
on E: Exception do begin on E: Exception do begin
LogSQL('Error loading process list (automatic refresh disabled): ' + e.Message); LogSQL('Error loading process list (automatic refresh disabled): ' + e.Message);
TimerProcesslist.Enabled := false; TimerProcesslist.Enabled := false;
end; end;
end; end;
ListProcesses.Items.EndUpdate; ListProcesses.RootNodeCount := Length(VTRowDataListProcesses);
ListProcesses.EndUpdate;
Screen.Cursor := crDefault; Screen.Cursor := crDefault;
end; end;
procedure TMDIChild.ListProcessesSelectItem(Sender: TObject; Item: TListItem;
Selected: Boolean);
begin
Kill1.Enabled := (ListProcesses.Selected <> nil) and (PageControlHost.ActivePage = tabProcessList);
end;
procedure TMDIChild.KillProcess(Sender: TObject); procedure TMDIChild.KillProcess(Sender: TObject);
var t : Boolean; var t : Boolean;
NodeData : PVTreeData;
begin begin
if ListProcesses.Selected.Caption = IntToStr( MySQLConn.Connection.GetThreadId ) then NodeData := ListProcesses.GetNodeData(ListProcesses.FocusedNode);
if NodeData.Captions[0] = IntToStr( MySQLConn.Connection.GetThreadId ) then
MessageDlg('Fatal: Better not kill my own Process...', mtError, [mbok], 0) MessageDlg('Fatal: Better not kill my own Process...', mtError, [mbok], 0)
else begin else begin
t := TimerProcessList.Enabled; t := TimerProcessList.Enabled;
TimerProcessList.Enabled := false; // prevent av (ListProcesses.selected...) TimerProcessList.Enabled := false; // prevent av (ListProcesses.selected...)
if MessageDlg('Kill Process '+ListProcesses.Selected.Caption+'?', mtConfirmation, [mbok,mbcancel], 0) = mrok then if MessageDlg('Kill Process '+NodeData.Captions[0]+'?', mtConfirmation, [mbok,mbcancel], 0) = mrok then
begin begin
ExecUpdateQuery( 'KILL '+ListProcesses.Selected.Caption ); ExecUpdateQuery( 'KILL '+NodeData.Captions[0] );
ShowVariablesAndProcesses(self); ShowVariablesAndProcesses(self);
end; end;
TimerProcessList.Enabled := t; // re-enable autorefresh timer TimerProcessList.Enabled := t; // re-enable autorefresh timer
@ -2738,9 +2728,8 @@ end;
procedure TMDIChild.PageControlHostChange(Sender: TObject); procedure TMDIChild.PageControlHostChange(Sender: TObject);
begin begin
// Show processlist if it's visible now but empty yet // Show processlist if it's visible now but empty yet
if ListProcesses.Items.Count = 0 then if ListProcesses.RootNodeCount = 0 then
ShowProcessList( self ); ShowProcessList( self );
ListProcessesSelectItem(self, nil, False);
end; end;
@ -4450,6 +4439,7 @@ end;
procedure TMDIChild.popupHostPopup(Sender: TObject); procedure TMDIChild.popupHostPopup(Sender: TObject);
begin begin
MenuAutoupdate.Enabled := PageControlHost.ActivePage = tabProcessList; MenuAutoupdate.Enabled := PageControlHost.ActivePage = tabProcessList;
Kill1.Enabled := (PageControlHost.ActivePage = tabProcessList) and Assigned(ListProcesses.FocusedNode);
end; end;
procedure TMDIChild.ListTablesEditing(Sender: TObject; Item: TListItem; procedure TMDIChild.ListTablesEditing(Sender: TObject; Item: TListItem;
@ -5989,6 +5979,11 @@ begin
begin begin
NodeData.Captions := VTRowDataListCommandStats[Node.Index].Captions; NodeData.Captions := VTRowDataListCommandStats[Node.Index].Captions;
NodeData.ImageIndex := VTRowDataListCommandStats[Node.Index].ImageIndex; NodeData.ImageIndex := VTRowDataListCommandStats[Node.Index].ImageIndex;
end
else if Sender = ListProcesses then
begin
NodeData.Captions := VTRowDataListProcesses[Node.Index].Captions;
NodeData.ImageIndex := VTRowDataListProcesses[Node.Index].ImageIndex;
end; end;
end; end;
@ -6017,13 +6012,20 @@ procedure TMDIChild.vstGetText(Sender: TBaseVirtualTree; Node:
WideString); WideString);
var var
NodeData : PVTreeData; NodeData : PVTreeData;
i : Integer;
begin begin
// Get pointer to node which gets displayed // Get pointer to node which gets displayed
NodeData := Sender.GetNodeData(Node); NodeData := Sender.GetNodeData(Node);
case Column of // Column is -1 if no column headers are defined
-1: CellText := NodeData.Captions[0]; // Column is -1 if no column headers are defined if Column = -1 then
else CellText := NodeData.Captions[Column]; i := 0
end; else
i := Column;
// Avoid AV, don't exceed Captions content
if NodeData.Captions.Count > i then
CellText := NodeData.Captions[i]
else
CellText := '';
end; end;

View File

@ -73,11 +73,11 @@ begin
// which ListView to print? // which ListView to print?
case cwin.PageControlMain.ActivePageIndex of case cwin.PageControlMain.ActivePageIndex of
0 : case cwin.PageControlHost.ActivePageIndex of // 0 : case cwin.PageControlHost.ActivePageIndex of
// 0 : begin list := cwin.ListVariables; title := 'Server-Variables for ' + cwin.Conn.MysqlParams.Host; end; // 0 : begin list := cwin.ListVariables; title := 'Server-Variables for ' + cwin.Conn.MysqlParams.Host; end;
1 : begin list := cwin.ListProcesses; title := 'Processlist for ' + cwin.Conn.MysqlParams.Host; end; // 1 : begin list := cwin.ListProcesses; title := 'Processlist for ' + cwin.Conn.MysqlParams.Host; end;
// 2 : begin list := cwin.ListCommandStats; title := 'Command-statistics for ' + cwin.Conn.MysqlParams.Host; end; // 2 : begin list := cwin.ListCommandStats; title := 'Command-statistics for ' + cwin.Conn.MysqlParams.Host; end;
end; // end;
1 : begin list := cwin.ListTables; title := 'Tables-List for Database ' + cwin.ActualDatabase; end; 1 : begin list := cwin.ListTables; title := 'Tables-List for Database ' + cwin.ActualDatabase; end;
2 : begin list := cwin.ListColumns; title := 'Field-List for ' + cwin.ActualDatabase + '/' + cwin.ActualTable; end; 2 : begin list := cwin.ListColumns; title := 'Field-List for ' + cwin.ActualDatabase + '/' + cwin.ActualTable; end;
end; end;