Allow multiple volumes with the same name if their creation dates differ.
[AROS.git] / workbench / libs / gadtools / listviewclass.c
blob1b39a87d0111b7cad82626d16d25e8d9db6cabfc
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 D(bug("RenderEntries: Rendering entry %d: node %s\n", current_entry, node->ln_Name));
242 /* update state */
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"));
247 state |= SELECTED;
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);
262 numentries --;
263 current_entry ++;
264 top += totalitemheight;
266 /* Reset SELECTED bit of state */
267 state &= ~SELECTED;
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 */
280 UWORD numfit;
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)
296 WORD numitemsfit;
297 WORD shown;
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)
312 D(ULONG Result;)
313 struct TagItem scrtags[] =
315 {PGA_Top , 0L },
316 {PGA_Total , 1L },
317 {PGA_Visible , 1L },
318 {TAG_DONE }
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));
333 else
335 D(bug("UpdateScroller: no NumEntries\n"));
337 if (gi)
339 D(bug("UpdateScroller: SetGadgetAttrs\n"));
340 D(Result = )SetGadgetAttrsA((struct Gadget *)data->ld_Scroller, gi->gi_Window, NULL, scrtags);
342 else
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));
349 else
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 */
367 UWORD abs_steps;
368 ULONG redraw_type;
369 struct RastPort *rp;
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
375 ** is to be scrolled
378 if (abs_steps < (NumItemsFit(g, data) >> 1))
380 redraw_type = GREDRAW_UPDATE;
382 else
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);
395 ReleaseGIRPort(rp);
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)"" },
413 {TAG_DONE }
416 if ((data->ld_Selected >= 0) && (data->ld_Selected < data->ld_NumEntries))
418 struct Node *node;
419 WORD i = 0;
421 ForeachNode(data->ld_Labels, node)
423 if (i++ == data->ld_Selected)
425 set_tags[0].ti_Data = (IPTR)node->ln_Name;
426 break;
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)
445 IPTR retval = 0UL;
447 struct TagItem *tag, *tstate;
448 struct LVData *data = INST_DATA(cl, g);
449 struct RastPort *rp;
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;
464 switch (tag->ti_Tag)
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;
470 break;
472 case GTLV_Top: /* [IS] */
473 D(bug("Listview::Set: GTLV_Top\n"));
474 old_top = data->ld_Top;
475 new_top = (WORD)tidata;
477 if (new_top < 0)
479 new_top = 0;
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;
492 retval = 1UL;
494 break;
496 case GTLV_MakeVisible: /* [IS] */
497 D(bug("Listview::Set: GTLV_MakeVisible\n"));
498 old_top = data->ld_Top;
499 new_top = (WORD)tidata;
501 if (new_top < 0)
503 new_top = 0;
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;
529 retval = 1UL;
530 break;
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;
536 data->ld_Top = 0;
539 struct Node *n;
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);
556 retval = 1UL;
557 update_scroller = TRUE;
558 refresh_all = TRUE;
559 break;
561 case GTLV_ReadOnly: /* [I] */
562 D(bug("Listview::Set: GTLV_ReadOnly\n"));
563 if (tidata)
564 data->ld_Flags |= LVFLG_READONLY;
565 else
566 data->ld_Flags &= ~LVFLG_READONLY;
568 D(bug("Readonly: tidata=%d, flags=%d\n",
569 tidata, data->ld_Flags));
570 break;
572 case GTLV_Selected: /* [IS] */
574 struct RastPort *rp;
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);
610 ReleaseGIRPort(rp);
612 } /* if ((rp = ObtainGIRPort(msg->ops_GInfo))) */
613 else
615 D(bug("Listview::Set: no rastport\n"));
617 DoShowSelected(data, msg->ops_GInfo, GadToolsBase);
619 } /* if (old_selected != data->ld_Selected) */
622 retval = 1UL;
623 break;
625 case LAYOUTA_Spacing: /* [I] */
626 D(bug("Listview::Set: LAYOUTA_Spacing\n"));
627 data->ld_Spacing = (UWORD)tidata;
628 break;
630 case GTLV_ItemHeight: /* [I] */
631 D(bug("Listview::Set: GTLV_ItemHeight\n"));
632 data->ld_ItemHeight = (UWORD)tidata;
633 break;
635 case GTLV_CallBack: /* [I] */
636 D(bug("Listview::Set: GTLV_CallBack\n"));
637 data->ld_CallBack = (struct Hook *)tidata;
638 break;
640 case GA_LabelPlace: /* [I] */
641 D(bug("Listview::Set: GTLV_LabelPlace\n"));
642 data->ld_LabelPlace = (LONG)tidata;
643 break;
645 case GA_Disabled:
647 struct TagItem set_tags[] =
649 {GA_Disabled , tag->ti_Data },
650 {TAG_DONE }
653 D(bug("Listview::Set: GA_Disabled\n"));
654 if (data->ld_Scroller)
656 if (msg->ops_GInfo)
658 SetGadgetAttrsA((struct Gadget *)data->ld_Scroller, msg->ops_GInfo->gi_Window, 0, set_tags);
659 } else {
660 SetAttrsA(data->ld_Scroller, set_tags);
664 refresh_all = TRUE;
665 break;
667 } /* switch (tag->ti_Tag) */
669 } /* while (more tags to iterate) */
671 if (update_scroller)
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);
682 else
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);
699 ReleaseGIRPort(rp);
703 ReturnInt ("Listview::Set", IPTR, retval);
706 /**********************************************************************************************/
708 struct Gadget *GTListView__OM_NEW(Class *cl, Object *o, struct opSet *msg)
710 struct DrawInfo *dri;
711 struct Gadget *g;
713 EnterFunc(bug("Listview::New()\n"));
715 dri = (struct DrawInfo *)GetTagData(GA_DrawInfo, (IPTR) NULL, msg->ops_AttrList);
716 if (dri == NULL)
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);
723 if (g != NULL)
725 struct LVData *data = INST_DATA(cl, g);
726 struct TextAttr *tattr;
728 struct TagItem fitags[] =
730 {IA_Width , 0UL },
731 {IA_Height , 0UL },
732 {IA_Resolution , 0UL },
733 {IA_FrameType , FRAME_BUTTON },
734 {IA_EdgesOnly , TRUE },
735 {TAG_DONE }
738 /* Create a frame for the listview */
739 data->ld_Frame = NewObjectA(NULL, FRAMEICLASS, fitags);
740 if (!data->ld_Frame)
742 CoerceMethod(cl, (Object *)g, OM_DISPOSE);
743 g = NULL;
745 else
748 data->ld_Dri = dri;
750 /* Set some defaults */
751 tattr = (struct TextAttr *) GetTagData(GA_TextAttr, (IPTR) NULL, msg->ops_AttrList);
752 if (tattr)
754 data->ld_Font = OpenFont(tattr);
755 if (data->ld_Font)
757 data->ld_Flags |= LVFLG_FONT_OPENED;
762 if (!data->ld_Font)
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)
789 IPTR retval = 1UL;
790 struct LVData *data;
792 data = INST_DATA(cl, g);
794 switch (msg->opg_AttrID)
796 case GTA_GadgetKind:
797 case GTA_ChildGadgetKind:
798 *(msg->opg_Storage) = LISTVIEW_KIND;
799 break;
801 case GTLV_Top:
802 *(msg->opg_Storage) = (IPTR)data->ld_Top;
803 break;
805 case GTLV_Visible: /* AROS Extension */
806 *(msg->opg_Storage) = (IPTR)NumItemsFit(g, data);
807 break;
809 case GTLV_Total: /* AROS Extension */
810 *(msg->opg_Storage) = (IPTR)data->ld_NumEntries;
811 break;
813 case GTLV_Selected:
814 *(msg->opg_Storage) = (IPTR)data->ld_Selected;
815 break;
817 case GTLV_Labels:
818 *(msg->opg_Storage) = (IPTR)data->ld_Labels;
819 break;
821 default:
822 retval = DoSuperMethodA(cl, (Object *)g, (Msg)msg);
823 break;
825 return (retval);
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);
847 WORD clickpos;
848 BOOL shown;
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);
877 if (clickpos < 0)
879 if (data->ld_Top > 0)
881 struct TagItem set_tags[] =
883 {GTLV_Top, data->ld_Top - 1},
884 {TAG_DONE }
887 DoMethod((Object *)g, OM_SET, (IPTR) set_tags, (IPTR) msg->gpi_GInfo);
890 clickpos = 0;
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},
903 {TAG_DONE }
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)))
918 struct RastPort *rp;
919 WORD oldpos = data->ld_Selected;
921 data->ld_Selected = clickpos + data->ld_Top;
923 rp = ObtainGIRPort(msg->gpi_GInfo);
924 if (rp)
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);
945 ReleaseGIRPort(rp);
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);
972 struct RastPort *rp;
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);
985 ReleaseGIRPort(rp);
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: {
1005 WORD x, y;
1006 struct TagItem itags[] =
1008 {IA_Width , 0L },
1009 {IA_Height , 0L },
1010 {TAG_DONE }
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,
1019 g->LeftEdge,
1020 g->TopEdge,
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);
1032 x = g->LeftEdge;
1033 y = g->TopEdge;
1035 DrawImageState(msg->gpr_RPort,
1036 (struct Image *)data->ld_Frame,
1037 x, y,
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);
1044 } break;
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;
1054 LONG dy;
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,
1096 GadToolsBase);
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
1105 to be sure ... */
1107 if (mustrefresh && !(msg->gpr_GInfo->gi_Layer->Flags & LAYERUPDATING))
1109 BOOL update;
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,
1128 g->TopEdge,
1129 g->LeftEdge + g->Width - 1,
1130 g->TopEdge + g->Height - 1,
1131 msg->gpr_GInfo->gi_DrInfo->dri_Pens[SHADOWPEN],
1132 GadToolsBase);
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 /**********************************************************************************************/
1159 #undef GadToolsBase
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;
1167 return TRUE;
1170 ADD2INITLIB(LV_RenderHook_Init, 0)
1172 /**********************************************************************************************/