1{%MainUnit gtkwscomctrls.pp}
2{ $Id: gtkwscustomlistview.inc 57164 2018-01-27 18:12:35Z ondrej $
3
4 *****************************************************************************
5  This file is part of the Lazarus Component Library (LCL)
6
7  See the file COPYING.modifiedLGPL.txt, included in this distribution,
8  for details about the license.
9 *****************************************************************************
10}
11
12
13{ TGtkWSCustomListView }
14
15type
16  TLVHack = class(TCustomListView)
17  end;
18
19  PCustomListViewData = ^TCustomListViewData;
20  TCustomListViewData = record
21    ScrollingData: TBaseScrollingWinControlData;
22    ViewStyle: TViewStyle;
23  end;
24
25////////////////////////////////////////////////////////////////////////////////
26// Event code
27////////////////////////////////////////////////////////////////////////////////
28
29
30
31//----------------------
32//HDN_ENDTRACK
33//HDN_TRACK
34function GtkWSCustomListView_AbortColumnResize(AList: PGTKCList; AInfo: PWidgetInfo): GBoolean; cdecl;
35begin
36  //TODO: implement
37  Result := False;
38end;
39
40//----------------------
41//HDN_ENDTRACK
42//HDN_TRACK
43//HDN_ITEMCHANGED
44//HDN_ITEMCHANGING
45function GtkWSCustomListView_ResizeColumn(AList: PGTKCList; AColumn, AWidth: Integer; AInfo: PWidgetInfo): GBoolean; cdecl;
46begin
47  //TODO: implement
48  Result := False;
49end;
50
51//----------------------
52//HDN_ITEMCLICK
53//LVN_COLUMNCLICK
54function GtkWSCustomListView_ClickColumn(AList: PGTKCList; AColumn: Integer; AInfo: PWidgetInfo): GBoolean; cdecl;
55var
56  msg: TLMNotify;
57  NM: TNMListView;
58  ALV: TListView;
59begin
60  // this can happen when no columns are added which crashes the program
61  ALV := TListView(AInfo^.LCLObject);
62  if AColumn > ALV.Columns.Count-1 then
63    Exit;
64
65  msg.Msg := CN_NOTIFY;
66
67  FillChar(NM, SizeOf(NM), 0);
68  NM.hdr.hwndfrom := PtrUInt(AList);
69  NM.hdr.code := LVN_COLUMNCLICK;
70  NM.iItem := -1;
71  NM.iSubItem := AColumn;
72  msg.NMHdr := @NM.hdr;
73  Result := DeliverMessage(AInfo^.LCLObject, msg) = 0;
74end;
75
76//----------------------
77//LVN_DELETEITEM
78//LVN_INSERTITEM
79function GtkWSCustomListView_RowMove(AList: PGTKCList; AnOldIdx, ANewIdx: Integer; AInfo: PWidgetInfo): GBoolean; cdecl;
80var
81  msg: TLMNotify;
82  NM: TNMListView;
83  r: Boolean;
84begin
85  // Simulate move by remove and insert
86  msg.Msg := CN_NOTIFY;
87
88  FillChar(NM, SizeOf(NM), 0);
89  NM.hdr.hwndfrom := PtrUInt(AList);
90  NM.hdr.code := LVN_DELETEITEM;
91  NM.iItem := AnOldIdx;
92  msg.NMHdr := @NM.hdr;
93  r := DeliverMessage(AInfo^.LCLObject, msg) = 0;
94
95  NM.hdr.code := LVN_INSERTITEM;
96  NM.iItem := ANewIdx;
97  Result := (DeliverMessage(AInfo^.LCLObject, msg) = 0) and r;
98end;
99
100//----------------------
101//LVN_ITEMCHANGED
102//LVN_ITEMCHANGING
103function GtkWSCustomListView_SelectRow(AList: PGTKCList; ARow, AColumn: Integer; AEvent: PGDKEventButton; AInfo: PWidgetInfo): GBoolean; cdecl;
104var
105  msg: TLMNotify;
106  NM: TNMListView;
107begin
108  msg.Msg := CN_NOTIFY;
109
110  FillChar(NM, SizeOf(NM), 0);
111  NM.hdr.hwndfrom := PtrUInt(AList);
112  NM.hdr.code := LVN_ITEMCHANGED;
113  NM.iItem := ARow;
114  NM.iSubItem := AColumn;
115  NM.uNewState := LVIS_SELECTED;
116  NM.uChanged := LVIF_STATE;
117  msg.NMHdr := @NM.hdr;
118  Result := DeliverMessage(AInfo^.LCLObject, msg) = 0;
119end;
120
121function GtkWSCustomListView_UnSelectRow(AList: PGTKCList; ARow, AColumn: Integer; AEvent: PGDKEventButton; AInfo: PWidgetInfo): GBoolean; cdecl;
122var
123  msg: TLMNotify;
124  NM: TNMListView;
125begin
126  msg.Msg := CN_NOTIFY;
127
128  FillChar(NM, SizeOf(NM), 0);
129  NM.hdr.hwndfrom := PtrUInt(AList);
130  NM.hdr.code := LVN_ITEMCHANGED;
131  NM.iItem := ARow;
132  NM.iSubItem := AColumn;
133  NM.uOldState := LVIS_SELECTED;
134  NM.uChanged := LVIF_STATE;
135  msg.NMHdr := @NM.hdr;
136  Result := DeliverMessage(AInfo^.LCLObject, msg) = 0;
137end;
138
139function GtkWSCustomListView_ToggleFocusRow(AList: PGTKCList; AInfo: PWidgetInfo): GBoolean; cdecl;
140var
141  msg: TLMNotify;
142  NM: TNMListView;
143begin
144  // the defocus of the oldrow isn't send
145
146  msg.Msg := CN_NOTIFY;
147
148  FillChar(NM, SizeOf(NM), 0);
149  NM.hdr.hwndfrom := PtrUInt(AList);
150  NM.hdr.code := LVN_ITEMCHANGED;
151  NM.iItem := AList^.focus_row;
152  NM.iSubItem := 0;
153  NM.uNewState := LVIS_FOCUSED;
154  NM.uChanged := LVIF_STATE;
155  msg.NMHdr := @NM.hdr;
156  Result := DeliverMessage(AInfo^.LCLObject, msg) = 0;
157end;
158
159function GtkWSCustomListView_SelectAll(AList: PGTKCList; AInfo: PWidgetInfo): GBoolean; cdecl;
160var
161  msg: TLMNotify;
162  NM: TNMListView;
163  ListView: TListView;
164  n: Integer;
165begin
166  msg.Msg := CN_NOTIFY;
167
168  ListView := AInfo^.LCLObject as TListView;
169
170  FillChar(NM, SizeOf(NM), 0);
171  NM.hdr.hwndfrom := PtrUInt(AList);
172  NM.hdr.code := LVN_ITEMCHANGED;
173  for n := 0 to Listview.Items.Count - 1 do
174  begin
175    if ListView.Items[n].Selected
176    then Continue;
177    NM.iItem := n;
178    NM.iSubItem := -1;
179    NM.uNewState := LVIS_SELECTED;
180    NM.uChanged := LVIF_STATE;
181    msg.NMHdr := @NM.hdr;
182    Result := DeliverMessage(AInfo^.LCLObject, msg) = 0;
183  end;
184end;
185
186function GtkWSCustomListView_UnSelectAll(AList: PGTKCList; AInfo: PWidgetInfo): GBoolean; cdecl;
187var
188  msg: TLMNotify;
189  NM: TNMListView;
190  ListView: TListView;
191  n: Integer;
192begin
193  msg.Msg := CN_NOTIFY;
194
195  ListView := AInfo^.LCLObject as TListView;
196
197  FillChar(NM, SizeOf(NM), 0);
198  NM.hdr.hwndfrom := PtrUInt(AList);
199  NM.hdr.code := LVN_ITEMCHANGED;
200  for n := 0 to Listview.Items.Count - 1 do
201  begin
202    if not ListView.Items[n].Selected
203    then Continue;
204    NM.iItem := n;
205    NM.iSubItem := -1;
206    NM.uOldState := LVIS_SELECTED;
207    NM.uChanged := LVIF_STATE;
208    msg.NMHdr := @NM.hdr;
209    Result := DeliverMessage(AInfo^.LCLObject, msg) = 0;
210  end;
211end;
212
213function GtkWSCustomListView_EndSelection(AList: PGTKCList; AInfo: PWidgetInfo): GBoolean; cdecl;
214begin
215  Result:=true;
216end;
217
218////////////////////////////////////////////////////////////////////////////////
219// Column code
220////////////////////////////////////////////////////////////////////////////////
221
222class procedure TGtkWSCustomListView.ColumnDelete(const ALV: TCustomListView; const AIndex: Integer);
223var
224  WidgetInfo: PWidgetInfo;
225  CListWidget: PGtkCList;
226begin
227  if not WSCheckHandleAllocated(ALV, 'ColumnDelete') then Exit;
228
229  // allow only column modifications when in report mode
230  if TLVHack(ALV).ViewStyle <> vsReport then Exit;
231
232  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
233  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
234  if CListWidget^.columns = TLVHack(ALV).Columns.Count then Exit; // possible delayed update
235  if AIndex >= CListWidget^.columns then Exit; // ???
236
237  RecreateWnd(ALV);
238end;
239
240class function TGtkWSCustomListView.ColumnGetWidth(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn): Integer;
241var
242  WidgetInfo: PWidgetInfo;
243  CListWidget: PGtkCList;
244  CListColumn: PGtkCListColumn;
245begin
246  Result := -1;
247  if not WSCheckHandleAllocated(ALV, 'ColumnGetSize') then Exit;
248
249  // allow only column modifications when in report mode
250  if TLVHack(ALV).ViewStyle <> vsReport then Exit;
251
252  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
253  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
254
255  // there is no get width function, so we need some internal hacking
256  if AIndex >= CListWidget^.columns then Exit;
257  CListColumn := CListWidget^.Column;
258  Inc(CListColumn, AIndex);
259  Result := CListColumn^.width;
260end;
261
262class procedure TGtkWSCustomListView.ColumnInsert(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn);
263var
264  WidgetInfo: PWidgetInfo;
265  CListWidget: PGtkCList;
266begin
267  if not WSCheckHandleAllocated(ALV, 'ColumnInsert') then Exit;
268
269  // allow only column modifications when in report mode
270  if TLVHack(ALV).ViewStyle <> vsReport then Exit;
271
272  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
273  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
274  if CListWidget^.columns = TLVHack(ALV).Columns.Count then Exit; // possible delayed update
275
276  RecreateWnd(ALV);
277end;
278
279class procedure TGtkWSCustomListView.ColumnMove(const ALV: TCustomListView; const AOldIndex, ANewIndex: Integer; const AColumn: TListColumn);
280  procedure CopyColumn(const AList: PGtkCList; const AIndex: Integer; const ASrc: PGtkCListColumn);
281  begin
282    gtk_clist_set_column_title(AList, AIndex, ASrc^.title);
283    gtk_clist_set_column_min_width(AList, AIndex, ASrc^.min_width);
284    gtk_clist_set_column_max_width(AList, AIndex, ASrc^.max_width);
285    gtk_clist_set_column_width(AList, AIndex, ASrc^.width);
286    gtk_clist_set_column_justification(AList, AIndex, ASrc^.justification);
287    gtk_clist_set_column_visibility(AList, AIndex, (ASrc^.flag0 and bm_TGtkCListColumn_visible) <> 0);
288    gtk_clist_set_column_resizeable(AList, AIndex, (ASrc^.flag0 and bm_TGtkCListColumn_resizeable) <> 0);
289    gtk_clist_set_column_auto_resize(AList, AIndex, (ASrc^.flag0 and bm_TGtkCListColumn_auto_resize) <> 0);
290    if (ASrc^.flag0 and bm_TGtkCListColumn_button_passive) <> 0
291    then gtk_clist_column_title_passive(AList, AIndex)
292    else gtk_clist_column_title_active(AList, AIndex);
293  end;
294var
295  WidgetInfo: PWidgetInfo;
296  CListWidget: PGtkCList;
297  CListColumn: PGtkCListColumn;
298  OldCListColumn: TGtkCListColumn;
299  Count: Integer;
300
301begin
302  if not WSCheckHandleAllocated(ALV, 'ColumnMove') then Exit;
303
304  // allow only column modifications when in report mode
305  if TLVHack(ALV).ViewStyle <> vsReport then Exit;
306
307  if AOldIndex = ANewIndex then Exit;
308  if AOldIndex < 0 then Exit;
309  if ANewIndex < 0 then Exit;
310
311  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
312  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
313
314  if AOldIndex >= CListWidget^.columns then Exit;
315  if ANewIndex >= CListWidget^.columns then Exit;
316
317  Count := AOldIndex - ANewIndex;
318
319  // Fetch old column values
320  CListColumn := CListWidget^.Column;
321  Inc(CListColumn, AOldIndex);
322  OldCListColumn := CListColumn^;
323  // Create copy of the title
324  OldCListColumn.title := StrNew(OldCListColumn.title);
325
326  while Count <> 0 do
327  begin
328    // move to next source
329    if Count < 0
330    then Inc(CListColumn)
331    else Dec(CListColumn);
332
333    CopyColumn(CListWidget, ANewIndex + Count, CListColumn);
334
335    if Count < 0
336    then Inc(Count)
337    else Dec(Count);
338  end;
339  // finally copy original data to new column
340  CopyColumn(CListWidget, ANewIndex, @OldCListColumn);
341  // dispose copy of the title
342  StrDispose(OldCListColumn.title);
343end;
344
345class procedure TGtkWSCustomListView.ColumnSetAlignment(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn; const AAlignment: TAlignment);
346const
347  JUSTIFICATION: array[TAlignment] of TGtkJustification = (
348    GTK_JUSTIFY_LEFT,
349    GTK_JUSTIFY_RIGHT,
350    GTK_JUSTIFY_CENTER
351  );
352var
353  WidgetInfo: PWidgetInfo;
354  CListWidget: PGtkCList;
355begin
356  if not WSCheckHandleAllocated(ALV, 'ColumnSetAlignment') then Exit;
357
358  // allow only column modifications when in report mode
359  if TLVHack(ALV).ViewStyle <> vsReport then Exit;
360
361  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
362  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
363
364  gtk_clist_set_column_justification(CListWidget, AIndex, JUSTIFICATION[AAlignment]);
365end;
366
367class procedure TGtkWSCustomListView.ColumnSetAutoSize(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn; const AAutoSize: Boolean);
368var
369  WidgetInfo: PWidgetInfo;
370  CListWidget: PGtkCList;
371begin
372  if not WSCheckHandleAllocated(ALV, 'ColumnSetAutoSize') then Exit;
373
374  // allow only column modifications when in report mode
375  if TLVHack(ALV).ViewStyle <> vsReport then Exit;
376
377  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
378  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
379
380  gtk_clist_set_column_auto_resize(CListWidget, AIndex, AAutoSize);
381end;
382
383class procedure TGtkWSCustomListView.ColumnSetCaption(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn; const ACaption: String);
384var
385  WidgetInfo: PWidgetInfo;
386  CListWidget: PGtkCList;
387begin
388  if not WSCheckHandleAllocated(ALV, 'ColumnSetCaption') then Exit;
389
390  // allow only column modifications when in report mode
391  if TLVHack(ALV).ViewStyle <> vsReport then Exit;
392
393  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
394  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
395
396  gtk_clist_set_column_title(CListWidget, AIndex, PChar(ACaption));
397end;
398
399class procedure TGtkWSCustomListView.ColumnSetImage(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn; const AImageIndex: Integer);
400var
401  WidgetInfo: PWidgetInfo;
402  CListWidget: PGtkCList;
403begin
404  if not WSCheckHandleAllocated(ALV, 'ColumnSetImage') then Exit;
405
406  // allow only column modifications when in report mode
407  if TLVHack(ALV).ViewStyle <> vsReport then Exit;
408
409  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
410  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
411
412  //TODO
413  if CListWidget=nil then exit;
414end;
415
416class procedure TGtkWSCustomListView.ColumnSetMaxWidth(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn; const AMaxWidth: Integer);
417var
418  WidgetInfo: PWidgetInfo;
419  CListWidget: PGtkCList;
420begin
421  if not WSCheckHandleAllocated(ALV, 'ColumnSetMaxWidth') then Exit;
422
423  // allow only column modifications when in report mode
424  if TLVHack(ALV).ViewStyle <> vsReport then Exit;
425
426  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
427  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
428
429
430  // TODO: ? -1 -2
431  //LVSCW_AUTOSIZE              = -1;
432  //LVSCW_AUTOSIZE_USEHEADER    = -2;
433
434  if AMaxWidth <= 0 // unlimited
435  then gtk_clist_set_column_max_width(CListWidget, AIndex, -1)
436  else gtk_clist_set_column_max_width(CListWidget, AIndex, AMaxWidth);
437end;
438
439class procedure TGtkWSCustomListView.ColumnSetMinWidth(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn; const AMinWidth: integer);
440var
441  WidgetInfo: PWidgetInfo;
442  CListWidget: PGtkCList;
443begin
444  if not WSCheckHandleAllocated(ALV, 'ColumnSetMinWidth') then Exit;
445
446  // allow only column modifications when in report mode
447  if TLVHack(ALV).ViewStyle <> vsReport then Exit;
448
449  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
450  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
451
452  gtk_clist_set_column_min_width(CListWidget, AIndex, AMinWidth);
453end;
454
455class procedure TGtkWSCustomListView.ColumnSetWidth(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn; const AWidth: Integer);
456var
457  WidgetInfo: PWidgetInfo;
458  CListWidget: PGtkCList;
459begin
460  if not WSCheckHandleAllocated(ALV, 'ColumnSetWidth') then Exit;
461
462  // allow only column modifications when in report mode
463  if TLVHack(ALV).ViewStyle <> vsReport then Exit;
464
465  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
466  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
467
468  gtk_clist_set_column_width(CListWidget, AIndex, AWidth);
469end;
470
471class procedure TGtkWSCustomListView.ColumnSetVisible(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn; const AVisible: Boolean);
472var
473  WidgetInfo: PWidgetInfo;
474  CListWidget: PGtkCList;
475begin
476  if not WSCheckHandleAllocated(ALV, 'ColumnSetVisible') then Exit;
477
478  // allow only column modifications when in report mode
479  if TLVHack(ALV).ViewStyle <> vsReport then Exit;
480
481  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
482  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
483
484  gtk_clist_set_column_visibility(CListWidget, AIndex, AVisible);
485end;
486
487////////////////////////////////////////////////////////////////////////////////
488// Item code
489////////////////////////////////////////////////////////////////////////////////
490
491class procedure TGtkWSCustomListView.ItemChangeInternal(const ACListWidget: PGtkCList; const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem);
492var
493  ImageBitmap: TBitmap;
494  GDIObject: PGDIObject;
495  Pixmap: PGdkPixmap;
496  Mask: PGdkBitmap;
497  n, Count: integer;
498begin
499  if  (TLVHack(ALV).SmallImages <> nil)
500  and (AItem.ImageIndex >= 0)
501  and (AItem.ImageIndex < TLVHack(ALV).SmallImages.Count)
502  then begin
503    // set image & caption
504    ImageBitmap := TBitmap.Create;
505    Mask := nil;
506    try
507      TLVHack(ALV).SmallImages.GetBitmap(AItem.ImageIndex, ImageBitmap);
508      GDIObject := PGDIObject(ImageBitmap.Handle);
509      case GDIObject^.GDIBitmapType of
510        gbBitmap:
511          begin
512            Pixmap := GDIObject^.GDIBitmapObject;
513            Mask := nil;
514          end;
515        gbPixmap:
516          begin
517            Pixmap := GDIObject^.GDIPixmapObject.Image;
518            Mask := CreateGdkMaskBitmap(ImageBitmap.Handle, ImageBitmap.MaskHandle);
519          end;
520        gbPixbuf:
521          begin
522            Pixmap := nil;
523            Mask := nil;
524            gdk_pixbuf_render_pixmap_and_mask(GDIObject^.GDIPixbufObject, Pixmap, Mask, $80);
525          end;
526      end;
527      gtk_clist_set_pixtext(ACListWidget, AIndex, 0, PChar(AItem.Caption), 3, Pixmap, Mask);
528    finally
529      if Mask <> nil then
530        gdk_bitmap_unref(Mask);
531      if Pixmap <> GDIObject^.GDIPixmapObject.Image then
532        gdk_pixmap_unref(Pixmap);
533      ImageBitmap.Free;
534    end;
535  end
536  else begin
537    // set caption alone
538    gtk_clist_set_text(ACListWidget, AIndex, 0, PChar(AItem.Caption));
539  end;
540
541  // set the other column texts
542  Count := AItem.SubItems.Count + 1;
543  if Count > ACListWidget^.Columns
544  then Count := ACListWidget^.Columns;
545  // set the existing subitems
546  for n := 1 to Count - 1 do
547    gtk_clist_set_text(ACListWidget, AIndex, n, PChar(AItem.SubItems[n - 1]));
548  // fill remaining
549  for n := Count to ACListWidget^.Columns - 1 do
550    gtk_clist_set_text(ACListWidget, AIndex, n, #0);
551end;
552
553class procedure TGtkWSCustomListView.ItemDelete(const ALV: TCustomListView;
554  const AIndex: Integer);
555var
556  WidgetInfo: PWidgetInfo;
557  CListWidget: PGtkCList;
558begin
559  if not WSCheckHandleAllocated(ALV, 'ItemDelete')
560  then Exit;
561
562  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
563  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
564
565  gtk_clist_remove(CListWidget, AIndex);
566end;
567
568class procedure TGtkWSCustomListView.ItemExchange(const ALV: TCustomListView;
569  AItem: TListItem; const AIndex1, AIndex2: Integer);
570begin
571  if not WSCheckHandleAllocated(ALV, 'ItemExchange') then
572    exit;
573  ItemMove(ALV, AItem, AIndex1, AIndex2);
574  if AIndex1 > AIndex2 then
575    ItemMove(ALV, AItem, AIndex2 + 1, AIndex1)
576  else
577    ItemMove(ALV, AItem, AIndex2 - 1, AIndex1);
578end;
579
580class procedure TGtkWSCustomListView.ItemMove(const ALV: TCustomListView;
581  AItem: TListItem; const AFromIndex, AToIndex: Integer);
582var
583  WidgetInfo: PWidgetInfo;
584  CListWidget: PGtkCList;
585begin
586  if not WSCheckHandleAllocated(ALV, 'ItemMove') then
587    exit;
588  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
589  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
590  gtk_clist_row_move(CListWidget, AFromIndex, AToIndex);
591end;
592
593class function TGtkWSCustomListView.ItemGetState(const ALV: TCustomListView;
594  const AIndex: Integer; const AItem: TListItem; const AState: TListItemState;
595  out AIsSet: Boolean): Boolean;
596var
597  WidgetInfo: PWidgetInfo;
598  CListWidget: PGtkCList;
599begin
600  Result := False;
601
602  if not WSCheckHandleAllocated(ALV, 'ItemGetState')
603  then Exit;
604
605  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
606  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
607  if (AIndex < 0) or (AIndex >= CListWidget^.rows)
608  then begin
609    DebugLN('[TGtkWSCustomListView.ItemGetState] Invalid row index: %d', [Aindex]);
610    Exit;
611  end;
612
613  case AState of
614    lisCut,
615    lisDropTarget: begin
616      //TODO: do something with the rowcolor ?
617    end;
618
619    lisFocused: begin
620      AIsSet := CListWidget^.focus_row = AIndex;
621      Result := True;
622    end;
623
624    lisSelected: begin
625      AIsSet := (CListWidget^.selection <> nil)
626            and (g_list_find(CListWidget^.selection, Pointer(PtrInt(Aindex))) <> nil);
627      Result := True;
628    end;
629  end;
630
631end;
632
633class procedure TGtkWSCustomListView.ItemInsert(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem);
634var
635  WidgetInfo: PWidgetInfo;
636  CListWidget: PGtkCList;
637  Titles: PPGChar;
638  idx, Count: Integer;
639begin
640  if not WSCheckHandleAllocated(ALV, 'ItemInsert')
641  then Exit;
642
643  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
644  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
645
646  Count := CListWidget^.columns;
647
648  if Count = 0
649  then begin
650    DebugLn('WARNING: TGtkWSCustomListView.ItemInsert  CListWidget^.columns = 0');
651    Exit;
652  end;
653
654  GetMem(Titles, SizeOf(PGChar) * Count);
655  FillChar(Titles^, SizeOf(PGChar) * Count, 0);
656  Titles[0] := #0;
657
658  idx := AIndex;
659  if idx = -1
660  then idx := gtk_clist_append(CListWidget, Titles)
661  else gtk_clist_insert(CListWidget, idx, Titles);
662  FreeMem(Titles);
663
664  ItemChangeInternal(CListWidget, ALV, idx, AItem);
665end;
666
667class procedure TGtkWSCustomListView.ItemSetImage(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem; const ASubIndex, AImageIndex: Integer);
668var
669  WidgetInfo: PWidgetInfo;
670  CListWidget: PGtkCList;
671  ImageBitmap: TBitmap;
672  Pixmap: PGdkPixmap;
673  Mask: PGdkBitmap;
674  Spacing: guint8;
675  Text: PChar;
676  Dummy1, Dummy2: PGdkBitmap;
677  CellType: TGtkCellType;
678begin
679  if not WSCheckHandleAllocated(ALV, 'ItemSetImage')
680  then Exit;
681
682  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
683  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
684
685  Pixmap := nil;
686  Mask := nil;
687
688  if  (TLVHack(ALV).SmallImages <> nil)
689  and (AImageIndex >= 0)
690  and (AImageIndex < TLVHack(ALV).SmallImages.Count)
691  then begin
692    // set image & caption
693    ImageBitmap := TBitmap.Create;
694    try
695      TLVHack(ALV).SmallImages.GetBitmap(AImageIndex, ImageBitmap);
696      case PGDIObject(ImageBitmap.Handle)^.GDIBitmapType of
697        gbBitmap:
698          begin
699            Pixmap := PGDIObject(ImageBitmap.Handle)^.GDIBitmapObject;
700            gdk_pixmap_ref(Pixmap);
701            Mask := nil;
702          end;
703        gbPixmap:
704          begin
705            Pixmap := PGDIObject(ImageBitmap.Handle)^.GDIPixmapObject.Image;
706            Mask := CreateGdkMaskBitmap(ImageBitmap.Handle, ImageBitmap.MaskHandle);
707            gdk_pixmap_ref(Pixmap);
708          end;
709        gbPixbuf:
710          begin
711            Pixmap := nil;
712            Mask := nil;
713            gdk_pixbuf_render_pixmap_and_mask(PGDIObject(ImageBitmap.Handle)^.GDIPixbufObject, pixmap, mask, $80);
714          end;
715      end;
716    finally
717      ImageBitmap.Free;
718    end;
719  end;
720
721  CellType := gtk_clist_get_cell_type(CListWidget, AIndex, ASubIndex);
722  // Sigh.
723  // gtk returns -1 for an invalid cell (which is not part of the enum)
724  // so to handle it, we need a case based on integer
725  case Ord(CellType) of
726    Ord(GTK_CELL_TEXT),
727    Ord(GTK_CELL_PIXTEXT),
728    Ord(GTK_CELL_EMPTY),
729    Ord(GTK_CELL_PIXMAP): begin
730      if pixmap <> nil
731      then begin
732        case CellType of
733          GTK_CELL_TEXT: begin
734            // convert the cell
735            Text := nil;
736            gtk_clist_get_text(CListWidget, AIndex, ASubIndex, @Text);
737            gtk_clist_set_pixtext(CListWidget, AIndex, ASubIndex, Text, DEFAULT_IMAGE_SPACING, Pixmap, Mask);
738          end;
739          GTK_CELL_PIXTEXT: begin
740            if gtk_clist_get_pixtext(CListWidget, AIndex, ASubIndex, @Text, @Spacing, @Dummy2, @Dummy1) <> 0
741            then gtk_clist_set_pixtext(CListWidget, AIndex, ASubIndex, Text, Spacing, Pixmap, Mask)
742            else gtk_clist_set_pixmap(CListWidget, AIndex, ASubIndex, Pixmap, Mask);
743          end;
744          GTK_CELL_EMPTY,
745          GTK_CELL_PIXMAP: begin
746            gtk_clist_set_pixtext(CListWidget, AIndex, ASubIndex, '', DEFAULT_IMAGE_SPACING, Pixmap, Mask);
747          end;
748        end;
749      end
750      else begin
751        case CellType of
752          GTK_CELL_EMPTY,
753          GTK_CELL_TEXT:; // nothing to do
754          GTK_CELL_PIXTEXT: begin
755            Text := nil;
756            if gtk_clist_get_pixtext(CListWidget, AIndex, ASubIndex, @Text, @Spacing, @Dummy2, @Dummy1) <> 0
757            then gtk_clist_set_text(CListWidget, AIndex, ASubIndex, Text)
758            else gtk_clist_set_text(CListWidget, AIndex, ASubIndex, '');
759          end;
760          GTK_CELL_PIXMAP: begin
761            gtk_clist_set_text(CListWidget, AIndex, ASubIndex, '');
762          end;
763        end;
764      end;
765    end;
766    Ord(GTK_CELL_WIDGET): DebugLN('[TGtkWSCustomListView.ItemSetImage] Setting text of widget cell');
767    -1: DebugLN('[TGtkWSCustomListView.ItemSetText] Cell (%d,%d) not created', [AIndex, ASubIndex]);
768  else
769    DebugLN('[TGtkWSCustomListView.ItemSetImage] Unknown celltype %d', [Ord(CellType)]);
770  end;
771  if Pixmap <> nil then
772    gdk_pixmap_unref(Pixmap);
773  if Mask <> nil then
774    gdk_bitmap_unref(Mask);
775end;
776
777class procedure TGtkWSCustomListView.ItemSetState(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem; const AState: TListItemState; const AIsSet: Boolean);
778var
779  WidgetInfo: PWidgetInfo;
780  CListWidget: PGtkCList;
781begin
782  if not WSCheckHandleAllocated(ALV, 'ItemSetState')
783  then Exit;
784
785  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
786  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
787  if (AIndex < 0) or (AIndex >= CListWidget^.rows)
788  then begin
789    DebugLN('[TGtkWSCustomListView.ItemSetState] Invalid row index: %d', [Aindex]);
790    Exit;
791  end;
792
793  case AState of
794    lisCut,
795    lisDropTarget: begin
796      //TODO: do something with the rowcolor ?
797    end;
798
799    lisFocused: begin
800      if AIsSet = (CListWidget^.focus_row = AIndex) then Exit;
801      // reset old focus
802      if (CListWidget^.focus_row <> -1)
803      and (gtk_widget_has_focus(PGtkWidget(CListWidget)))
804      then gtk_widget_draw_focus(PGtkWidget(CListWidget));
805
806      if AIsSet
807      then begin
808        CListWidget^.focus_row := AIndex;
809        if gtk_widget_has_focus(PGtkWidget(CListWidget))
810        then gtk_widget_draw_focus(PGtkWidget(CListWidget));
811      end
812      else CListWidget^.focus_row := -1;
813    end;
814
815    lisSelected: begin
816      if AIsSet
817      then begin
818        if (CListWidget^.selection_mode = GTK_SELECTION_SINGLE)
819        or (CListWidget^.selection_mode = GTK_SELECTION_BROWSE)
820        then begin
821          // check if the row is are already selected
822          // since we are in singleselect, the first item is checked
823          if (CListWidget^.selection <> nil)
824          and (PtrInt(PtrUInt(CListWidget^.selection^.Data)) = AIndex)
825          then Exit;
826          gtk_clist_unselect_all(CListWidget);
827        end;
828        gtk_clist_select_row(CListWidget, AIndex, 0);
829      end
830      else gtk_clist_unselect_row(CListWidget, AIndex, 0);
831    end;
832  end;
833end;
834
835class procedure TGtkWSCustomListView.ItemSetText(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem; const ASubIndex: Integer; const AText: String);
836var
837  WidgetInfo: PWidgetInfo;
838  CListWidget: PGtkCList;
839  Pixmap: PGdkPixmap;
840  Mask: PGdkBitmap;
841  Spacing: guint8;
842  Dummy: pgchar;
843  CellType: TGtkCellType;
844begin
845  if not WSCheckHandleAllocated(ALV, 'ItemSetText')
846  then Exit;
847
848  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
849  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
850
851  CellType := gtk_clist_get_cell_type(CListWidget, AIndex, ASubIndex);
852  // Sigh.
853  // gtk returns -1 for an invalid cell (which is not part of the enum)
854  // so to handle it, we need a case based on integer
855  case Ord(CellType) of
856    Ord(GTK_CELL_EMPTY),
857    Ord(GTK_CELL_TEXT): begin
858      // simply set the text
859      gtk_clist_set_text(CListWidget, AIndex, ASubIndex, PChar(AText));
860    end;
861    Ord(GTK_CELL_PIXTEXT): begin
862      if gtk_clist_get_pixtext(CListWidget, AIndex, ASubIndex, @Dummy, @Spacing, @Pixmap, @Mask) <> 0
863      then gtk_clist_set_pixtext(CListWidget, AIndex, ASubIndex, PChar(AText), Spacing, Pixmap, Mask)
864      else gtk_clist_set_text(CListWidget, AIndex, ASubIndex, PChar(AText));
865    end;
866    Ord(GTK_CELL_PIXMAP): begin
867      if gtk_clist_get_pixmap(CListWidget, AIndex, ASubIndex, @Pixmap, @Mask) <> 0
868      then gtk_clist_set_pixtext(CListWidget, AIndex, ASubIndex, PChar(AText), DEFAULT_IMAGE_SPACING, Pixmap, Mask)
869      else gtk_clist_set_text(CListWidget, AIndex, ASubIndex, PChar(AText));
870    end;
871    Ord(GTK_CELL_WIDGET): DebugLN('[TGtkWSCustomListView.ItemSetText] Setting text of widget cell');
872    -1: DebugLN('[TGtkWSCustomListView.ItemSetText] Cell (%d,%d) not created', [AIndex, ASubIndex]);
873  else
874    DebugLN('[TGtkWSCustomListView.ItemSetText] Unknown celltype %d', [Ord(CellType)]);
875  end;
876end;
877
878class procedure TGtkWSCustomListView.ItemShow(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem; const PartialOK: Boolean);
879var
880  WidgetInfo: PWidgetInfo;
881  CListWidget: PGtkCList;
882  RowTopY: Integer;
883begin
884  if not WSCheckHandleAllocated(ALV, 'ItemShow')
885  then Exit;
886
887  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
888  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
889
890  RowTopY := (CListWidget^.row_height * AIndex) + ((AIndex +1) *
891                                     {CELL} 1 {SPACING} + CListWidget^.voffset);
892
893                                                 // 0=NotVisible
894                                                 // 1=PartiallyVisible
895                                                 // 2=Fully Visible
896                                                 //   |
897  if gtk_clist_row_is_visible(CListWidget, AIndex) < (2 - Ord(PartialOK)) then begin
898    if (RowTopY + CListWidget^.row_height > CListWidget^.clist_window_height) then begin
899      gtk_clist_moveto (CListWidget, AIndex, -1, 1, 0);
900        //                              |     |  |  |
901        //                        The Row     |  |  |
902        //                           The Column  |  |
903        //                               Row Align  |
904    end //                               Column Align
905    else if (RowTopY < 0) then begin
906      gtk_clist_moveto (CListWidget, AIndex, -1, 0, 0);
907    end;//                                       |
908  end;  //                                       |
909        //                                       |
910        //                      0 = your row will be at the top.
911        //                      1 = it will be at the bottom.
912end;
913
914////////////////////////////////////////////////////////////////////////////////
915// LV code
916////////////////////////////////////////////////////////////////////////////////
917
918class function TGtkWSCustomListView.CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): HWND;
919var
920  ListView: TLVHack; //TCustomListView
921  WidgetInfo: PWidgetInfo;
922  ScrollingData: PBaseScrollingWinControlData;
923  ListViewData: PCustomListViewData;
924
925  ScrollWidget: PGtkScrolledWindow;
926  CListWidget: PGtkCList;
927begin
928  ListView := TLVHack(AWinControl as TCustomListView);
929
930  Result := TGtkWSBaseScrollingWinControl.CreateHandle(AWinControl, AParams);
931  if Result = 0 then Exit;
932  ScrollWidget := PGtkScrolledWindow(Result);
933
934  if ListView.ViewStyle = vsReport
935  then begin
936    // precreate colums since they cannot be added or removed
937    CListWidget := PGtkCList(gtk_clist_new(Max(1, ListView.Columns.Count)));
938    gtk_clist_column_titles_passive(CListWidget);
939  end
940  else begin
941    CListWidget := PGtkCList(gtk_clist_new(1));
942    gtk_clist_column_titles_hide(CListWidget);
943    gtk_clist_set_column_auto_resize(CListWidget, 0, True);
944  end;
945  gtk_clist_set_shadow_type(CListWidget, GTK_SHADOW_IN);
946
947  gtk_container_add(PGtkContainer(ScrollWidget), PGtkWidget(CListWidget));
948
949  gtk_widget_unset_flags(ScrollWidget^.hscrollbar, GTK_CAN_FOCUS);
950  gtk_widget_unset_flags(ScrollWidget^.vscrollbar, GTK_CAN_FOCUS);
951  gtk_scrolled_window_set_policy(ScrollWidget, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
952  // the next is not really needed since the container has only one child
953  gtk_container_set_focus_vadjustment(PGtkContainer(CListWidget), gtk_scrolled_window_get_vadjustment(ScrollWidget));
954  gtk_container_set_focus_hadjustment(PGtkContainer(CListWidget), gtk_scrolled_window_get_hadjustment(ScrollWidget));
955  gtk_widget_show_all(PGtkWidget(CListWidget));
956
957  // create widget info
958  // already created in TGtkWSBaseScrollingWinControl
959  // Replace the ScrollingInfo with our info
960  WidgetInfo := GetWidgetInfo(ScrollWidget);
961  WidgetInfo^.CoreWidget := PGtkWidget(CListWidget);
962  ScrollingData := WidgetInfo^.UserData;
963  New(ListViewData);
964  ListViewData^.ScrollingData := ScrollingData^;
965  ListViewData^.ViewStyle := ListView.ViewStyle;
966  Dispose(ScrollingData);
967  WidgetInfo^.UserData := ListViewData;
968  //todo: obsolete
969  // SetMainWidget(ScrollWidget, CListWidget);
970
971  // set allocation
972  // already created in TGtkWSBaseScrollingWinControl
973
974  Set_RC_Name(AWinControl, PGtkWidget(ScrollWidget));
975  SetListCallbacks(PGtkWidget(ScrollWidget), PGtkWidget(CListWidget), WidgetInfo);
976end;
977
978class procedure TGtkWSCustomListView.SetListCallbacks(const AScrollWidget, AListWidget: PGtkWidget; const AWidgetInfo: PWidgetInfo);
979begin
980  TGtkWSBaseScrollingWinControl.SetCallbacks(AScrollWidget, AWidgetInfo);
981
982  SignalConnect(AListWidget, 'click-column',        @GtkWSCustomListView_ClickColumn,       AWidgetInfo);
983  SignalConnect(AListWidget, 'resize-column',       @GtkWSCustomListView_ResizeColumn,      AWidgetInfo);
984  SignalConnect(AListWidget, 'abort-column-resize', @GtkWSCustomListView_AbortColumnResize, AWidgetInfo);
985  // SignalConnect(AListWidget, 'row-move',            @GtkWSCustomListView_RowMove,           AWidgetInfo);
986  SignalConnect(AListWidget, 'select-row',          @GtkWSCustomListView_SelectRow,         AWidgetInfo);
987  SignalConnect(AListWidget, 'unselect-row',        @GtkWSCustomListView_UnSelectRow,       AWidgetInfo);
988  SignalConnect(AListWidget, 'toggle-focus-row',    @GtkWSCustomListView_ToggleFocusRow,    AWidgetInfo);
989  SignalConnect(AListWidget, 'select-all',          @GtkWSCustomListView_SelectAll,         AWidgetInfo);
990  SignalConnect(AListWidget, 'unselect-all',        @GtkWSCustomListView_UnSelectAll,       AWidgetInfo);
991  SignalConnect(AListWidget, 'end-selection',       @GtkWSCustomListView_EndSelection,      AWidgetInfo);
992end;
993
994class procedure TGtkWSCustomListView.BeginUpdate(const ALV: TCustomListView);
995var
996  WidgetInfo: PWidgetInfo;
997  CListWidget: PGtkCList;
998begin
999  if not WSCheckHandleAllocated(ALV, 'BeginUpdate')
1000  then Exit;
1001
1002  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
1003  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
1004
1005  gtk_clist_freeze(CListWidget);
1006end;
1007
1008class procedure TGtkWSCustomListView.EndUpdate(const ALV: TCustomListView);
1009var
1010  WidgetInfo: PWidgetInfo;
1011  CListWidget: PGtkCList;
1012begin
1013  if not WSCheckHandleAllocated(ALV, 'EndUpdate')
1014  then Exit;
1015
1016  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
1017  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
1018
1019  gtk_clist_thaw(CListWidget);
1020end;
1021
1022class function TGtkWSCustomListView.GetBoundingRect(const ALV: TCustomListView): TRect;
1023var
1024  WidgetInfo: PWidgetInfo;
1025  CListWidget: PGtkCList;
1026begin
1027  Result:=Rect(0,0,0,0);
1028  if not WSCheckHandleAllocated(ALV, 'GetBoundingRect')
1029  then Exit;
1030
1031  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
1032  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
1033  if CListWidget=nil then exit;
1034
1035
1036  if TLVHack(ALV).ViewStyle in [vsIcon, vsSmallIcon]
1037  then begin
1038    // TODO: implement
1039  end
1040  else begin
1041    Result := Rect(0,0,0,0);
1042  end;
1043end;
1044
1045class function TGtkWSCustomListView.GetDropTarget(const ALV: TCustomListView): Integer;
1046var
1047  WidgetInfo: PWidgetInfo;
1048  CListWidget: PGtkCList;
1049begin
1050  Result:=0;
1051  if not WSCheckHandleAllocated(ALV, 'GetDropTarget')
1052  then Exit;
1053
1054  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
1055  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
1056  if CListWidget=nil then exit;
1057
1058  // TODO: implement
1059  Result := -1;
1060end;
1061
1062class function TGtkWSCustomListView.GetFocused(const ALV: TCustomListView): Integer;
1063var
1064  WidgetInfo: PWidgetInfo;
1065  CListWidget: PGtkCList;
1066begin
1067  if not WSCheckHandleAllocated(ALV, 'GetFocused')
1068  then Exit;
1069
1070  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
1071  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
1072
1073  Result := CListWidget^.focus_row;
1074end;
1075
1076class function TGtkWSCustomListView.GetHoverTime(const ALV: TCustomListView): Integer;
1077var
1078  WidgetInfo: PWidgetInfo;
1079  CListWidget: PGtkCList;
1080begin
1081  Result := -1; // = default
1082  if not WSCheckHandleAllocated(ALV, 'GetHoverTime')
1083  then Exit;
1084
1085  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
1086  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
1087  if CListWidget=nil then exit;
1088
1089  // TODO: implement
1090  Result := -1; // = default
1091end;
1092
1093class function TGtkWSCustomListView.GetItemAt(const ALV: TCustomListView; x,
1094  y: integer): Integer;
1095var
1096  WidgetInfo: PWidgetInfo;
1097  CListWidget: PGtkCList;
1098  I, FirstRowY, LastRowY: Integer;
1099  ScrolledTop: Integer;
1100  RowHeight: Integer;
1101
1102begin
1103  if not WSCheckHandleAllocated(ALV, 'GetItemAt')
1104  then Exit(-1);
1105
1106  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
1107  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
1108
1109  ScrolledTop := Trunc(CListWidget^.vadjustment^.value);
1110
1111  FirstRowY := ScrolledTop;
1112
1113  // For some reason the actual row height is one pixel more than the size it says
1114  RowHeight := CListWidget^.row_height+1;
1115
1116  Dec(y, CListWidget^.column_title_area.height);
1117
1118  LastRowY := FirstRowY + CListWidget^.clist_window_height;
1119
1120  Inc(y, ScrolledTop);
1121
1122  for I := FirstRowY div RowHeight to LastRowY div RowHeight do
1123  begin
1124    if I > CListWidget^.rows-1 then
1125      Exit;
1126    if (I * RowHeight < y) and ((I+1) * RowHeight >= y-1) then
1127      Exit(I);
1128  end;
1129end;
1130
1131class function TGtkWSCustomListView.GetSelCount(const ALV: TCustomListView): Integer;
1132var
1133  WidgetInfo: PWidgetInfo;
1134  CListWidget: PGtkCList;
1135begin
1136  if not WSCheckHandleAllocated(ALV, 'GetSelCount')
1137  then Exit;
1138
1139  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
1140  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
1141
1142  if CListWidget^.selection = nil
1143  then Result := 0
1144  else Result := g_list_length(CListWidget^.selection);
1145end;
1146
1147class function TGtkWSCustomListView.GetSelection(const ALV: TCustomListView): Integer;
1148var
1149  WidgetInfo: PWidgetInfo;
1150  CListWidget: PGtkCList;
1151begin
1152  if not WSCheckHandleAllocated(ALV, 'GetSelection')
1153  then Exit;
1154
1155  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
1156  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
1157
1158  if CListWidget^.selection = nil
1159  then Result := -1
1160  else Result := PtrUInt(CListWidget^.selection^.data)
1161end;
1162
1163class function TGtkWSCustomListView.GetTopItem(const ALV: TCustomListView): Integer;
1164var
1165  WidgetInfo: PWidgetInfo;
1166  CListWidget: PGtkCList;
1167begin
1168  if not WSCheckHandleAllocated(ALV, 'GetTopItem')
1169  then Exit;
1170
1171  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
1172  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
1173
1174  // Result := ROW_FROM_YPIXEL(0)
1175  // #define ROW_FROM_YPIXEL(clist, y)  (((y) - (clist)->voffset) / \
1176  //                                    ((clist)->row_height + CELL_SPACING))
1177
1178  Result := -CListWidget^.voffset div (CListWidget^.row_height + 1);
1179end;
1180
1181class function TGtkWSCustomListView.GetViewOrigin(const ALV: TCustomListView): TPoint;
1182var
1183  WidgetInfo: PWidgetInfo;
1184  CListWidget: PGtkCList;
1185begin
1186  if not WSCheckHandleAllocated(ALV, 'GetViewOrigin')
1187  then Exit;
1188
1189  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
1190  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
1191  if CListWidget=nil then exit;
1192
1193  Result.X := Round(CListWidget^.hAdjustment^.value);
1194  Result.Y := Round(CListWidget^.vAdjustment^.value);
1195end;
1196
1197class function TGtkWSCustomListView.GetVisibleRowCount(const ALV: TCustomListView): Integer;
1198var
1199  WidgetInfo: PWidgetInfo;
1200  CListWidget: PGtkCList;
1201begin
1202  if not WSCheckHandleAllocated(ALV, 'GetVisibleRowCount')
1203  then Exit;
1204
1205  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
1206  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
1207
1208  Result := CListWidget^.clist_window_height div (CListWidget^.row_height + 1);
1209end;
1210
1211class procedure TGtkWSCustomListView.SetAllocBy(const ALV: TCustomListView;
1212  const AValue: Integer);
1213var
1214  WidgetInfo: PWidgetInfo;
1215  CListWidget: PGtkCList;
1216begin
1217  if not WSCheckHandleAllocated(ALV, 'SetAllocBy')
1218  then Exit;
1219
1220  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
1221  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
1222  if CListWidget=nil then exit;
1223  // TODO: implement ?
1224end;
1225
1226class procedure TGtkWSCustomListView.SetDefaultItemHeight(const ALV: TCustomListView;
1227  const AValue: Integer);
1228var
1229  WidgetInfo: PWidgetInfo;
1230  CListWidget: PGtkCList;
1231const
1232  GTK_CLIST_ROW_HEIGHT_SET = 1 shl 1;
1233begin
1234  if not WSCheckHandleAllocated(ALV, 'SetDefaultItemHeight')
1235  then Exit;
1236
1237  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
1238  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
1239
1240  if (AValue = 0) and (CListWidget^.flags and GTK_CLIST_ROW_HEIGHT_SET = 0) then
1241    exit;
1242  gtk_clist_set_row_height(CListWidget, AValue);
1243end;
1244
1245class procedure TGtkWSCustomListView.SetHotTrackStyles(const ALV: TCustomListView;
1246  const AValue: TListHotTrackStyles);
1247var
1248  WidgetInfo: PWidgetInfo;
1249  CListWidget: PGtkCList;
1250begin
1251  if not WSCheckHandleAllocated(ALV, 'SetHotTrackStyles')
1252  then Exit;
1253
1254  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
1255  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
1256
1257  if CListWidget=nil then exit;
1258  // TODO: implement ?
1259end;
1260
1261class procedure TGtkWSCustomListView.SetHoverTime(const ALV: TCustomListView;
1262  const AValue: Integer);
1263var
1264  WidgetInfo: PWidgetInfo;
1265  CListWidget: PGtkCList;
1266begin
1267  if not WSCheckHandleAllocated(ALV, 'SetHoverTime')
1268  then Exit;
1269
1270  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
1271  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
1272
1273  if CListWidget=nil then exit;
1274  // TODO: implement ?
1275end;
1276
1277class procedure TGtkWSCustomListView.SetImageList(const ALV: TCustomListView;
1278  const AList: TListViewImageList; const AValue: TCustomImageListResolution);
1279var
1280  WidgetInfo: PWidgetInfo;
1281  CListWidget: PGtkCList;
1282begin
1283  if not WSCheckHandleAllocated(ALV, 'SetImageList')
1284  then Exit;
1285
1286  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
1287  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
1288
1289  if CListWidget=nil then exit;
1290  // TODO: implement
1291end;
1292
1293class procedure TGtkWSCustomListView.SetPropertyInternal(
1294  const ACListWidget: PGtkCList; const AInfo: PWidgetInfo;
1295  const AProp: TListViewProperty; const AIsSet: Boolean);
1296var
1297  ListViewData: PCustomListViewData;
1298begin
1299  case AProp of
1300    lvpAutoArrange: begin
1301      // TODO: implement ??
1302    end;
1303    lvpCheckboxes: begin
1304      // TODO: implement
1305    end;
1306    lvpColumnClick: begin
1307      // allow only column modifications when in report mode
1308      ListViewData := AInfo^.UserData;
1309      if ListViewData^.ViewStyle <> vsReport then Exit;
1310
1311      if AIsSet
1312      then gtk_clist_column_titles_active(ACListWidget)
1313      else gtk_clist_column_titles_passive(ACListWidget);
1314    end;
1315    lvpFlatScrollBars: begin
1316      // TODO: implement ??
1317    end;
1318    lvpFullDrag: begin
1319      // TODO: implement ??
1320    end;
1321    lvpGridLines: begin
1322      // TODO: implement
1323      // maybe possible with some cellwidget hacking
1324    end;
1325    lvpHideSelection: begin
1326      // TODO: implement
1327      // should be possible with some focus in/out events
1328    end;
1329    lvpHotTrack: begin
1330      // TODO: implement
1331      // should be possible with some mouse tracking
1332    end;
1333    lvpMultiSelect: begin
1334      if AIsSet
1335      then gtk_clist_set_selection_mode(ACListWidget, GTK_SELECTION_EXTENDED)
1336      else gtk_clist_set_selection_mode(ACListWidget, GTK_SELECTION_BROWSE);
1337    end;
1338    lvpOwnerDraw: begin
1339      // TODO: implement
1340      // use custom images/widgets ?
1341    end;
1342    lvpReadOnly: begin
1343      // TODO: implement inline editor ?
1344    end;
1345    lvpRowSelect: begin
1346      // TODO: implement ???
1347      // how to do cell select
1348    end;
1349    lvpShowColumnHeaders: begin
1350      // allow only column modifications when in report mode
1351      ListViewData := AInfo^.UserData;
1352      if ListViewData^.ViewStyle <> vsReport then Exit;
1353
1354      if AIsSet
1355      then gtk_clist_column_titles_show(ACListWidget)
1356      else gtk_clist_column_titles_hide(ACListWidget);
1357    end;
1358    lvpShowWorkAreas: begin
1359      // TODO: implement ???
1360    end;
1361    lvpWrapText: begin
1362      // TODO: implement ???
1363    end;
1364  end;
1365end;
1366
1367class procedure TGtkWSCustomListView.SetProperty(const ALV: TCustomListView;
1368  const AProp: TListViewProperty; const AIsSet: Boolean);
1369var
1370  WidgetInfo: PWidgetInfo;
1371  CListWidget: PGtkCList;
1372begin
1373  if not WSCheckHandleAllocated(ALV, 'SetProperty')
1374  then Exit;
1375
1376  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
1377  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
1378
1379  SetPropertyInternal(CListWidget, WidgetInfo, AProp, AIsSet);
1380end;
1381
1382class procedure TGtkWSCustomListView.SetProperties(const ALV: TCustomListView; const AProps: TListViewProperties);
1383var
1384  WidgetInfo: PWidgetInfo;
1385  CListWidget: PGtkCList;
1386  Prop: TListViewProperty;
1387begin
1388  if not WSCheckHandleAllocated(ALV, 'SetProperties')
1389  then Exit;
1390
1391  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
1392  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
1393
1394  for Prop := Low(TListViewProperty) to High(TListViewProperty) do
1395    SetPropertyInternal(CListWidget, WidgetInfo, Prop, Prop in AProps);
1396end;
1397
1398class procedure TGtkWSCustomListView.SetScrollBars(const ALV: TCustomListView; const AValue: TScrollStyle);
1399var
1400  ScrollWidget: PGtkScrolledWindow;
1401  hPolicy, vPolicy: TGtkPolicyType;
1402begin
1403  if not WSCheckHandleAllocated(ALV, 'SetScrollBars')
1404  then Exit;
1405
1406  ScrollWidget := PGtkScrolledWindow(ALV.Handle);
1407
1408  case AValue of
1409    ssHorizontal, ssBoth: hPolicy := GTK_POLICY_ALWAYS;
1410    ssAutoHorizontal, ssAutoBoth: hPolicy := GTK_POLICY_AUTOMATIC;
1411  else
1412    hPolicy := GTK_POLICY_NEVER;
1413  end;
1414
1415  case AValue of
1416    ssVertical, ssBoth: vPolicy := GTK_POLICY_ALWAYS;
1417    ssAutoVertical, ssAutoBoth: vPolicy := GTK_POLICY_AUTOMATIC;
1418  else
1419    vPolicy := GTK_POLICY_NEVER;
1420  end;
1421
1422  gtk_scrolled_window_set_policy(ScrollWidget, hPolicy, vPolicy);
1423end;
1424
1425class procedure TGtkWSCustomListView.SetSort(const ALV: TCustomListView;
1426  const AType: TSortType; const AColumn: Integer;
1427  const ASortDirection: TSortDirection);
1428var
1429  WidgetInfo: PWidgetInfo;
1430  CListWidget: PGtkCList;
1431begin
1432  if not WSCheckHandleAllocated(ALV, 'SetSort')
1433  then Exit;
1434
1435  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
1436  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
1437
1438  if CListWidget=nil then exit;
1439  // TODO implement
1440end;
1441
1442class procedure TGtkWSCustomListView.SetViewOrigin(const ALV: TCustomListView; const AValue: TPoint);
1443var
1444  WidgetInfo: PWidgetInfo;
1445  CListWidget: PGtkCList;
1446begin
1447  if not WSCheckHandleAllocated(ALV, 'SetViewOrigin')
1448  then Exit;
1449
1450  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
1451  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
1452  if CListWidget=nil then exit;
1453
1454  gtk_adjustment_set_value(CListWidget^.hAdjustment, AValue.X);
1455  gtk_adjustment_set_value(CListWidget^.vAdjustment, AValue.Y);
1456end;
1457
1458class procedure TGtkWSCustomListView.SetViewStyle(const ALV: TCustomListView; const Avalue: TViewStyle);
1459var
1460  WidgetInfo: PWidgetInfo;
1461  ListViewData: PCustomListViewData;
1462begin
1463  if not WSCheckHandleAllocated(ALV, 'SetViewStyle')
1464  then Exit;
1465
1466
1467  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
1468  ListViewData := WidgetInfo^.UserData;
1469  if ListViewData^.ViewStyle = AValue then Exit; // nothing to do
1470
1471  // We cannot change columns or viewstyle so we need to recreate
1472  RecreateWnd(ALV);
1473end;
1474
1475