2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
5 AROS specific listview class implementation.
8 #include <proto/alib.h>
9 #include <proto/exec.h>
10 #include <proto/intuition.h>
11 #include <proto/graphics.h>
12 #include <proto/utility.h>
14 #include <exec/memory.h>
15 #include <intuition/intuition.h>
16 #include <intuition/classes.h>
17 #include <intuition/classusr.h>
18 #include <intuition/cghooks.h>
19 #include <intuition/gadgetclass.h>
20 #include <intuition/icclass.h>
21 #include <graphics/gfxbase.h>
22 #include <aros/asmcall.h>
24 #include <gadgets/aroslistview.h>
25 #include <gadgets/aroslist.h>
27 #include "aroslistview_intern.h"
34 #include <aros/debug.h>
37 /*****************************************************************************/
41 #define SETFLAG(flagvar, boolvar, flag) \
48 STATIC IPTR
_OM_SET(Class
*cl
, Object
*o
,struct opSet
*msg
)
50 IPTR retval
= (IPTR
)0;
52 struct TagItem
*tag
, *tstate
;
55 EnterFunc(bug("ListView::OM_SET\n"));
57 data
= INST_DATA(cl
, o
);
58 tstate
= msg
->ops_AttrList
;
60 /* Set to 1 to signal visual changes */
61 while ((tag
= NextTagItem(&tstate
)) != NULL
)
65 case AROSA_Listview_DisplayHook
:
66 data
->lvd_DisplayHook
= (struct Hook
*)tag
->ti_Data
;
70 case AROSA_Listview_FrontPen
:
71 data
->lvd_FrontPen
= (UBYTE
)tag
->ti_Data
;
75 case AROSA_Listview_BackPen
:
76 data
->lvd_BackPen
= (UBYTE
)tag
->ti_Data
;
81 case AROSA_Listview_Visible
:
82 case AROSA_Listview_Total
:
84 struct TagItem tags
[] =
86 {tag
->ti_Tag
, tag
->ti_Data
},
90 NotifyAttrs(cl
, o
, msg
, tags
);
93 case AROSA_Listview_List
:
98 struct TagItem tags
[] =
100 {AROSA_Listview_First
, 0},
101 {AROSA_Listview_Total
, 0},
105 data
->lvd_List
= (Object
*)tag
->ti_Data
;
107 GetAttr(AROSA_List_Entries
, data
->lvd_List
, (IPTR
*)&numentries
);
108 SetAttrs(data
->lvd_List
,
109 AROSA_List_Active
, AROSV_List_Active_None
,
112 tags
[1].ti_Data
= numentries
;
113 DoMethod(o
, OM_SET
, (IPTR
) tags
, (IPTR
) msg
->ops_GInfo
);
119 case AROSA_Listview_HorSpacing
:
120 data
->lvd_HorSpacing
= (UBYTE
)tag
->ti_Data
;
124 case AROSA_Listview_VertSpacing
:
125 data
->lvd_VertSpacing
= (UBYTE
)tag
->ti_Data
;
127 ReCalcEntryHeight(data
);
131 case AROSA_Listview_Format
:
133 struct ColumnAttrs
*colattrs
= data
->lvd_ColAttrs
;
134 ULONG colattrsz
= UB(&colattrs
[data
->lvd_MaxColumns
]) - UB(&colattrs
[0]);
136 memset(colattrs
, 0, colattrsz
);
137 ParseFormatString((STRPTR
)tag
->ti_Data
, data
);
141 case AROSA_Listview_First
:
143 struct TagItem tags
[] =
145 {AROSA_Listview_First
, tag
->ti_Data
},
149 LONG old
= data
->lvd_First
;
152 if (msg
->MethodID
== OM_SET
)
154 D(bug("_First OM_SET\n"));
157 D(bug("_First OM_UPDATEd, lvd_NC=%d\n",
158 data
->lvd_NotifyCount
));
163 data
->lvd_First
= (LONG
)tag
->ti_Data
;
165 if ( ( msg
->MethodID
== OM_UPDATE
)
166 && ( old
!= data
->lvd_First
))
170 UWORD visible
, abs_steps
;
173 steps
= tag
->ti_Data
- old
;
174 abs_steps
= steps
< 0 ? -steps
: steps
;
176 GetGadgetIBox(o
, msg
->ops_GInfo
, &box
);
177 visible
= NumVisible(&box
, data
->lvd_EntryHeight
);
179 if (abs_steps
< visible
>> 1)
181 if ((rp
= ObtainGIRPort(msg
->ops_GInfo
)) != NULL
)
184 /* We make the assumption that the listview
185 ** is alvays 'full'. If it isn't, the
186 ** Scroll gadget won't be scrollable, and
187 ** we won't receive any OM_UPDATEs.
190 dy
= steps
* data
->lvd_EntryHeight
;
193 ScrollRaster(rp
, 0, dy
,
194 box
.Left
+ LV_BORDERWIDTH_X
,
195 box
.Top
+ LV_BORDERWIDTH_Y
,
196 box
.Left
+ box
.Width
- 1 - LV_BORDERWIDTH_X
,
197 box
.Top
+ LV_BORDERWIDTH_Y
+ visible
* data
->lvd_EntryHeight
);
199 data
->lvd_DamageOffset
= ((steps
> 0) ?
200 visible
- abs_steps
: 0);
202 data
->lvd_NumDamaged
= abs_steps
;
204 DoMethod(o
, GM_RENDER
, (IPTR
) msg
->ops_GInfo
, (IPTR
) rp
, GREDRAW_UPDATE
);
210 } /* if (ObtainGIRPort succeed) */
212 } /* if (at most half the visible entries scrolled out) */
214 } /* if (msg is of type OM_UPDATE) */
218 NotifyAttrs(cl
, o
, msg
, tags
);
222 case AROSA_Listview_RenderHook
:
223 data
->lvd_RenderHook
= (struct Hook
*)data
->lvd_RenderHook
;
227 case AROSA_Listview_MultiSelect
:
228 SETFLAG(data
->lvd_Flags
, tag
->ti_Data
, LVFLG_MULTISELECT
);
235 tf
= OpenFont((struct TextAttr
*)tag
->ti_Data
);
240 CloseFont(data
->lvd_Font
);
244 ReCalcEntryHeight(data
);
252 } /* switch (tag->ti_Tag) */
254 } /* while (more tags to iterate) */
256 ReturnPtr("ListView::OM_SET", IPTR
, retval
);
259 /**********************
260 ** Listview::Set() **
261 **********************/
263 IPTR
AROSListview__OM_SET(Class
*cl
, Object
*o
,struct opSet
*msg
)
265 IPTR retval
= DoSuperMethodA(cl
, o
, (Msg
)msg
);
267 retval
+= (IPTR
)_OM_SET(cl
, o
, (struct opSet
*)msg
);
271 /*************************
272 ** Listview::Update() **
273 *************************/
275 IPTR
AROSListview__OM_UPDATE(Class
*cl
, Object
*o
,struct opSet
*msg
)
277 IPTR retval
= DoSuperMethodA(cl
, o
, (Msg
)msg
);
278 struct LVData
*data
= INST_DATA(cl
, o
);
280 retval
+= (IPTR
)_OM_SET(cl
, o
, (struct opSet
*)msg
);
282 /* If we have been subclassed, OM_UPDATE should not cause a GM_RENDER
283 * because it would circumvent the subclass from fully overriding it.
284 * The check of cl == OCLASS(o) should fail if we have been
285 * subclassed, and we have gotten here via DoSuperMethodA().
289 && (data
->lvd_NotifyCount
)
292 struct GadgetInfo
*gi
= ((struct opSet
*)msg
)->ops_GInfo
;
295 struct RastPort
*rp
= ObtainGIRPort(gi
);
300 GetGadgetIBox(o
, gi
, &ibox
);
301 data
->lvd_DamageOffset
= 0;
302 data
->lvd_NumDamaged
= NumVisible(&ibox
, data
->lvd_EntryHeight
);
305 D(bug("Major rerender: o=%d, n=%d\n",
306 data
->lvd_DamageOffset
, data
->lvd_NumDamaged
)
309 DoMethod(o
, GM_RENDER
, (IPTR
) gi
, (IPTR
) rp
, GREDRAW_UPDATE
);
318 /**********************
319 ** Listview::New() **
320 **********************/
323 IPTR
AROSListview__OM_NEW(Class
*cl
, Object
*o
, struct opSet
*msg
)
326 struct ColumnAttrs
*colattrs
;
330 struct opSet ops
, *p_ops
= &ops
;
331 struct TagItem tags
[] =
333 {GA_RelSpecial
, TRUE
},
337 ops
.MethodID
= OM_NEW
;
338 ops
.ops_AttrList
= &tags
[0];
339 ops
.ops_GInfo
= NULL
;
341 tags
[1].ti_Data
= (IPTR
)msg
->ops_AttrList
;
343 o
= (Object
*)DoSuperMethodA(cl
, o
, (Msg
)p_ops
);
347 D(bug("lv: obj created\n"));
348 data
= INST_DATA(cl
, o
);
349 memset(data
, 0, sizeof (struct LVData
));
351 data
->lvd_MaxColumns
= GetTagData(AROSA_Listview_MaxColumns
, 0, msg
->ops_AttrList
);
352 if (!data
->lvd_MaxColumns
)
354 D(bug("Maxcolumns found: %d\n", data
->lvd_MaxColumns
));
356 /* Allocate mem for storing info parsed from Listview_Format. Do this
357 * before listview_set() call, because it needs this for parsing the
360 colattrsz
= data
->lvd_MaxColumns
* sizeof (struct ColumnAttrs
);
361 colattrs
= AllocVec(colattrsz
, MEMF_ANY
|MEMF_CLEAR
);
364 data
->lvd_ColAttrs
= colattrs
;
365 D(bug("Colattrs allocated\n"));
367 /* Only view first column */
368 data
->lvd_ViewedColumns
= 1;
369 colattrs
[0].ca_DHIndex
= 0;
371 /* Alloc mem for array to pass to _Listview_DisplayHook */
372 dharray
= AllocVec(data
->lvd_MaxColumns
* sizeof (STRPTR
), MEMF_ANY
);
375 data
->lvd_DHArray
= dharray
;
376 D(bug("disphookarray allocated\n"));
378 /* Set some defaults */
379 data
->lvd_HorSpacing
= LV_DEFAULTHORSPACING
;
380 data
->lvd_VertSpacing
= LV_DEFAULTVERTSPACING
;
382 data
->lvd_FrontPen
= TEXTPEN
;
383 data
->lvd_BackPen
= BACKGROUNDPEN
;
385 /* Handle our special tags - overrides defaults */
388 /* If not font has been set, use our own. */
391 struct TextAttr tattr
;
392 struct TextFont
*tf
= GfxBase
->DefaultFont
;
394 memset(&tattr
, 0, sizeof (struct TextAttr
));
395 tattr
.ta_Name
= tf
->tf_Message
.mn_Node
.ln_Name
;
396 tattr
.ta_YSize
= tf
->tf_YSize
;
397 tattr
.ta_Style
= tf
->tf_Style
;
398 tattr
.ta_Flags
= tf
->tf_Flags
;
400 if ((data
->lvd_Font
= OpenFont(&tattr
)) == NULL
)
403 ReCalcEntryHeight(data
);
410 CoerceMethod(cl
, o
, OM_DISPOSE
);
414 /**********************
415 ** Listview::Get() **
416 **********************/
417 IPTR
AROSListview__OM_GET(Class
*cl
, Object
*o
, struct opGet
*msg
)
422 data
= INST_DATA(cl
, o
);
424 switch (msg
->opg_AttrID
)
426 case AROSA_Listview_HorSpacing
:
427 *(msg
->opg_Storage
) = (IPTR
)data
->lvd_HorSpacing
;
430 case AROSA_Listview_VertSpacing
:
431 *(msg
->opg_Storage
) = (IPTR
)data
->lvd_VertSpacing
;
434 case AROSA_Listview_List
:
435 *(msg
->opg_Storage
) = (IPTR
)data
->lvd_List
;
438 case AROSA_Listview_DoubleClick
:
439 *(msg
->opg_Storage
) = (IPTR
)(data
->lvd_Flags
& LVFLG_DOUBLECLICK
) ?
444 retval
= DoSuperMethodA(cl
, o
, (Msg
)msg
);
451 /**************************
452 ** Listview::Dispose() **
453 **************************/
454 VOID
AROSListview__OM_DISPOSE(Class
*cl
, Object
*o
, Msg msg
)
458 data
= INST_DATA(cl
, o
);
460 if (data
->lvd_DHArray
)
461 FreeVec(data
->lvd_DHArray
);
463 if (data
->lvd_ColAttrs
)
464 FreeVec(data
->lvd_ColAttrs
);
467 CloseFont(data
->lvd_Font
);
472 /**************************
473 ** Listview::HitTest() **
474 **************************/
475 IPTR
AROSListview__GM_HITTEST(Class
*cl
, Object
*o
, struct gpHitTest
*msg
)
478 struct LVData
*data
= INST_DATA(cl
, o
);
480 retval
= DoSuperMethodA(cl
, o
, (Msg
)msg
);
481 if (retval
== GMR_GADGETHIT
)
483 /* If the listview is readonly, we should never reach GM_GOACTIVE */
484 if (data
->lvd_Flags
& LVFLG_READONLY
)
493 /***************************
494 ** Listview::GoActive() **
495 ***************************/
498 IPTR
AROSListview__GM_GOACTIVE(Class
*cl
, Object
*o
, struct gpInput
*msg
)
500 IPTR retval
= GMR_NOREUSE
;
502 struct LVData
*data
= INST_DATA(cl
, o
);
503 struct IBox container
;
506 /* pos of the selected inside the listview. Eg the first viewed has clickpos 0 */
511 WORD updateoldactive
= -1;
513 BOOL rerender
= FALSE
;
514 BOOL singleclick
= FALSE
, doubleclick
= FALSE
;
517 if (data
->lvd_Flags
& LVFLG_READONLY
)
520 if (!msg
->gpi_IEvent
)
523 GetGadgetIBox(o
, msg
->gpi_GInfo
, &container
);
525 GetAttr(AROSA_List_Entries
, data
->lvd_List
, (IPTR
*)&numentries
);
527 /* How many entries are currently shown in the listview ? */
528 shown
= ShownEntries(data
, &container
);
530 /* offset from top of listview of the entry clicked */
531 clickpos
= (msg
->gpi_Mouse
.Y
- LV_BORDERWIDTH_Y
)
532 / data
->lvd_EntryHeight
;
534 data
->lvd_Flags
&= ~LVFLG_DOUBLECLICK
;
536 if (clickpos
< shown
)
538 GetAttr(AROSA_List_Active
, data
->lvd_List
, (IPTR
*)&active
);
540 /* Check for a doubleclick */
541 activepos
= active
- data
->lvd_First
;
542 if (activepos
== clickpos
)
546 if (DoubleClick(data
->lvd_StartSecs
,
547 data
->lvd_StartMicros
,
548 msg
->gpi_IEvent
->ie_TimeStamp
.tv_secs
,
549 msg
->gpi_IEvent
->ie_TimeStamp
.tv_micro
))
551 data
->lvd_Flags
|= LVFLG_DOUBLECLICK
;
553 D(bug("\tlv: doubleclick at pos %d\n", clickpos
));
560 data
->lvd_StartSecs
= msg
->gpi_IEvent
->ie_TimeStamp
.tv_secs
;
561 data
->lvd_StartMicros
= msg
->gpi_IEvent
->ie_TimeStamp
.tv_micro
;
565 if (data
->lvd_Flags
& LVFLG_MULTISELECT
)
570 AROSM_List_Select
, data
->lvd_First
+ clickpos
,
571 AROSV_List_Select_Toggle
, (IPTR
) NULL
574 data
->lvd_DamageOffset
= clickpos
;
575 data
->lvd_NumDamaged
= 1;
581 if (activepos
!= clickpos
)
584 /* Active entry inside lv ? */
585 if ( (active
>= data
->lvd_First
)
586 && (active
< (data
->lvd_First
+ shown
)))
590 updateoldactive
= activepos
;
593 data
->lvd_DamageOffset
= clickpos
;
594 data
->lvd_NumDamaged
= 1;
597 } /* if (not user reclicked on active entry) */
599 } /* if (lv is simple or multiselect) */
601 /* Render the selected-imagery of the new active item */
602 active
= data
->lvd_First
+ clickpos
;
603 SetAttrs(data
->lvd_List
,
604 AROSA_List_Active
, active
,
607 *(msg
->gpi_Termination
) = IDCMP_GADGETUP
;
608 D(bug("\t GMR_VERIFY retval set\n"));
609 retval
= GMR_NOREUSE
|GMR_VERIFY
;
615 rp
= ObtainGIRPort(msg
->gpi_GInfo
);
618 DoMethod(o
, GM_RENDER
, (IPTR
) msg
->gpi_GInfo
, (IPTR
) rp
, GREDRAW_UPDATE
);
619 if (updateoldactive
!= -1)
621 data
->lvd_DamageOffset
= updateoldactive
;
622 data
->lvd_NumDamaged
= 1;
623 DoMethod(o
, GM_RENDER
, (IPTR
) msg
->gpi_GInfo
, (IPTR
) rp
, GREDRAW_UPDATE
);
630 /* Tell subclasses that a singleclick occured */
634 AROSM_Listview_SingleClick
,
635 (IPTR
) msg
->gpi_GInfo
,
636 clickpos
+ data
->lvd_First
);
640 /* Tell subclasses that a doubleclick occured */
644 AROSM_Listview_DoubleClick
,
645 (IPTR
) msg
->gpi_GInfo
,
646 clickpos
+ data
->lvd_First
);
649 } /* if (entry is shown) */
656 /******************************
657 ** Listview::HandleInput() **
658 ******************************/
660 IPTR
AROSListview__GM_HANDLEINPUT(Class
*cl
, Object
*o
, struct gpInput
*msg
)
662 /* Default: stay active */
663 IPTR retval
= GMR_MEACTIVE
;
669 /*************************
670 ** Listview::Render() **
671 *************************/
673 IPTR
AROSListview__GM_RENDER(Class
*cl
, Object
*o
, struct gpRender
*msg
)
675 struct IBox container
;
676 struct LVData
*data
= INST_DATA(cl
, o
);
678 switch (msg
->gpr_Redraw
)
682 /* Calculate the old bounding box */
683 GetGadgetIBox(o
, msg
->gpr_GInfo
, &container
);
685 if ((container
.Height
<= 2 * LV_BORDERWIDTH_Y
+ data
->lvd_VertSpacing
) ||
686 (container
.Width
<= 2 * LV_BORDERWIDTH_X
+ data
->lvd_HorSpacing
))
690 /* Erase the old gadget imagery */
691 SetAPen(msg
->gpr_RPort
,
692 msg
->gpr_GInfo
->gi_DrInfo
->dri_Pens
[data
->lvd_BackPen
]);
695 RectFill(msg
->gpr_RPort
,
698 container
.Left
+ container
.Width
- 1,
699 container
.Top
+ container
.Height
- 1);
701 DrawListBorder(msg
->gpr_RPort
,
702 msg
->gpr_GInfo
->gi_DrInfo
->dri_Pens
,
704 (data
->lvd_Flags
& LVFLG_READONLY
)
708 RenderEntries(cl
, o
, msg
,
710 ShownEntries(data
, &container
),
718 /* Redraw all damaged entries */
721 for (offset
= data
->lvd_DamageOffset
; data
->lvd_NumDamaged
--; offset
++)
723 RenderEntries(cl
, o
, msg
,
724 data
->lvd_First
+ offset
,
738 /*************************
739 ** Listview::Layout() **
740 **************************/
741 VOID
AROSListview__GM_LAYOUT(Class
*cl
, Object
*o
, struct gpLayout
*msg
)
744 #define RELFLAGS (GFLG_RELRIGHT|GFLG_RELWIDTH|GFLG_RELHEIGHT|GFLG_RELBOTTOM)
745 struct IBox container
;
746 struct LVData
*data
= INST_DATA(cl
, o
);
749 D(bug("Listview::Layout()\n"));
752 /* Only recalculate dimensions if this is the first layout, or we
753 * are a GFLG_xxx gadget
755 if (msg
->gpl_Initial
|| EG(o
)->Flags
& RELFLAGS
)
757 struct GadgetInfo
*gi
= msg
->gpl_GInfo
;
760 struct TagItem tags
[] =
762 {AROSA_Listview_Visible
, 0},
766 D(bug("data->lvd_List: %p\n", data
->lvd_List
));
767 GetGadgetIBox(o
, gi
, &container
);
769 /* Compute widths of each column */
770 ComputeColumnWidths(container
.Width
, data
);
772 /* Compute left and right offsets for each column */
773 ComputeColLeftRight(container
.Left
, data
);
776 tags
[0].ti_Data
= ShownEntries(data
, &container
);
777 D(bug("Layot: notifying visible=%d, gi=%d\n", tags
[0].ti_Data
, gi
));
778 DoMethod(o
, OM_SET
, (IPTR
) tags
, (IPTR
) gi
);
779 } /* if (gadgetinfo supplied) */
781 } /* if (GFLG_xxx or first layout) */
782 ReturnVoid("Listview::Layout");
786 /*****************************
787 ** Listview::GoInActive() **
788 *****************************/
789 IPTR
AROSListview__GM_GOINACTIVE(Class
*cl
, Object
*o
, struct gpGoInactive
*msg
)
794 /***********************
795 ** Listview::Insert() **
796 ***********************/
797 IPTR AROSListview__AROSM_Listview_Insert
801 struct AROSP_Listview_Insert
*msg
804 struct LVData
*data
= INST_DATA(cl
, o
);
806 return (IPTR
)DoMethod(data
->lvd_List
,
808 (IPTR
) msg
->ItemArray
,
813 /*****************************
814 ** Listview::InsertSingle() **
815 *****************************/
816 IPTR AROSListview__AROSM_Listview_InsertSingle
820 struct AROSP_Listview_InsertSingle
*msg
823 struct LVData
*data
= INST_DATA(cl
, o
);
825 return (IPTR
)DoMethod(data
->lvd_List
,
826 AROSM_List_InsertSingle
,
832 /***********************
833 ** Listview::Remove() **
834 ***********************/
835 IPTR AROSListview__AROSM_Listview_Remove
839 struct AROSP_Listview_Insert
*msg
842 struct LVData
*data
= INST_DATA(cl
, o
);
844 return (IPTR
)DoMethod(data
->lvd_List
,
845 AROSM_List_InsertSingle
,