add casts to zune macros to silence some warnings
[tangerine.git] / workbench / libs / gadtools / listviewclass.c
blobfa1d76d18cff2183cb2160443acbed039b3a8a90
1 /*
2 Copyright © 1995-2006, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Internal GadTools listview class.
6 Lang: English
7 */
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() */
30 #include <stdlib.h>
32 #include <aros/symbolsets.h>
34 #define SDEBUG 0
35 #define DEBUG 0
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 /**********************************************************************************************/
49 /* Flags */
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.
61 #undef SELECTED
62 #define NORMAL 0
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 */
81 #undef GadToolsBase
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)
92 AROS_USERFUNC_INIT
94 IPTR retval;
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;
114 SetDrMd(rp, JAM1);
117 switch (msg->lvdm_State)
119 case LVR_SELECTED:
120 case LVR_SELECTEDDISABLED:
121 /* We must fill the backgound with FILLPEN */
122 D(bug("RenderHook: LVR_SELECTED(DISABLED)\n"));
123 erasepen = FILLPEN;
125 /* Fall through */
127 case LVR_NORMAL:
128 case LVR_NORMALDISABLED: {
130 WORD numfit;
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]);
141 /* Render text */
142 Move(rp, min_x, min_y + rp->Font->tf_Baseline);
143 Text(rp, node->ln_Name, numfit);
146 } break;
148 default:
149 kprintf("!! LISTVIEW DRAWING STATE NOT SUPPORTED !!\n");
150 break;
154 retval = LVCB_OK;
156 else
158 retval = LVCB_UNKNOWN;
161 ReturnInt ("RenderHook", IPTR, retval);
163 AROS_USERFUNC_EXIT
166 /**********************************************************************************************/
168 #undef GadToolsBase
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);
179 struct Node *node;
180 UWORD entry_count, totalitemheight;
181 WORD left, top, width;
182 struct LVDrawMsg drawmsg;
183 struct TextFont *oldfont;
185 UBYTE state = 0;
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)
217 state |= READONLY;
219 if (g->Flags & GFLG_DISABLED)
220 state |= 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)
226 entry_count --;
227 current_entry ++;
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 ULONG retval;
242 D(bug("RenderEntries: Rendering entry %d: node %s\n", current_entry, node->ln_Name));
244 /* update state */
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"));
249 state |= SELECTED;
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);
264 numentries --;
265 current_entry ++;
266 top += totalitemheight;
268 /* Reset SELECTED bit of state */
269 state &= ~SELECTED;
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 */
282 UWORD numfit;
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)
298 WORD numitemsfit;
299 WORD shown;
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)
314 ULONG Result;
315 struct TagItem scrtags[] =
317 {PGA_Top , 0L },
318 {PGA_Total , 1L },
319 {PGA_Visible , 1L },
320 {TAG_DONE }
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));
335 else
337 D(bug("UpdateScroller: no NumEntries\n"));
339 if (gi)
341 D(bug("UpdateScroller: SetGadgetAttrs\n"));
342 Result = SetGadgetAttrsA((struct Gadget *)data->ld_Scroller, gi->gi_Window, NULL, scrtags);
344 else
346 D(bug("UpdateScroller: SetAttrs (no gadgetinfo)\n"));
347 Result = SetAttrsA(data->ld_Scroller, scrtags);
349 D(bug("UpdateScroller: Result 0x%lx\n",Result));
351 else
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 */
369 UWORD abs_steps;
370 ULONG redraw_type;
371 struct RastPort *rp;
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
377 ** is to be scrolled
380 if (abs_steps < (NumItemsFit(g, data) >> 1))
382 redraw_type = GREDRAW_UPDATE;
384 else
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);
397 ReleaseGIRPort(rp);
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)"" },
415 {TAG_DONE }
418 if ((data->ld_Selected >= 0) && (data->ld_Selected < data->ld_NumEntries))
420 struct Node *node;
421 WORD i = 0;
423 ForeachNode(data->ld_Labels, node)
425 if (i++ == data->ld_Selected)
427 set_tags[0].ti_Data = (IPTR)node->ln_Name;
428 break;
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)
447 IPTR retval = 0UL;
449 struct TagItem *tag, *tstate;
450 struct LVData *data = INST_DATA(cl, g);
451 struct RastPort *rp;
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;
466 switch (tag->ti_Tag)
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;
472 break;
474 case GTLV_Top: /* [IS] */
475 D(bug("Listview::Set: GTLV_Top\n"));
476 old_top = data->ld_Top;
477 new_top = (WORD)tidata;
479 if (new_top < 0)
481 new_top = 0;
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;
494 retval = 1UL;
496 break;
498 case GTLV_MakeVisible: /* [IS] */
499 D(bug("Listview::Set: GTLV_MakeVisible\n"));
500 old_top = data->ld_Top;
501 new_top = (WORD)tidata;
503 if (new_top < 0)
505 new_top = 0;
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;
531 retval = 1UL;
532 break;
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;
538 data->ld_Top = 0;
541 struct Node *n;
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);
558 retval = 1UL;
559 update_scroller = TRUE;
560 refresh_all = TRUE;
561 break;
563 case GTLV_ReadOnly: /* [I] */
564 D(bug("Listview::Set: GTLV_ReadOnly\n"));
565 if (tidata)
566 data->ld_Flags |= LVFLG_READONLY;
567 else
568 data->ld_Flags &= ~LVFLG_READONLY;
570 D(bug("Readonly: tidata=%d, flags=%d\n",
571 tidata, data->ld_Flags));
572 break;
574 case GTLV_Selected: /* [IS] */
576 struct RastPort *rp;
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);
612 ReleaseGIRPort(rp);
614 } /* if ((rp = ObtainGIRPort(msg->ops_GInfo))) */
615 else
617 D(bug("Listview::Set: no rastport\n"));
619 DoShowSelected(data, msg->ops_GInfo, GadToolsBase);
621 } /* if (old_selected != data->ld_Selected) */
624 retval = 1UL;
625 break;
627 case LAYOUTA_Spacing: /* [I] */
628 D(bug("Listview::Set: LAYOUTA_Spacing\n"));
629 data->ld_Spacing = (UWORD)tidata;
630 break;
632 case GTLV_ItemHeight: /* [I] */
633 D(bug("Listview::Set: GTLV_ItemHeight\n"));
634 data->ld_ItemHeight = (UWORD)tidata;
635 break;
637 case GTLV_CallBack: /* [I] */
638 D(bug("Listview::Set: GTLV_CallBack\n"));
639 data->ld_CallBack = (struct Hook *)tidata;
640 break;
642 case GA_LabelPlace: /* [I] */
643 D(bug("Listview::Set: GTLV_LabelPlace\n"));
644 data->ld_LabelPlace = (LONG)tidata;
645 break;
647 case GA_Disabled:
649 struct TagItem set_tags[] =
651 {GA_Disabled , tag->ti_Data },
652 {TAG_DONE }
655 D(bug("Listview::Set: GA_Disabled\n"));
656 if (data->ld_Scroller)
658 if (msg->ops_GInfo)
660 SetGadgetAttrsA((struct Gadget *)data->ld_Scroller, msg->ops_GInfo->gi_Window, 0, set_tags);
661 } else {
662 SetAttrsA(data->ld_Scroller, set_tags);
666 refresh_all = TRUE;
667 break;
669 } /* switch (tag->ti_Tag) */
671 } /* while (more tags to iterate) */
673 if (update_scroller)
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);
684 else
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);
701 ReleaseGIRPort(rp);
705 ReturnInt ("Listview::Set", IPTR, retval);
708 /**********************************************************************************************/
710 struct Gadget *GTListView__OM_NEW(Class *cl, Object *o, struct opSet *msg)
712 struct DrawInfo *dri;
713 struct Gadget *g;
715 EnterFunc(bug("Listview::New()\n"));
717 dri = (struct DrawInfo *)GetTagData(GA_DrawInfo, (IPTR) NULL, msg->ops_AttrList);
718 if (dri == NULL)
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);
725 if (g != NULL)
727 struct LVData *data = INST_DATA(cl, g);
728 struct TextAttr *tattr;
730 struct TagItem fitags[] =
732 {IA_Width , 0UL },
733 {IA_Height , 0UL },
734 {IA_Resolution , 0UL },
735 {IA_FrameType , FRAME_BUTTON },
736 {IA_EdgesOnly , TRUE },
737 {TAG_DONE }
740 /* Create a frame for the listview */
741 data->ld_Frame = NewObjectA(NULL, FRAMEICLASS, fitags);
742 if (!data->ld_Frame)
744 CoerceMethod(cl, (Object *)g, OM_DISPOSE);
745 g = NULL;
747 else
750 data->ld_Dri = dri;
752 /* Set some defaults */
753 tattr = (struct TextAttr *) GetTagData(GA_TextAttr, (IPTR) NULL, msg->ops_AttrList);
754 if (tattr)
756 data->ld_Font = OpenFont(tattr);
757 if (data->ld_Font)
759 data->ld_Flags |= LVFLG_FONT_OPENED;
764 if (!data->ld_Font)
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)
791 IPTR retval = 1UL;
792 struct LVData *data;
794 data = INST_DATA(cl, g);
796 switch (msg->opg_AttrID)
798 case GTA_GadgetKind:
799 case GTA_ChildGadgetKind:
800 *(msg->opg_Storage) = LISTVIEW_KIND;
801 break;
803 case GTLV_Top:
804 *(msg->opg_Storage) = (IPTR)data->ld_Top;
805 break;
807 case GTLV_Visible: /* AROS Extension */
808 *(msg->opg_Storage) = (IPTR)NumItemsFit(g, data);
809 break;
811 case GTLV_Total: /* AROS Extension */
812 *(msg->opg_Storage) = (IPTR)data->ld_NumEntries;
813 break;
815 case GTLV_Selected:
816 *(msg->opg_Storage) = (IPTR)data->ld_Selected;
817 break;
819 case GTLV_Labels:
820 *(msg->opg_Storage) = (IPTR)data->ld_Labels;
821 break;
823 default:
824 retval = DoSuperMethodA(cl, (Object *)g, (Msg)msg);
825 break;
827 return (retval);
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);
849 WORD clickpos;
850 BOOL shown;
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);
879 if (clickpos < 0)
881 if (data->ld_Top > 0)
883 struct TagItem set_tags[] =
885 {GTLV_Top, data->ld_Top - 1},
886 {TAG_DONE }
889 DoMethod((Object *)g, OM_SET, (IPTR) set_tags, (IPTR) msg->gpi_GInfo);
892 clickpos = 0;
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},
905 {TAG_DONE }
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)))
920 struct RastPort *rp;
921 WORD oldpos = data->ld_Selected;
923 data->ld_Selected = clickpos + data->ld_Top;
925 rp = ObtainGIRPort(msg->gpi_GInfo);
926 if (rp)
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);
947 ReleaseGIRPort(rp);
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);
974 struct RastPort *rp;
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);
987 ReleaseGIRPort(rp);
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: {
1007 WORD x, y;
1008 struct TagItem itags[] =
1010 {IA_Width , 0L },
1011 {IA_Height , 0L },
1012 {TAG_DONE }
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,
1021 g->LeftEdge,
1022 g->TopEdge,
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);
1034 x = g->LeftEdge;
1035 y = g->TopEdge;
1037 DrawImageState(msg->gpr_RPort,
1038 (struct Image *)data->ld_Frame,
1039 x, y,
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);
1046 } break;
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;
1056 LONG dy;
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,
1098 GadToolsBase);
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
1107 to be sure ... */
1109 if (mustrefresh && !(msg->gpr_GInfo->gi_Layer->Flags & LAYERUPDATING))
1111 BOOL update;
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,
1130 g->TopEdge,
1131 g->LeftEdge + g->Width - 1,
1132 g->TopEdge + g->Height - 1,
1133 msg->gpr_GInfo->gi_DrInfo->dri_Pens[SHADOWPEN],
1134 GadToolsBase);
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 /**********************************************************************************************/
1161 #undef GadToolsBase
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;
1169 return TRUE;
1172 ADD2INITLIB(LV_RenderHook_Init, 0)
1174 /**********************************************************************************************/