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
)
242 D(bug("RenderEntries: Rendering entry %d: node %s\n", current_entry
, node
->ln_Name
));
245 if ((current_entry
== data
->ld_Selected
) &&
246 ((data
->ld_ShowSelected
!= LV_SHOWSELECTED_NONE
) || (data
->ld_Flags
& LVFLG_FORCE_SELECT_STATE
)))
248 D(bug("RenderEntries: |= SELECTED\n"));
253 /* Call custom render hook */
255 /* Here comes the nice part about the state mechanism ! */
256 D(bug("RenderEntries: Rendering in state %d\n", state
));
257 drawmsg
.lvdm_State
= statetab
[state
];
259 drawmsg
.lvdm_Bounds
.MinY
= top
;
260 drawmsg
.lvdm_Bounds
.MaxY
= top
+ data
->ld_ItemHeight
- 1;
262 retval
= CallHookPkt( data
->ld_CallBack
, node
, &drawmsg
);
266 top
+= totalitemheight
;
268 /* Reset SELECTED bit of state */
273 SetFont(msg
->gpr_RPort
, oldfont
);
274 ReturnVoid("RenderEntries");
277 /**********************************************************************************************/
279 STATIC WORD
NumItemsFit(struct Gadget
*g
, struct LVData
*data
)
281 /* Returns the number of items that can possibly fit within the list */
284 EnterFunc(bug("NumItemsFit(g=%p, data=%p)\n",g
, data
));
285 D(bug("NumItemsFit: total spacing: %d\n", TotalItemHeight(data
) ));
287 numfit
= (g
->Height
- 2 * LV_BORDER_Y
) /
288 TotalItemHeight(data
);
290 ReturnInt ("NumItemsFit", UWORD
, numfit
);
294 /**********************************************************************************************/
296 STATIC WORD
ShownEntries(struct Gadget
*g
, struct LVData
*data
)
301 EnterFunc(bug("ShownEntries(g=%p, data=%p)\n", g
, data
));
303 numitemsfit
= NumItemsFit(g
, data
);
305 shown
= ((data
->ld_NumEntries
< numitemsfit
) ? data
->ld_NumEntries
: numitemsfit
);
307 ReturnInt("ShownEntries", WORD
, shown
);
310 /**********************************************************************************************/
312 STATIC VOID
UpdateScroller(struct Gadget
*g
, struct LVData
*data
, struct GadgetInfo
*gi
, struct GadToolsBase_intern
*GadToolsBase
)
315 struct TagItem scrtags
[] =
323 EnterFunc(bug("UpdateScroller(data=%p, gi=%p\n", data
, gi
));
325 if (data
->ld_Scroller
)
327 D(bug("UpdateScroller: Scroller 0x%lx\n",data
->ld_Scroller
));
328 if (data
->ld_NumEntries
> 0)
330 scrtags
[0].ti_Data
= data
->ld_Top
;
331 scrtags
[1].ti_Data
= data
->ld_NumEntries
;
332 scrtags
[2].ti_Data
= ShownEntries(g
, data
);
333 D(bug("UpdateScroller: Top %ld NumEntries %ld ShownEntries %ld\n",data
->ld_Top
,data
->ld_NumEntries
,scrtags
[2].ti_Data
));
337 D(bug("UpdateScroller: no NumEntries\n"));
341 D(bug("UpdateScroller: SetGadgetAttrs\n"));
342 Result
= SetGadgetAttrsA((struct Gadget
*)data
->ld_Scroller
, gi
->gi_Window
, NULL
, scrtags
);
346 D(bug("UpdateScroller: SetAttrs (no gadgetinfo)\n"));
347 Result
= SetAttrsA(data
->ld_Scroller
, scrtags
);
349 D(bug("UpdateScroller: Result 0x%lx\n",Result
));
353 D(bug("UpdateScroller: no ld_Scroller\n"));
356 ReturnVoid("UpdateScroller");
359 /**********************************************************************************************/
361 STATIC VOID
ScrollEntries(struct Gadget
*g
, struct LVData
*data
, WORD old_top
, WORD new_top
,
362 struct GadgetInfo
*gi
, struct GadToolsBase_intern
*GadToolsBase
)
364 EnterFunc(bug("ScrollEntries(new_tio=%d, gi=%p)\n", new_top
, gi
));
366 /* Tries to scroll the listiew to the new top */
367 if (gi
) /* Sanity check */
373 data
->ld_ScrollEntries
= new_top
- old_top
;
374 abs_steps
= abs(data
->ld_ScrollEntries
);
376 /* We do a scroll only if less than half of the visible area
380 if (abs_steps
< (NumItemsFit(g
, data
) >> 1))
382 redraw_type
= GREDRAW_UPDATE
;
386 redraw_type
= GREDRAW_REDRAW
;
388 data
->ld_ScrollEntries
= 0;
393 if ( (rp
= ObtainGIRPort(gi
)) )
395 DoMethod((Object
*)g
, GM_RENDER
, (IPTR
) gi
, (IPTR
) rp
, redraw_type
);
402 ReturnVoid("ScrollEntries");
405 /**********************************************************************************************/
407 STATIC VOID
DoShowSelected(struct LVData
*data
, struct GadgetInfo
*gi
, struct GadToolsBase_intern
*GadToolsBase
)
409 D(bug("DoShowSelected:\n"));
410 if (data
->ld_ShowSelected
&& (data
->ld_ShowSelected
!= LV_SHOWSELECTED_NONE
))
412 struct TagItem set_tags
[] =
414 {GTST_String
, (IPTR
)"" },
418 if ((data
->ld_Selected
>= 0) && (data
->ld_Selected
< data
->ld_NumEntries
))
423 ForeachNode(data
->ld_Labels
, node
)
425 if (i
++ == data
->ld_Selected
)
427 set_tags
[0].ti_Data
= (IPTR
)node
->ln_Name
;
434 GT_SetGadgetAttrsA(data
->ld_ShowSelected
, gi
? gi
->gi_Window
: NULL
, NULL
, set_tags
);
436 } /* if (data->ld_ShowSelected && (data->ld_ShowSelected != LV_SHOWSELECTED_NONE)) */
439 /**********************************************************************************************/
441 #define GadToolsBase ((struct GadToolsBase_intern *)cl->cl_UserData)
443 /**********************************************************************************************/
445 STATIC IPTR
listview_set(Class
*cl
, struct Gadget
*g
,struct opSet
*msg
)
449 struct TagItem
*tag
, *tstate
;
450 struct LVData
*data
= INST_DATA(cl
, g
);
453 BOOL update_scroller
= FALSE
;
454 BOOL scroll_entries
= FALSE
;
455 BOOL refresh_all
= FALSE
;
457 WORD new_top
= 0, old_top
= 0;
459 EnterFunc(bug("Listview::Set: Data 0x%lx\n",data
));
461 tstate
= msg
->ops_AttrList
;
462 while ((tag
= NextTagItem((const struct TagItem
**)&tstate
)) != NULL
)
464 IPTR tidata
= tag
->ti_Data
;
468 case GTA_Listview_Scroller
: /* [IS] */
469 data
->ld_Scroller
= (Object
*)tidata
;
470 D(bug("Listview::Set: GTA_Listview_Scroller Scroller 0x%lx\n",data
->ld_Scroller
));
471 update_scroller
= TRUE
;
474 case GTLV_Top
: /* [IS] */
475 D(bug("Listview::Set: GTLV_Top\n"));
476 old_top
= data
->ld_Top
;
477 new_top
= (WORD
)tidata
;
482 } else if (new_top
> data
->ld_NumEntries
- ShownEntries(g
, data
))
484 new_top
= data
->ld_NumEntries
- ShownEntries(g
, data
);
487 if (data
->ld_Top
!= new_top
)
489 data
->ld_Top
= new_top
;
491 update_scroller
= TRUE
;
492 scroll_entries
= TRUE
;
498 case GTLV_MakeVisible
: /* [IS] */
499 D(bug("Listview::Set: GTLV_MakeVisible\n"));
500 old_top
= data
->ld_Top
;
501 new_top
= (WORD
)tidata
;
506 } else if (new_top
>= data
->ld_NumEntries
)
508 new_top
= data
->ld_NumEntries
- 1;
509 if (new_top
< 0) new_top
= 0;
512 /* No need to do anything if it is already visible */
514 if (new_top
< data
->ld_Top
)
516 /* new_top already okay */
518 data
->ld_Top
= new_top
;
519 update_scroller
= TRUE
;
520 scroll_entries
= TRUE
;
522 else if (new_top
>= data
->ld_Top
+ NumItemsFit(g
, data
))
524 new_top
-= (NumItemsFit(g
, data
) - 1);
526 data
->ld_Top
= new_top
;
527 update_scroller
= TRUE
;
528 scroll_entries
= TRUE
;
534 case GTLV_Labels
: /* [IS] */
535 D(bug("Listview::Set: GTLV_Labels\n"));
536 data
->ld_Labels
= (struct List
*)tidata
;
537 // data->ld_Selected = ~0;
543 data
->ld_NumEntries
= 0;
545 if (data
->ld_Labels
&& (data
->ld_Labels
!= (struct List
*)~0))
547 /* Update the labelcount */
548 ForeachNode(data
->ld_Labels
, n
)
550 data
->ld_NumEntries
++;
553 D(bug("Listview::Set: Number of items added: %d\n", data
->ld_NumEntries
));
556 DoShowSelected(data
, msg
->ops_GInfo
, GadToolsBase
);
559 update_scroller
= TRUE
;
563 case GTLV_ReadOnly
: /* [I] */
564 D(bug("Listview::Set: GTLV_ReadOnly\n"));
566 data
->ld_Flags
|= LVFLG_READONLY
;
568 data
->ld_Flags
&= ~LVFLG_READONLY
;
570 D(bug("Readonly: tidata=%d, flags=%d\n",
571 tidata
, data
->ld_Flags
));
574 case GTLV_Selected
: /* [IS] */
577 WORD old_selected
= data
->ld_Selected
;
579 D(bug("Listview::Set: GTLV_Selected 0x%lx\n",tidata
));
581 data
->ld_Selected
= (WORD
)tidata
;
583 if (old_selected
!= data
->ld_Selected
)
585 D(bug("Listview::Set: old_Selected %ld != Selected %ld\n",old_selected
,data
->ld_Selected
));
586 if ((rp
= ObtainGIRPort(msg
->ops_GInfo
)))
588 /* rerender old selected if it was visible */
590 if ((old_selected
>= data
->ld_Top
) &&
591 (old_selected
< data
->ld_Top
+ NumItemsFit(g
, data
)))
593 D(bug("Listview::Set: rerender old_Selected\n"));
594 data
->ld_FirstDamaged
= old_selected
- data
->ld_Top
;
595 data
->ld_NumDamaged
= 1;
597 DoMethod((Object
*)g
, GM_RENDER
, (IPTR
) msg
->ops_GInfo
, (IPTR
) rp
, GREDRAW_UPDATE
);
600 /* rerender new selected if it is visible */
602 if ((data
->ld_Selected
>= data
->ld_Top
) &&
603 (data
->ld_Selected
< data
->ld_Top
+ NumItemsFit(g
, data
)))
605 D(bug("Listview::Set: rerender new Selected\n"));
606 data
->ld_FirstDamaged
= data
->ld_Selected
- data
->ld_Top
;
607 data
->ld_NumDamaged
= 1;
609 DoMethod((Object
*)g
, GM_RENDER
, (IPTR
) msg
->ops_GInfo
, (IPTR
) rp
, GREDRAW_UPDATE
);
614 } /* if ((rp = ObtainGIRPort(msg->ops_GInfo))) */
617 D(bug("Listview::Set: no rastport\n"));
619 DoShowSelected(data
, msg
->ops_GInfo
, GadToolsBase
);
621 } /* if (old_selected != data->ld_Selected) */
627 case LAYOUTA_Spacing
: /* [I] */
628 D(bug("Listview::Set: LAYOUTA_Spacing\n"));
629 data
->ld_Spacing
= (UWORD
)tidata
;
632 case GTLV_ItemHeight
: /* [I] */
633 D(bug("Listview::Set: GTLV_ItemHeight\n"));
634 data
->ld_ItemHeight
= (UWORD
)tidata
;
637 case GTLV_CallBack
: /* [I] */
638 D(bug("Listview::Set: GTLV_CallBack\n"));
639 data
->ld_CallBack
= (struct Hook
*)tidata
;
642 case GA_LabelPlace
: /* [I] */
643 D(bug("Listview::Set: GTLV_LabelPlace\n"));
644 data
->ld_LabelPlace
= (LONG
)tidata
;
649 struct TagItem set_tags
[] =
651 {GA_Disabled
, tag
->ti_Data
},
655 D(bug("Listview::Set: GA_Disabled\n"));
656 if (data
->ld_Scroller
)
660 SetGadgetAttrsA((struct Gadget
*)data
->ld_Scroller
, msg
->ops_GInfo
->gi_Window
, 0, set_tags
);
662 SetAttrsA(data
->ld_Scroller
, set_tags
);
669 } /* switch (tag->ti_Tag) */
671 } /* while (more tags to iterate) */
675 /* IMPORTANT! If this is an OM_UPDATE, we should NOT redraw the
676 ** set the scroller, as we the scroller has allready been updated
678 D(bug("Listview::Set: update_scroller flag\n"));
679 if (msg
->MethodID
!= OM_UPDATE
)
681 D(bug("Listview::Set: MethodID 0x%lx\n",msg
->MethodID
));
682 UpdateScroller(g
, data
, msg
->ops_GInfo
, GadToolsBase
);
686 D(bug("Listview::Set: don't update scroller as OM_UPDATE is used\n"));
690 if (scroll_entries
&& !refresh_all
)
692 ScrollEntries(g
, data
, old_top
, new_top
, msg
->ops_GInfo
, GadToolsBase
);
695 if (refresh_all
&& msg
->ops_GInfo
)
697 if ((rp
= ObtainGIRPort(msg
->ops_GInfo
)))
699 DoMethod((Object
*)g
, GM_RENDER
, (IPTR
) msg
->ops_GInfo
, (IPTR
) rp
, GREDRAW_REDRAW
);
705 ReturnInt ("Listview::Set", IPTR
, retval
);
708 /**********************************************************************************************/
710 struct Gadget
*GTListView__OM_NEW(Class
*cl
, Object
*o
, struct opSet
*msg
)
712 struct DrawInfo
*dri
;
715 EnterFunc(bug("Listview::New()\n"));
717 dri
= (struct DrawInfo
*)GetTagData(GA_DrawInfo
, (IPTR
) NULL
, msg
->ops_AttrList
);
719 ReturnPtr ("Listview::New", Object
*, NULL
);
721 D(bug("GTListView__OM_NEW: Got dri: %p, dri font=%p, size=%d\n", dri
, dri
->dri_Font
, dri
->dri_Font
->tf_YSize
));
723 g
= (struct Gadget
*)DoSuperMethodA(cl
, o
, (Msg
)msg
);
727 struct LVData
*data
= INST_DATA(cl
, g
);
728 struct TextAttr
*tattr
;
730 struct TagItem fitags
[] =
734 {IA_Resolution
, 0UL },
735 {IA_FrameType
, FRAME_BUTTON
},
736 {IA_EdgesOnly
, TRUE
},
740 /* Create a frame for the listview */
741 data
->ld_Frame
= NewObjectA(NULL
, FRAMEICLASS
, fitags
);
744 CoerceMethod(cl
, (Object
*)g
, OM_DISPOSE
);
752 /* Set some defaults */
753 tattr
= (struct TextAttr
*) GetTagData(GA_TextAttr
, (IPTR
) NULL
, msg
->ops_AttrList
);
756 data
->ld_Font
= OpenFont(tattr
);
759 data
->ld_Flags
|= LVFLG_FONT_OPENED
;
765 data
->ld_Font
= dri
->dri_Font
;
767 data
->ld_ItemHeight
= data
->ld_Font
->tf_YSize
;
768 data
->ld_LabelPlace
= GV_LabelPlace_Above
;
769 data
->ld_Spacing
= LV_DEF_INTERNAL_SPACING
;
771 /* default render hook */
772 data
->ld_CallBack
= &GadToolsBase
->lv_RenderHook
;
774 data
->ld_ShowSelected
= (struct Gadget
*)GetTagData(GTLV_ShowSelected
, (IPTR
)LV_SHOWSELECTED_NONE
, msg
->ops_AttrList
);
776 D(bug("GTListView__OM_NEW: Selected %ld\n", data
->ld_ShowSelected
));
778 listview_set(cl
, g
, msg
);
780 } /* if (frame created) */
782 } /* if (object created) */
784 ReturnPtr ("Listview::New", struct Gadget
*, g
);
787 /**********************************************************************************************/
789 IPTR
GTListView__OM_GET(Class
*cl
, struct Gadget
*g
, struct opGet
*msg
)
794 data
= INST_DATA(cl
, g
);
796 switch (msg
->opg_AttrID
)
799 case GTA_ChildGadgetKind
:
800 *(msg
->opg_Storage
) = LISTVIEW_KIND
;
804 *(msg
->opg_Storage
) = (IPTR
)data
->ld_Top
;
807 case GTLV_Visible
: /* AROS Extension */
808 *(msg
->opg_Storage
) = (IPTR
)NumItemsFit(g
, data
);
811 case GTLV_Total
: /* AROS Extension */
812 *(msg
->opg_Storage
) = (IPTR
)data
->ld_NumEntries
;
816 *(msg
->opg_Storage
) = (IPTR
)data
->ld_Selected
;
820 *(msg
->opg_Storage
) = (IPTR
)data
->ld_Labels
;
824 retval
= DoSuperMethodA(cl
, (Object
*)g
, (Msg
)msg
);
830 /**********************************************************************************************/
832 IPTR
GTListView__OM_DISPOSE(Class
*cl
, Object
*o
, Msg msg
)
834 struct LVData
*data
= INST_DATA(cl
, o
);
836 if (data
->ld_Flags
& LVFLG_FONT_OPENED
)
837 CloseFont(data
->ld_Font
);
839 if (data
->ld_Frame
) DisposeObject(data
->ld_Frame
);
841 return DoSuperMethodA(cl
, o
, msg
);
844 /**********************************************************************************************/
846 IPTR
GTListView__GM_HANDLEINPUT(Class
*cl
, struct Gadget
*g
, struct gpInput
*msg
)
848 struct LVData
*data
= INST_DATA(cl
, g
);
852 EnterFunc(bug("Listview::GoActive()\n"));
854 if (msg
->MethodID
== GM_GOACTIVE
)
856 if ((!msg
->gpi_IEvent
) || /* Not activated by user ? */
857 (data
->ld_Flags
& LVFLG_READONLY
) ||
858 (!data
->ld_Labels
) ||
859 (data
->ld_Labels
== (struct List
*)~0))
861 ReturnInt("Listview::GoActive", IPTR
, GMR_NOREUSE
);
865 /* How many entries are currently shown in the Gtlv ? */
866 shown
= ShownEntries(g
, data
);
868 if ((msg
->gpi_IEvent
->ie_Class
== IECLASS_RAWMOUSE
) ||
869 (msg
->gpi_IEvent
->ie_Class
== IECLASS_TIMER
))
871 if ((msg
->gpi_IEvent
->ie_Class
== IECLASS_TIMER
) ||
872 (msg
->gpi_IEvent
->ie_Code
== SELECTDOWN
) ||
873 (msg
->gpi_IEvent
->ie_Code
== IECODE_NOBUTTON
))
875 /* offset from top of listview of the entry clicked */
876 clickpos
= (msg
->gpi_Mouse
.Y
- LV_BORDER_Y
) /
877 TotalItemHeight(data
);
881 if (data
->ld_Top
> 0)
883 struct TagItem set_tags
[] =
885 {GTLV_Top
, data
->ld_Top
- 1},
889 DoMethod((Object
*)g
, OM_SET
, (IPTR
) set_tags
, (IPTR
) msg
->gpi_GInfo
);
894 } else if (clickpos
>= shown
)
896 WORD max_top
= data
->ld_NumEntries
- NumItemsFit(g
, data
);
898 if (max_top
< 0) max_top
= 0;
900 if (data
->ld_Top
< max_top
)
902 struct TagItem set_tags
[] =
904 {GTLV_Top
, data
->ld_Top
+ 1},
908 DoMethod((Object
*)g
, OM_SET
, (IPTR
) set_tags
, (IPTR
) msg
->gpi_GInfo
);
911 clickpos
= shown
- 1;
915 if ((clickpos
>= 0) && (clickpos
< shown
))
917 if ((clickpos
+ data
->ld_Top
!= data
->ld_Selected
) ||
918 ((data
->ld_ShowSelected
== LV_SHOWSELECTED_NONE
) && (msg
->MethodID
== GM_GOACTIVE
)))
921 WORD oldpos
= data
->ld_Selected
;
923 data
->ld_Selected
= clickpos
+ data
->ld_Top
;
925 rp
= ObtainGIRPort(msg
->gpi_GInfo
);
928 /* Rerender new active */
929 data
->ld_FirstDamaged
= clickpos
;
930 data
->ld_NumDamaged
= 1;
932 data
->ld_Flags
|= LVFLG_FORCE_SELECT_STATE
;
933 DoMethod((Object
*)g
, GM_RENDER
, (IPTR
) msg
->gpi_GInfo
, (IPTR
) rp
, GREDRAW_UPDATE
);
935 /* Rerender old active if it was shown in the listview */
936 if ( (oldpos
>= data
->ld_Top
)
937 && (oldpos
< data
->ld_Top
+ NumItemsFit(g
, data
))
938 && (oldpos
!= data
->ld_Selected
) )
941 data
->ld_FirstDamaged
= oldpos
- data
->ld_Top
;
942 data
->ld_NumDamaged
= 1;
944 DoMethod((Object
*)g
, GM_RENDER
, (IPTR
) msg
->gpi_GInfo
, (IPTR
) rp
, GREDRAW_UPDATE
);
951 DoShowSelected(data
, msg
->gpi_GInfo
, GadToolsBase
);
953 } /* if (click wasn't on old active item) */
955 } /* if (entry is shown) */
957 } /* if mouse down or mouse move event */
958 else if (msg
->gpi_IEvent
->ie_Code
== SELECTUP
)
960 *(msg
->gpi_Termination
) = data
->ld_Selected
;
961 ReturnInt ("ListView::Input", IPTR
, GMR_VERIFY
| GMR_NOREUSE
);
962 } /* mouse up event */
964 } /* if (is mouse event) */
966 ReturnInt ("Listview::Input", IPTR
, GMR_MEACTIVE
);
969 /**********************************************************************************************/
971 IPTR
GTListView__GM_GOINACTIVE(Class
*cl
, struct Gadget
*g
, struct gpGoInactive
*msg
)
973 struct LVData
*data
= INST_DATA(cl
, g
);
976 if ((data
->ld_ShowSelected
== LV_SHOWSELECTED_NONE
) &&
977 (data
->ld_Selected
>= data
->ld_Top
) &&
978 (data
->ld_Selected
< data
->ld_Top
+ NumItemsFit(g
, data
)))
980 if ((rp
= ObtainGIRPort(msg
->gpgi_GInfo
)))
982 data
->ld_FirstDamaged
= data
->ld_Selected
- data
->ld_Top
;
983 data
->ld_NumDamaged
= 1;
985 DoMethod((Object
*)g
, GM_RENDER
, (IPTR
) msg
->gpgi_GInfo
, (IPTR
) rp
, GREDRAW_UPDATE
);
991 ReturnInt ("Listview::GoInactive", IPTR
, 0);
994 /**********************************************************************************************/
996 IPTR
GTListView__GM_RENDER(Class
*cl
, struct Gadget
*g
, struct gpRender
*msg
)
998 struct LVData
*data
= INST_DATA(cl
, g
);
999 BOOL mustrefresh
= FALSE
;
1001 EnterFunc(bug("Listview::Render()\n"));
1003 switch (msg
->gpr_Redraw
)
1005 case GREDRAW_REDRAW
: {
1008 struct TagItem itags
[] =
1015 D(bug("GTListView__GM_RENDER: GREDRAW_REDRAW\n"));
1017 /* Erase the old gadget imagery */
1018 SetAPen(msg
->gpr_RPort
, data
->ld_Dri
->dri_Pens
[BACKGROUNDPEN
]);
1020 RectFill(msg
->gpr_RPort
,
1023 g
->LeftEdge
+ g
->Width
- 1,
1024 g
->TopEdge
+ g
->Height
- 1);
1026 RenderEntries(cl
, g
, msg
, 0, ShownEntries(g
, data
), GadToolsBase
);
1028 /* center image position, we assume image top and left is 0 */
1029 itags
[0].ti_Data
= g
->Width
;
1030 itags
[1].ti_Data
= g
->Height
;
1032 SetAttrsA((Object
*)data
->ld_Frame
, itags
);
1037 DrawImageState(msg
->gpr_RPort
,
1038 (struct Image
*)data
->ld_Frame
,
1040 ((data
->ld_Flags
& LVFLG_READONLY
) ? IDS_SELECTED
: IDS_NORMAL
),
1041 msg
->gpr_GInfo
->gi_DrInfo
);
1043 /* Render gadget label */
1044 renderlabel(GadToolsBase
, g
, msg
->gpr_RPort
, data
->ld_LabelPlace
);
1048 case GREDRAW_UPDATE
:
1050 D(bug("GTListView__GM_RENDER: GREDRAW_UPDATE\n"));
1052 /* Should we scroll the listview ? */
1053 if (data
->ld_ScrollEntries
)
1055 UWORD abs_steps
, visible
;
1058 abs_steps
= abs(data
->ld_ScrollEntries
);
1059 visible
= NumItemsFit(g
, data
);
1061 /* We make the assumption that the listview
1062 ** is always 'full'. If it isn't, the
1063 ** Scroll gadget won't be scrollable, and
1064 ** we won't receive any OM_UPDATEs.
1067 dy
= data
->ld_ScrollEntries
* TotalItemHeight(data
);
1069 D(bug("GTListView__GM_RENDER: Scrolling delta y: %d\n", dy
));
1071 ScrollRaster(msg
->gpr_RPort
, 0, dy
,
1072 g
->LeftEdge
+ LV_BORDER_X
,
1073 g
->TopEdge
+ LV_BORDER_Y
,
1074 g
->LeftEdge
+ g
->Width
- 1 - LV_BORDER_X
,
1075 g
->TopEdge
+ LV_BORDER_Y
+ NumItemsFit(g
, data
) * TotalItemHeight(data
) - 1);
1077 mustrefresh
= (msg
->gpr_GInfo
->gi_Layer
->Flags
& LAYERREFRESH
) != 0;
1079 data
->ld_FirstDamaged
= ((data
->ld_ScrollEntries
> 0) ?
1080 visible
- abs_steps
: 0);
1082 data
->ld_NumDamaged
= abs_steps
;
1084 data
->ld_ScrollEntries
= 0;
1086 } /* If (we should do a scroll) */
1088 D(bug("GTListView__GM_RENDER: Rerendering entries: first damaged=%d, num=%d\n",
1089 data
->ld_FirstDamaged
, data
->ld_NumDamaged
));
1091 /* Redraw all damaged entries */
1092 if (data
->ld_FirstDamaged
!= -1)
1095 RenderEntries(cl
, g
, msg
,
1096 data
->ld_FirstDamaged
,
1097 data
->ld_NumDamaged
,
1100 data
->ld_FirstDamaged
= -1;
1104 /* the LAYERUPDATING check should not be necessary,
1105 as then we should always have a GREDRAW_REDRAW,
1106 while here we are in GREDRAW_UPDATE. But just
1109 if (mustrefresh
&& !(msg
->gpr_GInfo
->gi_Layer
->Flags
& LAYERUPDATING
))
1113 if(!(update
= BeginUpdate(msg
->gpr_GInfo
->gi_Layer
)))
1115 EndUpdate(msg
->gpr_GInfo
->gi_Layer
, FALSE
);
1118 RenderEntries(cl
, g
, msg
, 0, ShownEntries(g
, data
), GadToolsBase
);
1120 if(update
) EndUpdate(msg
->gpr_GInfo
->gi_Layer
, TRUE
);
1123 break; /* GREDRAW_UPDATE */
1125 } /* switch (render mode) */
1127 if (g
->Flags
& GFLG_DISABLED
)
1129 DoDisabledPattern(msg
->gpr_RPort
, g
->LeftEdge
,
1131 g
->LeftEdge
+ g
->Width
- 1,
1132 g
->TopEdge
+ g
->Height
- 1,
1133 msg
->gpr_GInfo
->gi_DrInfo
->dri_Pens
[SHADOWPEN
],
1137 data
->ld_Flags
&= ~LVFLG_FORCE_SELECT_STATE
;
1139 ReturnInt ("Listview::Render", IPTR
, 1UL);
1142 /**********************************************************************************************/
1144 IPTR
GTListView__OM_SET(Class
*cl
, struct Gadget
*g
, struct opSet
*msg
)
1147 if (msg
->MethodID
== OM_UPDATE
)
1149 LONG top
= GetTagData(GTLV_Top
, 148, msg
->ops_AttrList
);
1150 bug("dispatch_listviewclass: OM_UPDATE: top=%d, attrs=%p, gi=%p\n",
1151 top
, msg
->ops_AttrList
, msg
->ops_GInfo
);
1154 D(bug("dispatch_listviewclass: OM_SET\n"));
1156 return DoSuperMethodA(cl
, (Object
*)g
, (Msg
)msg
) + listview_set(cl
, g
, msg
);
1159 /**********************************************************************************************/
1163 static int LV_RenderHook_Init(LIBBASETYPEPTR LIBBASE
)
1165 LIBBASE
->lv_RenderHook
.h_Entry
= (APTR
) AROS_ASMSYMNAME(RenderHook
);
1166 LIBBASE
->lv_RenderHook
.h_SubEntry
= NULL
;
1167 LIBBASE
->lv_RenderHook
.h_Data
= (APTR
)LIBBASE
;
1172 ADD2INITLIB(LV_RenderHook_Init
, 0)
1174 /**********************************************************************************************/