2 Copyright © 1995-2006, The AROS Development Team. All rights reserved.
5 Desc: Internal GadTools listview class.
10 #include <proto/exec.h>
11 #include <exec/libraries.h>
12 #include <exec/memory.h>
13 #include <proto/dos.h>
14 #include <intuition/classes.h>
15 #include <intuition/classusr.h>
16 #include <intuition/gadgetclass.h>
17 #include <intuition/imageclass.h>
18 #include <intuition/intuition.h>
19 #include <intuition/cghooks.h>
20 #include <graphics/rastport.h>
21 #include <graphics/text.h>
22 #include <utility/tagitem.h>
23 #include <devices/inputevent.h>
24 #include <proto/alib.h>
25 #include <proto/utility.h>
26 #include <proto/gadtools.h>
27 #include <proto/layers.h>
29 #include <string.h> /* memset() */
32 #include <aros/symbolsets.h>
36 #include <aros/debug.h>
38 #include "gadtools_intern.h"
41 #include LC_LIBDEFS_FILE
43 /**********************************************************************************************/
45 #define GadToolsBase ((struct GadToolsBase_intern *)cl->cl_UserData)
47 /**********************************************************************************************/
50 #define LVFLG_READONLY (1 << 0)
51 #define LVFLG_FONT_OPENED (1 << 1)
52 #define LVFLG_FORCE_SELECT_STATE (1 << 2)
54 /* The flags below are used for as a trick to easily select the right pens,
55 ** or the right hook draw states. The combinations of the flags can be used
56 ** as an index into the table. This saves me from a LOT of if-else statements.
57 ** As one can se, the 4 last ones is all LVR_NORMAL, cause it makes no sense
58 ** to mark entries selected or disabled in a readonly listview.
63 #define SELECTED (1 << 0)
64 #define DISABLED (1 << 1)
65 #define READONLY (1 << 2)
67 #define TotalItemHeight(data) (data->ld_Spacing + data->ld_ItemHeight)
69 const ULONG statetab
[] =
71 LVR_NORMAL
, /* 0 NORMAL */
72 LVR_SELECTED
, /* 1 SELECTED */
73 LVR_NORMALDISABLED
, /* 2 NORMAL|DISABLED */
74 LVR_SELECTED
, /* 3 SELECTED|DISABLED */
75 LVR_NORMAL
, /* 4 NORMAL|READONLY */
76 LVR_NORMAL
, /* 5 SELECTED|READONLY */
77 LVR_NORMAL
, /* 6 NORMAL|DISABLED|READONLY */
78 LVR_NORMAL
/* 7 SELECTED|DISABLED|READONLY */
82 #define GadToolsBase ((struct GadToolsBase_intern *)hook->h_Data)
84 /**********************************************************************************************/
86 AROS_UFH3(IPTR
, RenderHook
,
87 AROS_UFHA(struct Hook
*, hook
, A0
),
88 AROS_UFHA(struct Node
*, node
, A2
),
89 AROS_UFHA(struct LVDrawMsg
*, msg
, A1
)
96 EnterFunc(bug("RenderHook: hook=%p, node=%sm msg=%p)\n",
97 hook
, node
->ln_Name
, msg
));
99 D(bug("RenderHook: State %ld\n",msg
->lvdm_State
));
101 if (msg
->lvdm_MethodID
== LV_DRAW
)
103 struct DrawInfo
*dri
= msg
->lvdm_DrawInfo
;
104 struct RastPort
*rp
= msg
->lvdm_RastPort
;
106 WORD min_x
= msg
->lvdm_Bounds
.MinX
;
107 WORD min_y
= msg
->lvdm_Bounds
.MinY
;
108 WORD max_x
= msg
->lvdm_Bounds
.MaxX
;
109 WORD max_y
= msg
->lvdm_Bounds
.MaxY
;
111 UWORD erasepen
= BACKGROUNDPEN
;
117 switch (msg
->lvdm_State
)
120 case LVR_SELECTEDDISABLED
:
121 /* We must fill the backgound with FILLPEN */
122 D(bug("RenderHook: LVR_SELECTED(DISABLED)\n"));
128 case LVR_NORMALDISABLED
: {
131 struct TextExtent te
;
133 SetAPen(rp
, dri
->dri_Pens
[erasepen
]);
134 RectFill(rp
, min_x
, min_y
, max_x
, max_y
);
136 numfit
= TextFit(rp
, node
->ln_Name
, strlen(node
->ln_Name
),
137 &te
, NULL
, 1, max_x
- min_x
+ 1, max_y
- min_y
+ 1);
139 SetAPen(rp
, dri
->dri_Pens
[TEXTPEN
]);
142 Move(rp
, min_x
, min_y
+ rp
->Font
->tf_Baseline
);
143 Text(rp
, node
->ln_Name
, numfit
);
149 kprintf("!! LISTVIEW DRAWING STATE NOT SUPPORTED !!\n");
158 retval
= LVCB_UNKNOWN
;
161 ReturnInt ("RenderHook", IPTR
, retval
);
166 /**********************************************************************************************/
170 STATIC VOID
RenderEntries(Class
*cl
, struct Gadget
*g
, struct gpRender
*msg
,
171 WORD entryoffset
, UWORD numentries
, struct GadToolsBase_intern
*GadToolsBase
)
174 /* NOTE: entry is the the number ot the item to draw
175 ** counted from first visible
178 struct LVData
*data
= INST_DATA(cl
, g
);
180 UWORD entry_count
, totalitemheight
;
181 WORD left
, top
, width
;
182 struct LVDrawMsg drawmsg
;
183 struct TextFont
*oldfont
;
187 UWORD current_entry
= 0;
189 EnterFunc(bug("RenderEntries(msg=%p, entryoffset=%d, numentries=%d)\n",
190 msg
, entryoffset
, numentries
));
192 if (!data
->ld_Labels
|| (data
->ld_Labels
== (struct List
*)~0)) return;
194 oldfont
= msg
->gpr_RPort
->Font
;
195 SetFont(msg
->gpr_RPort
, data
->ld_Font
);
197 totalitemheight
= TotalItemHeight(data
);
199 left
= g
->LeftEdge
+ LV_BORDER_X
;
200 top
= g
->TopEdge
+ LV_BORDER_Y
;
201 top
+= totalitemheight
* entryoffset
;
203 width
= g
->Width
- LV_BORDER_X
* 2;
205 if (data
->ld_CallBack
)
207 drawmsg
.lvdm_MethodID
= LV_DRAW
;
208 drawmsg
.lvdm_RastPort
= msg
->gpr_RPort
;
209 drawmsg
.lvdm_DrawInfo
= data
->ld_Dri
;
210 drawmsg
.lvdm_Bounds
.MinX
= left
;
211 drawmsg
.lvdm_Bounds
.MaxX
= left
+ width
- 1;
215 /* Update the state */
216 if (data
->ld_Flags
& LVFLG_READONLY
)
219 if (g
->Flags
& GFLG_DISABLED
)
222 /* Find first entry to rerender */
223 entry_count
= data
->ld_Top
+ entryoffset
;
224 for (node
= data
->ld_Labels
->lh_Head
; node
->ln_Succ
&& entry_count
; node
= node
->ln_Succ
)
230 /* Start rerndering entries */
231 D(bug("RenderEntries: About to render %d nodes\n", numentries
));
234 D(bug("RenderEntries: Selected Entry %d\n", data
->ld_Selected
));
235 D(bug("RenderEntries: ShowSelected Gadget 0x%lx\n", data
->ld_ShowSelected
));
236 D(bug("RenderEntries: Flags 0x%lx\n", data
->ld_Flags
));
238 for ( ; node
->ln_Succ
&& numentries
; node
= node
->ln_Succ
)
240 D(bug("RenderEntries: Rendering entry %d: node %s\n", current_entry
, node
->ln_Name
));
243 if ((current_entry
== data
->ld_Selected
) &&
244 ((data
->ld_ShowSelected
!= LV_SHOWSELECTED_NONE
) || (data
->ld_Flags
& LVFLG_FORCE_SELECT_STATE
)))
246 D(bug("RenderEntries: |= SELECTED\n"));
251 /* Call custom render hook */
253 /* Here comes the nice part about the state mechanism ! */
254 D(bug("RenderEntries: Rendering in state %d\n", state
));
255 drawmsg
.lvdm_State
= statetab
[state
];
257 drawmsg
.lvdm_Bounds
.MinY
= top
;
258 drawmsg
.lvdm_Bounds
.MaxY
= top
+ data
->ld_ItemHeight
- 1;
260 CallHookPkt( data
->ld_CallBack
, node
, &drawmsg
);
264 top
+= totalitemheight
;
266 /* Reset SELECTED bit of state */
271 SetFont(msg
->gpr_RPort
, oldfont
);
272 ReturnVoid("RenderEntries");
275 /**********************************************************************************************/
277 STATIC WORD
NumItemsFit(struct Gadget
*g
, struct LVData
*data
)
279 /* Returns the number of items that can possibly fit within the list */
282 EnterFunc(bug("NumItemsFit(g=%p, data=%p)\n",g
, data
));
283 D(bug("NumItemsFit: total spacing: %d\n", TotalItemHeight(data
) ));
285 numfit
= (g
->Height
- 2 * LV_BORDER_Y
) /
286 TotalItemHeight(data
);
288 ReturnInt ("NumItemsFit", UWORD
, numfit
);
292 /**********************************************************************************************/
294 STATIC WORD
ShownEntries(struct Gadget
*g
, struct LVData
*data
)
299 EnterFunc(bug("ShownEntries(g=%p, data=%p)\n", g
, data
));
301 numitemsfit
= NumItemsFit(g
, data
);
303 shown
= ((data
->ld_NumEntries
< numitemsfit
) ? data
->ld_NumEntries
: numitemsfit
);
305 ReturnInt("ShownEntries", WORD
, shown
);
308 /**********************************************************************************************/
310 STATIC VOID
UpdateScroller(struct Gadget
*g
, struct LVData
*data
, struct GadgetInfo
*gi
, struct GadToolsBase_intern
*GadToolsBase
)
313 struct TagItem scrtags
[] =
321 EnterFunc(bug("UpdateScroller(data=%p, gi=%p\n", data
, gi
));
323 if (data
->ld_Scroller
)
325 D(bug("UpdateScroller: Scroller 0x%lx\n",data
->ld_Scroller
));
326 if (data
->ld_NumEntries
> 0)
328 scrtags
[0].ti_Data
= data
->ld_Top
;
329 scrtags
[1].ti_Data
= data
->ld_NumEntries
;
330 scrtags
[2].ti_Data
= ShownEntries(g
, data
);
331 D(bug("UpdateScroller: Top %ld NumEntries %ld ShownEntries %ld\n",data
->ld_Top
,data
->ld_NumEntries
,scrtags
[2].ti_Data
));
335 D(bug("UpdateScroller: no NumEntries\n"));
339 D(bug("UpdateScroller: SetGadgetAttrs\n"));
340 D(Result
= )SetGadgetAttrsA((struct Gadget
*)data
->ld_Scroller
, gi
->gi_Window
, NULL
, scrtags
);
344 D(bug("UpdateScroller: SetAttrs (no gadgetinfo)\n"));
345 D(Result
= )SetAttrsA(data
->ld_Scroller
, scrtags
);
347 D(bug("UpdateScroller: Result 0x%lx\n",Result
));
351 D(bug("UpdateScroller: no ld_Scroller\n"));
354 ReturnVoid("UpdateScroller");
357 /**********************************************************************************************/
359 STATIC VOID
ScrollEntries(struct Gadget
*g
, struct LVData
*data
, WORD old_top
, WORD new_top
,
360 struct GadgetInfo
*gi
, struct GadToolsBase_intern
*GadToolsBase
)
362 EnterFunc(bug("ScrollEntries(new_tio=%d, gi=%p)\n", new_top
, gi
));
364 /* Tries to scroll the listiew to the new top */
365 if (gi
) /* Sanity check */
371 data
->ld_ScrollEntries
= new_top
- old_top
;
372 abs_steps
= abs(data
->ld_ScrollEntries
);
374 /* We do a scroll only if less than half of the visible area
378 if (abs_steps
< (NumItemsFit(g
, data
) >> 1))
380 redraw_type
= GREDRAW_UPDATE
;
384 redraw_type
= GREDRAW_REDRAW
;
386 data
->ld_ScrollEntries
= 0;
391 if ( (rp
= ObtainGIRPort(gi
)) )
393 DoMethod((Object
*)g
, GM_RENDER
, (IPTR
) gi
, (IPTR
) rp
, redraw_type
);
400 ReturnVoid("ScrollEntries");
403 /**********************************************************************************************/
405 STATIC VOID
DoShowSelected(struct LVData
*data
, struct GadgetInfo
*gi
, struct GadToolsBase_intern
*GadToolsBase
)
407 D(bug("DoShowSelected:\n"));
408 if (data
->ld_ShowSelected
&& (data
->ld_ShowSelected
!= LV_SHOWSELECTED_NONE
))
410 struct TagItem set_tags
[] =
412 {GTST_String
, (IPTR
)"" },
416 if ((data
->ld_Selected
>= 0) && (data
->ld_Selected
< data
->ld_NumEntries
))
421 ForeachNode(data
->ld_Labels
, node
)
423 if (i
++ == data
->ld_Selected
)
425 set_tags
[0].ti_Data
= (IPTR
)node
->ln_Name
;
432 GT_SetGadgetAttrsA(data
->ld_ShowSelected
, gi
? gi
->gi_Window
: NULL
, NULL
, set_tags
);
434 } /* if (data->ld_ShowSelected && (data->ld_ShowSelected != LV_SHOWSELECTED_NONE)) */
437 /**********************************************************************************************/
439 #define GadToolsBase ((struct GadToolsBase_intern *)cl->cl_UserData)
441 /**********************************************************************************************/
443 STATIC IPTR
listview_set(Class
*cl
, struct Gadget
*g
,struct opSet
*msg
)
447 struct TagItem
*tag
, *tstate
;
448 struct LVData
*data
= INST_DATA(cl
, g
);
451 BOOL update_scroller
= FALSE
;
452 BOOL scroll_entries
= FALSE
;
453 BOOL refresh_all
= FALSE
;
455 WORD new_top
= 0, old_top
= 0;
457 EnterFunc(bug("Listview::Set: Data 0x%lx\n",data
));
459 tstate
= msg
->ops_AttrList
;
460 while ((tag
= NextTagItem(&tstate
)) != NULL
)
462 IPTR tidata
= tag
->ti_Data
;
466 case GTA_Listview_Scroller
: /* [IS] */
467 data
->ld_Scroller
= (Object
*)tidata
;
468 D(bug("Listview::Set: GTA_Listview_Scroller Scroller 0x%lx\n",data
->ld_Scroller
));
469 update_scroller
= TRUE
;
472 case GTLV_Top
: /* [IS] */
473 D(bug("Listview::Set: GTLV_Top\n"));
474 old_top
= data
->ld_Top
;
475 new_top
= (WORD
)tidata
;
480 } else if (new_top
> data
->ld_NumEntries
- ShownEntries(g
, data
))
482 new_top
= data
->ld_NumEntries
- ShownEntries(g
, data
);
485 if (data
->ld_Top
!= new_top
)
487 data
->ld_Top
= new_top
;
489 update_scroller
= TRUE
;
490 scroll_entries
= TRUE
;
496 case GTLV_MakeVisible
: /* [IS] */
497 D(bug("Listview::Set: GTLV_MakeVisible\n"));
498 old_top
= data
->ld_Top
;
499 new_top
= (WORD
)tidata
;
504 } else if (new_top
>= data
->ld_NumEntries
)
506 new_top
= data
->ld_NumEntries
- 1;
507 if (new_top
< 0) new_top
= 0;
510 /* No need to do anything if it is already visible */
512 if (new_top
< data
->ld_Top
)
514 /* new_top already okay */
516 data
->ld_Top
= new_top
;
517 update_scroller
= TRUE
;
518 scroll_entries
= TRUE
;
520 else if (new_top
>= data
->ld_Top
+ NumItemsFit(g
, data
))
522 new_top
-= (NumItemsFit(g
, data
) - 1);
524 data
->ld_Top
= new_top
;
525 update_scroller
= TRUE
;
526 scroll_entries
= TRUE
;
532 case GTLV_Labels
: /* [IS] */
533 D(bug("Listview::Set: GTLV_Labels\n"));
534 data
->ld_Labels
= (struct List
*)tidata
;
535 // data->ld_Selected = ~0;
541 data
->ld_NumEntries
= 0;
543 if (data
->ld_Labels
&& (data
->ld_Labels
!= (struct List
*)~0))
545 /* Update the labelcount */
546 ForeachNode(data
->ld_Labels
, n
)
548 data
->ld_NumEntries
++;
551 D(bug("Listview::Set: Number of items added: %d\n", data
->ld_NumEntries
));
554 DoShowSelected(data
, msg
->ops_GInfo
, GadToolsBase
);
557 update_scroller
= TRUE
;
561 case GTLV_ReadOnly
: /* [I] */
562 D(bug("Listview::Set: GTLV_ReadOnly\n"));
564 data
->ld_Flags
|= LVFLG_READONLY
;
566 data
->ld_Flags
&= ~LVFLG_READONLY
;
568 D(bug("Readonly: tidata=%d, flags=%d\n",
569 tidata
, data
->ld_Flags
));
572 case GTLV_Selected
: /* [IS] */
575 WORD old_selected
= data
->ld_Selected
;
577 D(bug("Listview::Set: GTLV_Selected 0x%lx\n",tidata
));
579 data
->ld_Selected
= (WORD
)tidata
;
581 if (old_selected
!= data
->ld_Selected
)
583 D(bug("Listview::Set: old_Selected %ld != Selected %ld\n",old_selected
,data
->ld_Selected
));
584 if ((rp
= ObtainGIRPort(msg
->ops_GInfo
)))
586 /* rerender old selected if it was visible */
588 if ((old_selected
>= data
->ld_Top
) &&
589 (old_selected
< data
->ld_Top
+ NumItemsFit(g
, data
)))
591 D(bug("Listview::Set: rerender old_Selected\n"));
592 data
->ld_FirstDamaged
= old_selected
- data
->ld_Top
;
593 data
->ld_NumDamaged
= 1;
595 DoMethod((Object
*)g
, GM_RENDER
, (IPTR
) msg
->ops_GInfo
, (IPTR
) rp
, GREDRAW_UPDATE
);
598 /* rerender new selected if it is visible */
600 if ((data
->ld_Selected
>= data
->ld_Top
) &&
601 (data
->ld_Selected
< data
->ld_Top
+ NumItemsFit(g
, data
)))
603 D(bug("Listview::Set: rerender new Selected\n"));
604 data
->ld_FirstDamaged
= data
->ld_Selected
- data
->ld_Top
;
605 data
->ld_NumDamaged
= 1;
607 DoMethod((Object
*)g
, GM_RENDER
, (IPTR
) msg
->ops_GInfo
, (IPTR
) rp
, GREDRAW_UPDATE
);
612 } /* if ((rp = ObtainGIRPort(msg->ops_GInfo))) */
615 D(bug("Listview::Set: no rastport\n"));
617 DoShowSelected(data
, msg
->ops_GInfo
, GadToolsBase
);
619 } /* if (old_selected != data->ld_Selected) */
625 case LAYOUTA_Spacing
: /* [I] */
626 D(bug("Listview::Set: LAYOUTA_Spacing\n"));
627 data
->ld_Spacing
= (UWORD
)tidata
;
630 case GTLV_ItemHeight
: /* [I] */
631 D(bug("Listview::Set: GTLV_ItemHeight\n"));
632 data
->ld_ItemHeight
= (UWORD
)tidata
;
635 case GTLV_CallBack
: /* [I] */
636 D(bug("Listview::Set: GTLV_CallBack\n"));
637 data
->ld_CallBack
= (struct Hook
*)tidata
;
640 case GA_LabelPlace
: /* [I] */
641 D(bug("Listview::Set: GTLV_LabelPlace\n"));
642 data
->ld_LabelPlace
= (LONG
)tidata
;
647 struct TagItem set_tags
[] =
649 {GA_Disabled
, tag
->ti_Data
},
653 D(bug("Listview::Set: GA_Disabled\n"));
654 if (data
->ld_Scroller
)
658 SetGadgetAttrsA((struct Gadget
*)data
->ld_Scroller
, msg
->ops_GInfo
->gi_Window
, 0, set_tags
);
660 SetAttrsA(data
->ld_Scroller
, set_tags
);
667 } /* switch (tag->ti_Tag) */
669 } /* while (more tags to iterate) */
673 /* IMPORTANT! If this is an OM_UPDATE, we should NOT redraw the
674 ** set the scroller, as we the scroller has allready been updated
676 D(bug("Listview::Set: update_scroller flag\n"));
677 if (msg
->MethodID
!= OM_UPDATE
)
679 D(bug("Listview::Set: MethodID 0x%lx\n",msg
->MethodID
));
680 UpdateScroller(g
, data
, msg
->ops_GInfo
, GadToolsBase
);
684 D(bug("Listview::Set: don't update scroller as OM_UPDATE is used\n"));
688 if (scroll_entries
&& !refresh_all
)
690 ScrollEntries(g
, data
, old_top
, new_top
, msg
->ops_GInfo
, GadToolsBase
);
693 if (refresh_all
&& msg
->ops_GInfo
)
695 if ((rp
= ObtainGIRPort(msg
->ops_GInfo
)))
697 DoMethod((Object
*)g
, GM_RENDER
, (IPTR
) msg
->ops_GInfo
, (IPTR
) rp
, GREDRAW_REDRAW
);
703 ReturnInt ("Listview::Set", IPTR
, retval
);
706 /**********************************************************************************************/
708 struct Gadget
*GTListView__OM_NEW(Class
*cl
, Object
*o
, struct opSet
*msg
)
710 struct DrawInfo
*dri
;
713 EnterFunc(bug("Listview::New()\n"));
715 dri
= (struct DrawInfo
*)GetTagData(GA_DrawInfo
, (IPTR
) NULL
, msg
->ops_AttrList
);
717 ReturnPtr ("Listview::New", Object
*, NULL
);
719 D(bug("GTListView__OM_NEW: Got dri: %p, dri font=%p, size=%d\n", dri
, dri
->dri_Font
, dri
->dri_Font
->tf_YSize
));
721 g
= (struct Gadget
*)DoSuperMethodA(cl
, o
, (Msg
)msg
);
725 struct LVData
*data
= INST_DATA(cl
, g
);
726 struct TextAttr
*tattr
;
728 struct TagItem fitags
[] =
732 {IA_Resolution
, 0UL },
733 {IA_FrameType
, FRAME_BUTTON
},
734 {IA_EdgesOnly
, TRUE
},
738 /* Create a frame for the listview */
739 data
->ld_Frame
= NewObjectA(NULL
, FRAMEICLASS
, fitags
);
742 CoerceMethod(cl
, (Object
*)g
, OM_DISPOSE
);
750 /* Set some defaults */
751 tattr
= (struct TextAttr
*) GetTagData(GA_TextAttr
, (IPTR
) NULL
, msg
->ops_AttrList
);
754 data
->ld_Font
= OpenFont(tattr
);
757 data
->ld_Flags
|= LVFLG_FONT_OPENED
;
763 data
->ld_Font
= dri
->dri_Font
;
765 data
->ld_ItemHeight
= data
->ld_Font
->tf_YSize
;
766 data
->ld_LabelPlace
= GV_LabelPlace_Above
;
767 data
->ld_Spacing
= LV_DEF_INTERNAL_SPACING
;
769 /* default render hook */
770 data
->ld_CallBack
= &GadToolsBase
->lv_RenderHook
;
772 data
->ld_ShowSelected
= (struct Gadget
*)GetTagData(GTLV_ShowSelected
, (IPTR
)LV_SHOWSELECTED_NONE
, msg
->ops_AttrList
);
774 D(bug("GTListView__OM_NEW: Selected %ld\n", data
->ld_ShowSelected
));
776 listview_set(cl
, g
, msg
);
778 } /* if (frame created) */
780 } /* if (object created) */
782 ReturnPtr ("Listview::New", struct Gadget
*, g
);
785 /**********************************************************************************************/
787 IPTR
GTListView__OM_GET(Class
*cl
, struct Gadget
*g
, struct opGet
*msg
)
792 data
= INST_DATA(cl
, g
);
794 switch (msg
->opg_AttrID
)
797 case GTA_ChildGadgetKind
:
798 *(msg
->opg_Storage
) = LISTVIEW_KIND
;
802 *(msg
->opg_Storage
) = (IPTR
)data
->ld_Top
;
805 case GTLV_Visible
: /* AROS Extension */
806 *(msg
->opg_Storage
) = (IPTR
)NumItemsFit(g
, data
);
809 case GTLV_Total
: /* AROS Extension */
810 *(msg
->opg_Storage
) = (IPTR
)data
->ld_NumEntries
;
814 *(msg
->opg_Storage
) = (IPTR
)data
->ld_Selected
;
818 *(msg
->opg_Storage
) = (IPTR
)data
->ld_Labels
;
822 retval
= DoSuperMethodA(cl
, (Object
*)g
, (Msg
)msg
);
828 /**********************************************************************************************/
830 IPTR
GTListView__OM_DISPOSE(Class
*cl
, Object
*o
, Msg msg
)
832 struct LVData
*data
= INST_DATA(cl
, o
);
834 if (data
->ld_Flags
& LVFLG_FONT_OPENED
)
835 CloseFont(data
->ld_Font
);
837 if (data
->ld_Frame
) DisposeObject(data
->ld_Frame
);
839 return DoSuperMethodA(cl
, o
, msg
);
842 /**********************************************************************************************/
844 IPTR
GTListView__GM_HANDLEINPUT(Class
*cl
, struct Gadget
*g
, struct gpInput
*msg
)
846 struct LVData
*data
= INST_DATA(cl
, g
);
850 EnterFunc(bug("Listview::GoActive()\n"));
852 if (msg
->MethodID
== GM_GOACTIVE
)
854 if ((!msg
->gpi_IEvent
) || /* Not activated by user ? */
855 (data
->ld_Flags
& LVFLG_READONLY
) ||
856 (!data
->ld_Labels
) ||
857 (data
->ld_Labels
== (struct List
*)~0))
859 ReturnInt("Listview::GoActive", IPTR
, GMR_NOREUSE
);
863 /* How many entries are currently shown in the Gtlv ? */
864 shown
= ShownEntries(g
, data
);
866 if ((msg
->gpi_IEvent
->ie_Class
== IECLASS_RAWMOUSE
) ||
867 (msg
->gpi_IEvent
->ie_Class
== IECLASS_TIMER
))
869 if ((msg
->gpi_IEvent
->ie_Class
== IECLASS_TIMER
) ||
870 (msg
->gpi_IEvent
->ie_Code
== SELECTDOWN
) ||
871 (msg
->gpi_IEvent
->ie_Code
== IECODE_NOBUTTON
))
873 /* offset from top of listview of the entry clicked */
874 clickpos
= (msg
->gpi_Mouse
.Y
- LV_BORDER_Y
) /
875 TotalItemHeight(data
);
879 if (data
->ld_Top
> 0)
881 struct TagItem set_tags
[] =
883 {GTLV_Top
, data
->ld_Top
- 1},
887 DoMethod((Object
*)g
, OM_SET
, (IPTR
) set_tags
, (IPTR
) msg
->gpi_GInfo
);
892 } else if (clickpos
>= shown
)
894 WORD max_top
= data
->ld_NumEntries
- NumItemsFit(g
, data
);
896 if (max_top
< 0) max_top
= 0;
898 if (data
->ld_Top
< max_top
)
900 struct TagItem set_tags
[] =
902 {GTLV_Top
, data
->ld_Top
+ 1},
906 DoMethod((Object
*)g
, OM_SET
, (IPTR
) set_tags
, (IPTR
) msg
->gpi_GInfo
);
909 clickpos
= shown
- 1;
913 if ((clickpos
>= 0) && (clickpos
< shown
))
915 if ((clickpos
+ data
->ld_Top
!= data
->ld_Selected
) ||
916 ((data
->ld_ShowSelected
== LV_SHOWSELECTED_NONE
) && (msg
->MethodID
== GM_GOACTIVE
)))
919 WORD oldpos
= data
->ld_Selected
;
921 data
->ld_Selected
= clickpos
+ data
->ld_Top
;
923 rp
= ObtainGIRPort(msg
->gpi_GInfo
);
926 /* Rerender new active */
927 data
->ld_FirstDamaged
= clickpos
;
928 data
->ld_NumDamaged
= 1;
930 data
->ld_Flags
|= LVFLG_FORCE_SELECT_STATE
;
931 DoMethod((Object
*)g
, GM_RENDER
, (IPTR
) msg
->gpi_GInfo
, (IPTR
) rp
, GREDRAW_UPDATE
);
933 /* Rerender old active if it was shown in the listview */
934 if ( (oldpos
>= data
->ld_Top
)
935 && (oldpos
< data
->ld_Top
+ NumItemsFit(g
, data
))
936 && (oldpos
!= data
->ld_Selected
) )
939 data
->ld_FirstDamaged
= oldpos
- data
->ld_Top
;
940 data
->ld_NumDamaged
= 1;
942 DoMethod((Object
*)g
, GM_RENDER
, (IPTR
) msg
->gpi_GInfo
, (IPTR
) rp
, GREDRAW_UPDATE
);
949 DoShowSelected(data
, msg
->gpi_GInfo
, GadToolsBase
);
951 } /* if (click wasn't on old active item) */
953 } /* if (entry is shown) */
955 } /* if mouse down or mouse move event */
956 else if (msg
->gpi_IEvent
->ie_Code
== SELECTUP
)
958 *(msg
->gpi_Termination
) = data
->ld_Selected
;
959 ReturnInt ("ListView::Input", IPTR
, GMR_VERIFY
| GMR_NOREUSE
);
960 } /* mouse up event */
962 } /* if (is mouse event) */
964 ReturnInt ("Listview::Input", IPTR
, GMR_MEACTIVE
);
967 /**********************************************************************************************/
969 IPTR
GTListView__GM_GOINACTIVE(Class
*cl
, struct Gadget
*g
, struct gpGoInactive
*msg
)
971 struct LVData
*data
= INST_DATA(cl
, g
);
974 if ((data
->ld_ShowSelected
== LV_SHOWSELECTED_NONE
) &&
975 (data
->ld_Selected
>= data
->ld_Top
) &&
976 (data
->ld_Selected
< data
->ld_Top
+ NumItemsFit(g
, data
)))
978 if ((rp
= ObtainGIRPort(msg
->gpgi_GInfo
)))
980 data
->ld_FirstDamaged
= data
->ld_Selected
- data
->ld_Top
;
981 data
->ld_NumDamaged
= 1;
983 DoMethod((Object
*)g
, GM_RENDER
, (IPTR
) msg
->gpgi_GInfo
, (IPTR
) rp
, GREDRAW_UPDATE
);
989 ReturnInt ("Listview::GoInactive", IPTR
, 0);
992 /**********************************************************************************************/
994 IPTR
GTListView__GM_RENDER(Class
*cl
, struct Gadget
*g
, struct gpRender
*msg
)
996 struct LVData
*data
= INST_DATA(cl
, g
);
997 BOOL mustrefresh
= FALSE
;
999 EnterFunc(bug("Listview::Render()\n"));
1001 switch (msg
->gpr_Redraw
)
1003 case GREDRAW_REDRAW
: {
1006 struct TagItem itags
[] =
1013 D(bug("GTListView__GM_RENDER: GREDRAW_REDRAW\n"));
1015 /* Erase the old gadget imagery */
1016 SetAPen(msg
->gpr_RPort
, data
->ld_Dri
->dri_Pens
[BACKGROUNDPEN
]);
1018 RectFill(msg
->gpr_RPort
,
1021 g
->LeftEdge
+ g
->Width
- 1,
1022 g
->TopEdge
+ g
->Height
- 1);
1024 RenderEntries(cl
, g
, msg
, 0, ShownEntries(g
, data
), GadToolsBase
);
1026 /* center image position, we assume image top and left is 0 */
1027 itags
[0].ti_Data
= g
->Width
;
1028 itags
[1].ti_Data
= g
->Height
;
1030 SetAttrsA((Object
*)data
->ld_Frame
, itags
);
1035 DrawImageState(msg
->gpr_RPort
,
1036 (struct Image
*)data
->ld_Frame
,
1038 ((data
->ld_Flags
& LVFLG_READONLY
) ? IDS_SELECTED
: IDS_NORMAL
),
1039 msg
->gpr_GInfo
->gi_DrInfo
);
1041 /* Render gadget label */
1042 renderlabel(GadToolsBase
, g
, msg
->gpr_RPort
, data
->ld_LabelPlace
);
1046 case GREDRAW_UPDATE
:
1048 D(bug("GTListView__GM_RENDER: GREDRAW_UPDATE\n"));
1050 /* Should we scroll the listview ? */
1051 if (data
->ld_ScrollEntries
)
1053 UWORD abs_steps
, visible
;
1056 abs_steps
= abs(data
->ld_ScrollEntries
);
1057 visible
= NumItemsFit(g
, data
);
1059 /* We make the assumption that the listview
1060 ** is always 'full'. If it isn't, the
1061 ** Scroll gadget won't be scrollable, and
1062 ** we won't receive any OM_UPDATEs.
1065 dy
= data
->ld_ScrollEntries
* TotalItemHeight(data
);
1067 D(bug("GTListView__GM_RENDER: Scrolling delta y: %d\n", dy
));
1069 ScrollRaster(msg
->gpr_RPort
, 0, dy
,
1070 g
->LeftEdge
+ LV_BORDER_X
,
1071 g
->TopEdge
+ LV_BORDER_Y
,
1072 g
->LeftEdge
+ g
->Width
- 1 - LV_BORDER_X
,
1073 g
->TopEdge
+ LV_BORDER_Y
+ NumItemsFit(g
, data
) * TotalItemHeight(data
) - 1);
1075 mustrefresh
= (msg
->gpr_GInfo
->gi_Layer
->Flags
& LAYERREFRESH
) != 0;
1077 data
->ld_FirstDamaged
= ((data
->ld_ScrollEntries
> 0) ?
1078 visible
- abs_steps
: 0);
1080 data
->ld_NumDamaged
= abs_steps
;
1082 data
->ld_ScrollEntries
= 0;
1084 } /* If (we should do a scroll) */
1086 D(bug("GTListView__GM_RENDER: Rerendering entries: first damaged=%d, num=%d\n",
1087 data
->ld_FirstDamaged
, data
->ld_NumDamaged
));
1089 /* Redraw all damaged entries */
1090 if (data
->ld_FirstDamaged
!= -1)
1093 RenderEntries(cl
, g
, msg
,
1094 data
->ld_FirstDamaged
,
1095 data
->ld_NumDamaged
,
1098 data
->ld_FirstDamaged
= -1;
1102 /* the LAYERUPDATING check should not be necessary,
1103 as then we should always have a GREDRAW_REDRAW,
1104 while here we are in GREDRAW_UPDATE. But just
1107 if (mustrefresh
&& !(msg
->gpr_GInfo
->gi_Layer
->Flags
& LAYERUPDATING
))
1111 if(!(update
= BeginUpdate(msg
->gpr_GInfo
->gi_Layer
)))
1113 EndUpdate(msg
->gpr_GInfo
->gi_Layer
, FALSE
);
1116 RenderEntries(cl
, g
, msg
, 0, ShownEntries(g
, data
), GadToolsBase
);
1118 if(update
) EndUpdate(msg
->gpr_GInfo
->gi_Layer
, TRUE
);
1121 break; /* GREDRAW_UPDATE */
1123 } /* switch (render mode) */
1125 if (g
->Flags
& GFLG_DISABLED
)
1127 DoDisabledPattern(msg
->gpr_RPort
, g
->LeftEdge
,
1129 g
->LeftEdge
+ g
->Width
- 1,
1130 g
->TopEdge
+ g
->Height
- 1,
1131 msg
->gpr_GInfo
->gi_DrInfo
->dri_Pens
[SHADOWPEN
],
1135 data
->ld_Flags
&= ~LVFLG_FORCE_SELECT_STATE
;
1137 ReturnInt ("Listview::Render", IPTR
, 1UL);
1140 /**********************************************************************************************/
1142 IPTR
GTListView__OM_SET(Class
*cl
, struct Gadget
*g
, struct opSet
*msg
)
1145 if (msg
->MethodID
== OM_UPDATE
)
1147 LONG top
= GetTagData(GTLV_Top
, 148, msg
->ops_AttrList
);
1148 bug("dispatch_listviewclass: OM_UPDATE: top=%d, attrs=%p, gi=%p\n",
1149 top
, msg
->ops_AttrList
, msg
->ops_GInfo
);
1152 D(bug("dispatch_listviewclass: OM_SET\n"));
1154 return DoSuperMethodA(cl
, (Object
*)g
, (Msg
)msg
) + listview_set(cl
, g
, msg
);
1157 /**********************************************************************************************/
1161 static int LV_RenderHook_Init(LIBBASETYPEPTR LIBBASE
)
1163 LIBBASE
->lv_RenderHook
.h_Entry
= (APTR
) AROS_ASMSYMNAME(RenderHook
);
1164 LIBBASE
->lv_RenderHook
.h_SubEntry
= NULL
;
1165 LIBBASE
->lv_RenderHook
.h_Data
= (APTR
)LIBBASE
;
1170 ADD2INITLIB(LV_RenderHook_Init
, 0)
1172 /**********************************************************************************************/