LCL: TListViewItems: implemented Exchange() and Move() to easy move or exchange items in list. Implementation done for gtk2 and qt

git-svn-id: trunk@31358 -
This commit is contained in:
zeljko 2011-06-24 12:04:01 +00:00
parent bf3766ff9c
commit 8bdf87abed
9 changed files with 305 additions and 23 deletions

View File

@ -847,6 +847,8 @@ type
destructor Destroy; override;
procedure Delete(const AIndex : Integer);
procedure EndUpdate;
procedure Exchange(const AIndex1, AIndex2: Integer);
procedure Move(const AFromIndex, AToIndex: Integer);
function FindCaption(StartIndex: Integer; Value: string;
Partial, Inclusive, Wrap: Boolean;
PartStart: Boolean = True): TListItem;

View File

@ -219,7 +219,8 @@ begin
// LVN_ODSTATECHANGED:
// LVN_BEGINLABELEDIT:
// LVN_ENDLABELEDIT:
LVN_COLUMNCLICK: begin
LVN_COLUMNCLICK:
begin
ColClick(Columns[nm^.iSubItem]);
end;
LVN_INSERTITEM: begin
@ -998,7 +999,7 @@ end;
procedure TCustomListView.SetItemIndex(const AValue: Integer);
begin
if (AValue < -1) or (AValue >= Items.Count) then
raise Exception.Create(Format('index %d out of bounds',[AValue]));
raise Exception.CreateFmt(rsListIndexExceedsBounds,[AValue]);
if AValue = -1 then
Selected := nil
else
@ -1013,7 +1014,7 @@ var
i: integer;
begin
if (AValue<>nil) and (AValue.ListView<>Self) then
raise Exception.Create('item does not belong to this listview');
raise Exception.Create('Item does not belong to this listview');
if FSelected = AValue then Exit;
//DebugLn('TCustomListView.SetSelection FSelected=',dbgs(FSelected));
if AValue = nil then begin

View File

@ -144,6 +144,40 @@ begin
Owner.EndUpdate;
end;
procedure TListItems.Exchange(const AIndex1, AIndex2: Integer);
var
AItem: TListItem;
begin
if (AIndex1 < 0) or (AIndex1 >= FItems.Count) then
raise Exception.CreateFmt(rsListIndexExceedsBounds, [AIndex1]);
if (AIndex2 < 0) or (AIndex2 >= FItems.Count) then
raise Exception.CreateFmt(rsListIndexExceedsBounds, [AIndex2]);
AItem := Item[AIndex1];
FItems.Exchange(AIndex1, AIndex2);
if WSUpdateAllowed then
begin
TWSCustomListViewClass(FOwner.WidgetSetClass).ItemExchange(FOwner, AItem,
AIndex1, AIndex2);
end;
end;
procedure TListItems.Move(const AFromIndex, AToIndex: Integer);
var
AItem: TListItem;
begin
if (AFromIndex < 0) or (AFromIndex >= FItems.Count) then
raise Exception.CreateFmt(rsListIndexExceedsBounds, [AFromIndex]);
if (AToIndex < 0) or (AToIndex >= FItems.Count) then
raise Exception.CreateFmt(rsListIndexExceedsBounds, [AToIndex]);
AItem := Item[AFromIndex];
FItems.Move(AFromIndex, AToIndex);
if WSUpdateAllowed then
begin
TWSCustomListViewClass(FOwner.WidgetSetClass).ItemMove(FOwner, AItem,
AFromIndex, AToIndex);
end;
end;
{------------------------------------------------------------------------------}
{ TListItems IntfCreateItem }
{------------------------------------------------------------------------------}

View File

@ -367,7 +367,7 @@ begin
gtk_tree_path_free(APath);
WidgetInfo := PWidgetInfo(data);
// DebugLn(['LCLIntfCellRenderer_CellDataFunc stamp=',iter^.stamp,' tree_model=',dbgs(tree_model),' cell=',dbgs(cell),' WidgetInfo=',WidgetInfo <> nil]);
// DebugLn(['LCLIntfCellRenderer_CellDataFunc stamp=',iter^.stamp,' tree_model=',dbgs(tree_model),' cell=',dbgs(cell),' WidgetInfo=',WidgetInfo <> nil,' Time=',TimeToStr(Now)]);
if (WidgetInfo <> nil) and
(WidgetInfo^.LCLObject is TCustomComboBox) and
@ -382,6 +382,7 @@ begin
end else
if (WidgetInfo <> nil) and (WidgetInfo^.LCLObject.InheritsFrom(TCustomListView)) then
begin
// DebugLn(['LCLIntfCellRenderer_CellDataFunc stamp=',iter^.stamp,' tree_model=',dbgs(tree_model),' cell=',dbgs(cell),' WidgetInfo=',WidgetInfo <> nil,' Time=',TimeToStr(Now)]);
Value.g_type := G_TYPE_STRING;
gtk_tree_model_get(tree_model, iter, [0, @ListItem, -1]);
if (ListItem = nil) and TCustomListView(WidgetInfo^.LCLObject).OwnerData then

View File

@ -96,7 +96,7 @@ type
class procedure AddRemoveCheckboxRenderer(const ALV: TCustomListView; const WidgetInfo: PWidgetInfo; const Add: Boolean);
class function GetViewModel(const AView: PGtkWidget): PGtkTreeModel;
protected
class procedure SetCallbacks(const AScrollWidget: PGtkWidget; const Widgets: PTVWidgets; const AWidgetInfo: PWidgetInfo); virtual;
class procedure SetListCallbacks(const AScrollWidget: PGtkWidget; const Widgets: PTVWidgets; const AWidgetInfo: PWidgetInfo);
published
// columns
class procedure ColumnDelete(const ALV: TCustomListView; const AIndex: Integer); override;
@ -115,6 +115,8 @@ type
// items
class procedure ItemDelete(const ALV: TCustomListView; const AIndex: Integer); override;
class function ItemDisplayRect(const ALV: TCustomListView; const AIndex, ASubItem: Integer; ACode: TDisplayCode): TRect; override;
class procedure ItemExchange(const ALV: TCustomListView; AItem: TListItem; const AIndex1, AIndex2: Integer); override;
class procedure ItemMove(const ALV: TCustomListView; AItem: TListItem; const AFromIndex, AToIndex: Integer); override;
class function ItemGetChecked(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem): Boolean; override;
class function ItemGetState(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem; const AState: TListItemState; out AIsSet: Boolean): Boolean; override; // returns True if supported
class procedure ItemInsert(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem); override;

View File

@ -125,7 +125,7 @@ begin
// property of the item changed or the appropriate image list changed in the
// list view. Change is ctState if the Cut, Focused, or Selected property of
// the item changed.
//DebugLn('Gtk2_ItemChanged');
// DebugLn('Gtk2_ItemChanged');
end;
@ -574,7 +574,7 @@ begin
Result := nil;
end;
class procedure TGtk2WSCustomListView.SetCallbacks(const AScrollWidget: PGtkWidget;
class procedure TGtk2WSCustomListView.SetListCallbacks(const AScrollWidget: PGtkWidget;
const Widgets: PTVWidgets; const AWidgetInfo: PWidgetInfo);
begin
TGtk2WSBaseScrollingWinControl.SetCallbacks(AScrollWidget, AWidgetInfo);
@ -969,6 +969,76 @@ begin
end;
end;
class procedure TGtk2WSCustomListView.ItemExchange(const ALV: TCustomListView;
AItem: TListItem; const AIndex1, AIndex2: Integer);
var
Widgets: PTVWidgets;
Path: PGtkTreePath;
ItemRect: TGdkRectangle;
begin
if not WSCheckHandleAllocated(ALV, 'ItemExchange') then
exit;
// gtk2 needs only update
GetCommonTreeViewWidgets(PGtkWidget(ALV.Handle), Widgets);
if GetViewModel(Widgets^.MainView) = nil then
exit;
if not gtk_widget_realized(Widgets^.MainView) then
exit;
if GTK_IS_TREE_VIEW(Widgets^.MainView) then
begin
Path := gtk_tree_path_new_from_indices(AIndex1, -1);
gtk_tree_view_get_cell_area(PGtkTreeView(Widgets^.MainView), Path, nil, @ItemRect);
gtk_tree_path_free(Path);
if ItemRect.height = 0 then
begin
Path := gtk_tree_path_new_from_indices(AIndex2, -1);
gtk_tree_view_get_cell_area(PGtkTreeView(Widgets^.MainView), Path, nil, @ItemRect);
gtk_tree_path_free(Path);
end;
end else
ItemRect.height := 1; // force redraw
if ItemRect.height <> 0 then // item is visible
gtk_widget_queue_draw(Widgets^.MainView);
end;
class procedure TGtk2WSCustomListView.ItemMove(const ALV: TCustomListView;
AItem: TListItem; const AFromIndex, AToIndex: Integer);
var
Widgets: PTVWidgets;
Path: PGtkTreePath;
ItemRect: TGdkRectangle;
begin
if not WSCheckHandleAllocated(ALV, 'ItemMove') then
exit;
// gtk2 needs only update
GetCommonTreeViewWidgets(PGtkWidget(ALV.Handle), Widgets);
if GetViewModel(Widgets^.MainView) = nil then
exit;
if not gtk_widget_realized(Widgets^.MainView) then
exit;
if GTK_IS_TREE_VIEW(Widgets^.MainView) then
begin
Path := gtk_tree_path_new_from_indices(AFromIndex, -1);
gtk_tree_view_get_cell_area(PGtkTreeView(Widgets^.MainView), Path, nil, @ItemRect);
gtk_tree_path_free(Path);
if ItemRect.height = 0 then
begin
Path := gtk_tree_path_new_from_indices(AToIndex, -1);
gtk_tree_view_get_cell_area(PGtkTreeView(Widgets^.MainView), Path, nil, @ItemRect);
gtk_tree_path_free(Path);
end;
end else
ItemRect.height := 1; // force redraw
if ItemRect.height <> 0 then // item is visible
gtk_widget_queue_draw(Widgets^.MainView);
end;
class function TGtk2WSCustomListView.ItemGetChecked(const ALV: TCustomListView;
const AIndex: Integer; const AItem: TListItem): Boolean;
begin
@ -1454,9 +1524,7 @@ begin
//SetMainWidget(ScrollWidget, MainView);
//GetWidgetInfo(ScrollWidget, True)^.CoreWidget := PGtkWidget(MainView);
gtk_widget_show_all(PGtkWidget(MainView));
SetCallbacks(PGtkWidget(ScrollWidget), Widgets, WidgetInfo);
// SetCallbacks(PGtkWidget(MainView), Widgets^, WidgetInfo);
SetListCallbacks(PGtkWidget(ScrollWidget), Widgets, Widgets^.WidgetInfo);
end;
(*

View File

@ -1144,7 +1144,8 @@ type
procedure scrollToItem(row: integer; hint: QAbstractItemViewScrollHint);
procedure removeItem(AIndex: Integer);
function rowCount: integer;
procedure exchangeItems(AIndex1, AIndex2: Integer);
procedure ExchangeItems(const AIndex1, AIndex2: Integer);
procedure MoveItem(const AFromIndex, AToIndex: Integer);
property ItemCount: Integer read getItemCount write setItemCount;
end;
@ -1282,6 +1283,8 @@ type
public
procedure AttachEvents; override;
procedure DetachEvents; override;
procedure ExchangeItems(const AIndex1, AIndex2: Integer);
procedure MoveItem(const AFromIndex, AToIndex: Integer);
function getClientBounds: TRect; override;
function getClientOffset: TPoint; override;
@ -1300,6 +1303,7 @@ type
property MaxColSize[ACol: Integer]: Integer read getMaxColSize write setMaxColSize;
property MinColSize[ACol: Integer]: Integer read getMinColSize write setMinColSize;
property SortEnabled: Boolean read getSortEnabled write setSortEnabled;
property Sorting: Boolean read FSorting;
end;
{TQtTableView}
@ -9565,9 +9569,9 @@ begin
Result := QListWidget_count(QListWidgetH(Widget));
end;
procedure TQtListWidget.exchangeItems(AIndex1, AIndex2: Integer);
procedure TQtListWidget.ExchangeItems(const AIndex1, AIndex2: Integer);
var
Item1, Item2: QListWidgetItemH;
ItemTo, ItemFrom: QListWidgetItemH;
R: TRect;
begin
if AIndex1 = AIndex2 then
@ -9579,22 +9583,40 @@ begin
if AIndex1 < AIndex2 then
begin
Item1 := QListWidget_takeItem(QListWidgetH(Widget), AIndex1);
Item2 := QListWidget_takeItem(QListWidgetH(Widget), AIndex2 - 1);
QListWidget_insertItem(QListWidgetH(Widget), AIndex1, Item2);
QListWidget_insertItem(QListWidgetH(Widget), AIndex2, Item1);
ItemTo := QListWidget_takeItem(QListWidgetH(Widget), AIndex2);
ItemFrom := QListWidget_takeItem(QListWidgetH(Widget), AIndex1);
QListWidget_insertItem(QListWidgetH(Widget), AIndex1, ItemTo);
QListWidget_insertItem(QListWidgetH(Widget), AIndex2, ItemFrom);
end else
begin
Item1 := QListWidget_takeItem(QListWidgetH(Widget), AIndex2);
Item2 := QListWidget_takeItem(QListWidgetH(Widget), AIndex1 - 1);
QListWidget_insertItem(QListWidgetH(Widget), AIndex2, Item2);
QListWidget_insertItem(QListWidgetH(Widget), AIndex1, Item1);
ItemFrom := QListWidget_takeItem(QListWidgetH(Widget), AIndex1);
ItemTo := QListWidget_takeItem(QListWidgetH(Widget), AIndex2);
QListWidget_insertItem(QListWidgetH(Widget), AIndex2, ItemFrom);
QListWidget_insertItem(QListWidgetH(Widget), AIndex1, ItemTo);
end;
if OwnerDrawn then
begin
R := getVisualItemRect(Item1);
R := getVisualItemRect(ItemTo);
Update(@R);
R := getVisualItemRect(Item2);
R := getVisualItemRect(ItemFrom);
Update(@R);
end;
end;
procedure TQtListWidget.MoveItem(const AFromIndex, AToIndex: Integer);
var
Item: QListWidgetItemH;
R: TRect;
begin
if (currentRow = AFromIndex) or (currentRow = AToIndex) then
if (getSelectionMode = QAbstractItemViewSingleSelection) then
setCurrentRow(-1);
Item := QListWidget_takeItem(QListWidgetH(Widget), AFromIndex);
QListWidget_insertItem(QListWidgetH(Widget), AToIndex, Item);
if OwnerDrawn then
begin
R := getVisualItemRect(Item);
Update(@R);
end;
end;
@ -10505,8 +10527,31 @@ begin
end;
procedure TQtTreeWidget.sortItems(Acolumn: Integer; AOrder: QtSortOrder);
var
i: Integer;
j: Integer;
v: QVariantH;
Item: QTreeWidgetItemH;
p: PtrUInt;
AOk: Boolean;
begin
QTreeWidget_sortItems(QTreeWidgetH(Widget), AColumn, AOrder);
AOk := False;
TListView(LCLObject).BeginUpdate;
for i := 0 to ItemCount - 1 do
begin
v := QVariant_create();
Item := topLevelItem(i);
QTreeWidgetItem_data(Item, v, 0, Ord(QtUserRole));
if QVariant_isValid(v) and not QVariant_isNull(v) then
begin
P := QVariant_toUInt(v, @AOk);
if AOk and (p > 0) then
TListView(LCLObject).Items.Move(TListItem(p).Index, i);
end;
QVariant_destroy(v);
end;
TListView(LCLObject).EndUpdate;
end;
procedure TQtTreeWidget.AttachEvents;
@ -10576,6 +10621,60 @@ begin
inherited DetachEvents;
end;
procedure TQtTreeWidget.ExchangeItems(const AIndex1, AIndex2: Integer);
var
ItemFrom: QTreeWidgetItemH;
ItemTo: QTreeWidgetItemH;
R: TRect;
begin
if AIndex1 = AIndex2 then
exit;
if (currentRow = AIndex1) or (currentRow = AIndex2) then
if (getSelectionMode = QAbstractItemViewSingleSelection) then
setCurrentRow(-1);
if AIndex1 < AIndex2 then
begin
ItemTo := takeTopLevelItem(AIndex2);
ItemFrom := takeTopLevelItem(AIndex1);
insertTopLevelItem(AIndex1, ItemTo);
insertTopLevelItem(AIndex2, ItemFrom);
end else
begin
ItemFrom := takeTopLevelItem(AIndex1);
ItemTo := takeTopLevelItem(AIndex2);
insertTopLevelItem(AIndex2, ItemFrom);
insertTopLevelItem(AIndex1, ItemTo);
end;
if OwnerDrawn then
begin
R := VisualItemRect(ItemFrom);
Update(@R);
R := VisualItemRect(ItemTo);
Update(@R);
end;
end;
procedure TQtTreeWidget.MoveItem(const AFromIndex, AToIndex: Integer);
var
Item: QTreeWidgetItemH;
R: TRect;
begin
if (currentRow = AFromIndex) or (currentRow = AToIndex) then
if (getSelectionMode = QAbstractItemViewSingleSelection) then
setCurrentRow(-1);
Item := takeTopLevelItem(AFromIndex);
insertTopLevelItem(AToIndex, Item);
if OwnerDrawn then
begin
R := VisualItemRect(Item);
Update(@R);
end;
end;
function TQtTreeWidget.getClientBounds: TRect;
var
Offset: Integer;

View File

@ -92,6 +92,8 @@ type
{items}
class procedure ItemInsert(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem); override;
class procedure ItemDelete(const ALV: TCustomListView; const AIndex: Integer); override;
class procedure ItemExchange(const ALV: TCustomListView; AItem: TListItem; const AIndex1, AIndex2: Integer); override;
class procedure ItemMove(const ALV: TCustomListView; AItem: TListItem; const AFromIndex, AToIndex: Integer); override;
class function ItemGetChecked(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem): Boolean; override;
class procedure ItemSetChecked(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem; const AChecked: Boolean); override;
class function ItemGetPosition(const ALV: TCustomListView; const AIndex: Integer): TPoint; override;
@ -1031,6 +1033,60 @@ begin
end;
end;
class procedure TQtWSCustomListView.ItemExchange(const ALV: TCustomListView;
AItem: TListItem; const AIndex1, AIndex2: Integer);
var
QtTreeWidget: TQtTreeWidget;
QtListWidget: TQtListWidget;
ItemFrom: QTreeWidgetItemH;
ItemTo: QTreeWidgetItemH;
begin
if not WSCheckHandleAllocated(ALV, 'ItemExchange') then
Exit;
if IsIconView(ALV) then
begin
QtListWidget := TQtListWidget(ALV.Handle);
QtListWidget.BeginUpdate;
QtListWidget.ExchangeItems(AIndex1, AIndex2);
QtListWidget.EndUpdate;
end else
begin
QtTreeWidget := TQtTreeWidget(ALV.Handle);
if QtTreeWidget.Sorting then
exit;
QtTreeWidget.BeginUpdate;
QtTreeWidget.ExchangeItems(AIndex1, AIndex2);
QtTreeWidget.EndUpdate;
end;
end;
class procedure TQtWSCustomListView.ItemMove(const ALV: TCustomListView;
AItem: TListItem; const AFromIndex, AToIndex: Integer);
var
QtTreeWidget: TQtTreeWidget;
QtListWidget: TQtListWidget;
begin
if not WSCheckHandleAllocated(ALV, 'ItemMove') then
Exit;
if IsIconView(ALV) then
begin
QtListWidget := TQtListWidget(ALV.Handle);
QtListWidget.BeginUpdate;
QtListWidget.MoveItem(AFromIndex, AToIndex);
QtListWidget.EndUpdate;
end else
begin
QtTreeWidget := TQtTreeWidget(ALV.Handle);
if QtTreeWidget.Sorting then
exit;
QtTreeWidget.BeginUpdate;
QtTreeWidget.MoveItem(AFromIndex, AToIndex);
// Item := QtTreeWidget.takeTopLevelItem(AFromIndex);
// QtTreeWidget.insertTopLevelItem(AToIndex, Item);
QtTreeWidget.EndUpdate;
end;
end;
{------------------------------------------------------------------------------
Method: TQtWSCustomListView.ItemGetChecked
Params: None
@ -1312,6 +1368,7 @@ var
Str: WideString;
i: Integer;
AAlignment: QtAlignment;
v: QVariantH;
begin
if not WSCheckHandleAllocated(ALV, 'ItemInsert') then
Exit;
@ -1356,6 +1413,12 @@ begin
QtTreeWidget.setItemText(TWI, i + 1, Str, AAlignment);
end;
end;
v := QVariant_create(QWord(PtrUInt(AItem)));
try
QTreeWidgetItem_setData(TWI, 0, Ord(QtUserRole), v);
finally
QVariant_destroy(v);
end;
QtTreeWidget.insertTopLevelItem(AIndex, TWI);
end;
end;

View File

@ -98,6 +98,8 @@ type
// Item
class procedure ItemDelete(const ALV: TCustomListView; const AIndex: Integer); virtual;
class function ItemDisplayRect(const ALV: TCustomListView; const AIndex, ASubItem: Integer; ACode: TDisplayCode): TRect; virtual;
class procedure ItemExchange(const ALV: TCustomListView; AItem: TListItem; const AIndex1, AIndex2: Integer); virtual;
class procedure ItemMove(const ALV: TCustomListView; AItem: TListItem; const AFromIndex, AToIndex: Integer); virtual;
class function ItemGetChecked(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem): Boolean; virtual;
class function ItemGetPosition(const ALV: TCustomListView; const AIndex: Integer): TPoint; virtual;
class function ItemGetState(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem; const AState: TListItemState; out AIsSet: Boolean): Boolean; virtual; // returns True if supported
@ -335,6 +337,16 @@ begin
Result := Rect(0,0,0,0);
end;
class procedure TWSCustomListView.ItemExchange(const ALV: TCustomListView;
AItem: TListItem; const AIndex1, AIndex2: Integer);
begin
end;
class procedure TWSCustomListView.ItemMove(const ALV: TCustomListView;
AItem: TListItem; const AFromIndex, AToIndex: Integer);
begin
end;
class function TWSCustomListView.ItemGetChecked(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem): Boolean;
begin
Result := False;