unit StringGridUtils; (* {$IFDEF WIN32} {$ENDIF} {$IFDEF LINUX} {$ENDIF} *) interface uses {$IFDEF WIN32} SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Grids; {$ENDIF} {$IFDEF LINUX} SysUtils, Classes, Types, QGraphics, QControls, QForms, QDialogs, QStdCtrls, QGrids; {$ENDIF} {* Zweck: Move StringGrid Column by code: Die Klasse TStringGrid enthält die zwei benötigten procedures, die in abgeleiteten Klassen jedoch nicht(!!) public sind. Genau das wird jetzt in der neuen Klasse TStringGridHack gemacht = als public deklariert: Aufruf z.B.: TStringGridHack(GridMeldungen).MoveColumn(1, 3); *} type TStringGridHack = class(TStringGrid) public procedure MoveColumn(FromIndex, ToIndex: Longint); procedure MoveRow(FromIndex, ToIndex: Longint); end; type TMoveSG = class(TCustomGrid); procedure jSplitLine(const Line: string; Result: TStrings; Delimiter, QuoteChar: Char; StripQuotes: boolean); procedure LoadFromFile(Grid: TStringGrid; const FileName: string; const Delimiter: Char; QuoteChar: Char = '"'; StripQuotes: boolean = true); procedure SaveToFile(Grid: TStringGrid; const FileName: string; const Delimiter: Char; QuoteChar: Char); procedure AutoSizeGridColumn(Grid: TStringGrid; column: integer); function RemoveDoubleBlanks(AStr: string): string; procedure SortGridByCols(Grid: TStringGrid; ColOrder: array of Integer); procedure SortStringGrid(var GenStrGrid: TStringGrid; ThatCol: Integer); implementation {* BEISPIEL: Sort bei Klick auf TitleButton procedure TMainForm1.StringGrid1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var JCol, JRow: integer; begin StringGrid1.MouseToCell(X, Y, JCol, JRow); if JRow > 0 then exit; SortStringGrid(StringGrid1, JCol); end; *} procedure SortStringGrid(var GenStrGrid: TStringGrid; ThatCol: Integer); const TheSeparator = '@'; var CountItem, I, J, K, ThePosition: integer; MyList: TStringList; MyString, TempString: string; begin // Give the number of rows in the StringGrid CountItem := GenStrGrid.RowCount; //Create the List MyList := TStringList.Create; MyList.Sorted := False; try begin for I := 1 to (CountItem - 1) do MyList.Add(GenStrGrid.Rows[I].Strings[ThatCol] + TheSeparator + GenStrGrid.Rows[I].Text); //Sort the List Mylist.Sort; for K := 1 to Mylist.Count do begin //Take the String of the line (K - 1) MyString := MyList.Strings[(K - 1)]; //Find the position of the Separator in the String ThePosition := Pos(TheSeparator, MyString); TempString := ''; {Eliminate the Text of the column on which we have sorted the StringGrid} TempString := Copy(MyString, (ThePosition + 1), Length(MyString)); MyList.Strings[(K - 1)] := ''; MyList.Strings[(K - 1)] := TempString; end; // Refill the StringGrid for J := 1 to (CountItem - 1) do GenStrGrid.Rows[J].Text := MyList.Strings[(J - 1)]; end; finally //Free the List MyList.Free; end; end; { procedure TForm1.Button1Click(Sender: TObject); begin { Sort rows based on the contents of two or more columns. Sorts first by column 1. If there are duplicate values in column 1, the next sort column is column 2 and so on...} //SortGridByCols(StringGrid1, [1, 2, 0, 3, 4]); // end; procedure SortGridByCols(Grid: TStringGrid; ColOrder: array of Integer); var i, j: Integer; Sorted: Boolean; function Sort(Row1, Row2: Integer): Integer; var C: Integer; begin C := 0; Result := AnsiCompareStr(Grid.Cols[ColOrder[C]][Row1], Grid.Cols[ColOrder[C]][Row2]); if Result = 0 then begin Inc(C); while (C <= High(ColOrder)) and (Result = 0) do begin Result := AnsiCompareStr(Grid.Cols[ColOrder[C]][Row1], Grid.Cols[ColOrder[C]][Row2]); Inc(C); end; end; end; begin if SizeOf(ColOrder) div SizeOf(i) <> Grid.ColCount then Exit; for i := 0 to High(ColOrder) do if (ColOrder[i] < 0) or (ColOrder[i] >= Grid.ColCount) then Exit; j := 0; Sorted := False; repeat Inc(j); with Grid do for i := 0 to RowCount - 2 do if Sort(i, i + 1) > 0 then begin TMoveSG(Grid).MoveRow(i + 1, i); Sorted := False; end; until Sorted or (j = 1000); Grid.Repaint; end; procedure TStringGridHack.MoveColumn(FromIndex, ToIndex: Integer); begin inherited; end; procedure TStringGridHack.MoveRow(FromIndex, ToIndex: Integer); begin inherited; end; procedure jSplitLine(const Line: string; Result: TStrings; Delimiter, QuoteChar: Char; StripQuotes: boolean); var x, sLen, QuoteCount: Integer; s: string; IgnoreDelim: boolean; QuotedStr: PChar; begin {* Example with 3 Values delimited by comma: Line := Field1, "FirstName, LastName","""FirstName""" Line description: -> simple string, -> string contains Delimiter, -> quoted string example procedure call: jSplitLine(Line,ListBox1.Items) Result in ListBox1: Field1 FirstName, LastName "FirstName" *} s := ''; sLen := Length(Line); IgnoreDelim := false; QuoteCount := 0; Result.Clear; for x := 1 to sLen do begin if Line[x] = QuoteChar then begin inc(QuoteCount); {* A Delimiter surrounded by a pair of QuoteChar has to be ignored. See example above: "FirstName, LastName" therefor: *} IgnoreDelim := QuoteCount mod 2 <> 0; end; if IgnoreDelim then s := s + Line[x] else if Line[x] <> Delimiter then s := s + Line[x] else begin if s <> '' then begin if (StripQuotes and (s[1] = QuoteChar)) then begin QuotedStr := PChar(s); Result.Add(AnsiExtractQuotedStr(QuotedStr, QuoteChar)); end else Result.Add(s); end else Result.Add(s); s := ''; end; end; if s <> '' then begin if (StripQuotes and (s[1] = QuoteChar)) then begin QuotedStr := PChar(s); Result.Add(AnsiExtractQuotedStr(QuotedStr, QuoteChar)); end else Result.Add(s); end else Result.Add(s); end; procedure SaveToFile(Grid: TStringGrid; const FileName: string; const Delimiter: Char; QuoteChar: Char); var r, c: LongInt; BufStr, Value: string; Lines: TStringList; begin Lines := TStringList.Create; try Lines.Clear; for r := 0 to Grid.RowCount - 1 do begin BufStr := ''; for c := 0 to Grid.ColCount - 1 do begin {* added John *} Value := Grid.Cells[c, r]; if pos(Delimiter, Value) > 0 then Value := AnsiQuotedStr(Value, QuoteChar); {* end added John *} BufStr := BufStr + Value; if c <> (Grid.ColCount - 1) then BufStr := BufStr + Delimiter; end; Lines.Add(BufStr); end; Lines.SaveToFile(FileName); finally Lines.Free; end; end; procedure LoadFromFile(Grid: TStringGrid; const FileName: string; const Delimiter: Char; QuoteChar: Char = '"'; StripQuotes: boolean = true); var r: LongInt; Lines, Fields: TStringList; begin Lines := TStringList.Create; Fields := TStringList.Create; try Lines.LoadFromFile(FileName); Grid.RowCount := Lines.Count; Grid.ColCount := Grid.FixedCols + 1; for r := 0 to Lines.Count - 1 do begin //TokenizeGridString(Lines[r], Delimiter, Fields); {* added John *} jSplitLine(Lines[r], Fields, Delimiter, QuoteChar, StripQuotes); if Fields.Count > Grid.ColCount then Grid.ColCount := Fields.Count; Grid.Rows[r].Assign(Fields); end; finally Fields.Free; Lines.Free; end; end; procedure AutoSizeGridColumn(Grid: TStringGrid; column: integer); var i: integer; temp: integer; max: integer; begin max := 0; for i := 0 to (Grid.RowCount - 1) do begin temp := Grid.Canvas.TextWidth(grid.cells[column, i]); if temp > max then max := temp; end; Grid.ColWidths[column] := Max + Grid.GridLineWidth + 3; end; function RemoveDoubleBlanks(AStr: string): string; var tmp: string; {* BEGIN: Hilfsfunktionen aus JvJCLUtils.pas *} function DelBSpace(const S: string): string; var I, L: Integer; begin L := Length(S); I := 1; while (I <= L) and (S[I] = ' ') do Inc(I); Result := Copy(S, I, MaxInt); end; function Copy2Symb(const S: string; Symb: Char): string; var P: Integer; begin P := Pos(Symb, S); if P = 0 then P := Length(S) + 1; Result := Copy(S, 1, P - 1); end; function Copy2SymbDel(var S: string; Symb: Char): string; begin Result := Copy2Symb(S, Symb); S := DelBSpace(Copy(S, Length(Result) + 1, Length(S))); end; {* END: Hilfsfunktionen aus JvJCLUtils.pas *} begin AStr := trim(AStr); Result := ''; {* Result := '' -> komisch,aber notwendig *} tmp := Copy2SymbDel(AStr, ' '); while tmp <> '' do begin Result := trim(Result) + ' ' + tmp; tmp := Copy2SymbDel(AStr, ' '); end; end; end.