revert between 56095 -> 55830 in arch
[AROS.git] / workbench / classes / gadgets / aroslistview / listviewclass.c
blobdc2570ad3c93a46170d4eb89a846d13df78429d6
1 /*
2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
3 $Id$
5 AROS specific listview class implementation.
6 */
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>
23 #include <string.h>
24 #include <gadgets/aroslistview.h>
25 #include <gadgets/aroslist.h>
27 #include "aroslistview_intern.h"
30 #define SDEBUG 0
31 #define DEBUG 0
34 #include <aros/debug.h>
37 /*****************************************************************************/
41 #define SETFLAG(flagvar, boolvar, flag) \
42 if (boolvar) \
43 flagvar |= flag; \
44 else \
45 flagvar &= ~flag;
48 STATIC IPTR _OM_SET(Class *cl, Object *o,struct opSet *msg)
50 IPTR retval = (IPTR)0;
52 struct TagItem *tag, *tstate;
53 struct LVData *data;
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)
63 switch (tag->ti_Tag)
65 case AROSA_Listview_DisplayHook:
66 data->lvd_DisplayHook = (struct Hook *)tag->ti_Data;
67 retval = 1UL;
68 break;
70 case AROSA_Listview_FrontPen:
71 data->lvd_FrontPen = (UBYTE)tag->ti_Data;
72 retval = 1UL;
73 break;
75 case AROSA_Listview_BackPen:
76 data->lvd_BackPen = (UBYTE)tag->ti_Data;
77 retval = 1UL;
78 break;
81 case AROSA_Listview_Visible:
82 case AROSA_Listview_Total:
84 struct TagItem tags[] =
86 {tag->ti_Tag, tag->ti_Data},
87 {TAG_END}
90 NotifyAttrs(cl, o, msg, tags);
91 } break;
93 case AROSA_Listview_List:
96 ULONG numentries;
98 struct TagItem tags[] =
100 {AROSA_Listview_First, 0},
101 {AROSA_Listview_Total, 0},
102 {TAG_END}
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,
110 TAG_END);
112 tags[1].ti_Data = numentries;
113 DoMethod(o, OM_SET, (IPTR) tags, (IPTR) msg->ops_GInfo);
115 retval = 1UL;
116 } break;
119 case AROSA_Listview_HorSpacing:
120 data->lvd_HorSpacing = (UBYTE)tag->ti_Data;
121 retval = 1UL;
122 break;
124 case AROSA_Listview_VertSpacing:
125 data->lvd_VertSpacing = (UBYTE)tag->ti_Data;
127 ReCalcEntryHeight(data);
128 retval = 1UL;
129 break;
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);
138 retval = 1UL;
139 } break;
141 case AROSA_Listview_First:
143 struct TagItem tags[] =
145 {AROSA_Listview_First, tag->ti_Data},
146 {TAG_END}
149 LONG old = data->lvd_First;
151 #if DEBUG
152 if (msg->MethodID == OM_SET)
154 D(bug("_First OM_SET\n"));
155 } else
157 D(bug("_First OM_UPDATEd, lvd_NC=%d\n",
158 data->lvd_NotifyCount));
160 #endif
162 retval = 1UL;
163 data->lvd_First = (LONG)tag->ti_Data;
165 if ( ( msg->MethodID == OM_UPDATE )
166 && ( old != data->lvd_First ))
168 struct RastPort *rp;
169 WORD steps;
170 UWORD visible, abs_steps;
171 struct IBox box;
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)
183 LONG dy;
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);
205 ReleaseGIRPort(rp);
206 retval = 0UL;
210 } /* if (ObtainGIRPort succeed) */
212 } /* if (at most half the visible entries scrolled out) */
214 } /* if (msg is of type OM_UPDATE) */
217 /* Notify change */
218 NotifyAttrs(cl, o, msg, tags);
220 } break;
222 case AROSA_Listview_RenderHook:
223 data->lvd_RenderHook = (struct Hook *)data->lvd_RenderHook;
224 retval = 1UL;
225 break;
227 case AROSA_Listview_MultiSelect:
228 SETFLAG(data->lvd_Flags, tag->ti_Data, LVFLG_MULTISELECT);
229 break;
231 case GA_TextAttr:
233 struct TextFont *tf;
235 tf = OpenFont((struct TextAttr *)tag->ti_Data);
236 if (tf)
238 if (data->lvd_Font)
240 CloseFont(data->lvd_Font);
242 data->lvd_Font = tf;
244 ReCalcEntryHeight(data);
246 } break;
249 default:
250 break;
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);
268 return retval;
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().
287 if ( retval
288 && (cl == OCLASS(o))
289 && (data->lvd_NotifyCount)
292 struct GadgetInfo *gi = ((struct opSet *)msg)->ops_GInfo;
293 if (gi)
295 struct RastPort *rp = ObtainGIRPort(gi);
296 if (rp)
298 struct IBox ibox;
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);
310 ReleaseGIRPort(rp);
315 return retval;
318 /**********************
319 ** Listview::New() **
320 **********************/
323 IPTR AROSListview__OM_NEW(Class *cl, Object *o, struct opSet *msg)
325 struct LVData *data;
326 struct ColumnAttrs *colattrs;
327 STRPTR *dharray;
328 ULONG colattrsz;
330 struct opSet ops, *p_ops = &ops;
331 struct TagItem tags[] =
333 {GA_RelSpecial, TRUE},
334 {TAG_MORE, 0}
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);
344 if(!o)
345 return (IPTR) NULL;
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)
353 goto failure;
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
358 * format string.
360 colattrsz = data->lvd_MaxColumns * sizeof (struct ColumnAttrs);
361 colattrs = AllocVec(colattrsz, MEMF_ANY|MEMF_CLEAR);
362 if (!colattrs)
363 goto failure;
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);
373 if (!dharray)
374 goto failure;
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 */
386 _OM_SET(cl, o, msg);
388 /* If not font has been set, use our own. */
389 if (!data->lvd_Font)
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)
401 goto failure;
403 ReCalcEntryHeight(data);
406 return ((IPTR)o);
409 failure:
410 CoerceMethod(cl, o, OM_DISPOSE);
411 return (IPTR) NULL;
414 /**********************
415 ** Listview::Get() **
416 **********************/
417 IPTR AROSListview__OM_GET(Class *cl, Object *o, struct opGet *msg)
419 IPTR retval = 1UL;
420 struct LVData *data;
422 data = INST_DATA(cl, o);
424 switch (msg->opg_AttrID)
426 case AROSA_Listview_HorSpacing:
427 *(msg->opg_Storage) = (IPTR)data->lvd_HorSpacing;
428 break;
430 case AROSA_Listview_VertSpacing:
431 *(msg->opg_Storage) = (IPTR)data->lvd_VertSpacing;
432 break;
434 case AROSA_Listview_List:
435 *(msg->opg_Storage) = (IPTR)data->lvd_List;
436 break;
438 case AROSA_Listview_DoubleClick:
439 *(msg->opg_Storage) = (IPTR)(data->lvd_Flags & LVFLG_DOUBLECLICK) ?
440 TRUE : FALSE;
441 break;
443 default:
444 retval = DoSuperMethodA(cl, o, (Msg)msg);
445 break;
447 return (retval);
451 /**************************
452 ** Listview::Dispose() **
453 **************************/
454 VOID AROSListview__OM_DISPOSE(Class *cl, Object *o, Msg msg)
456 struct LVData *data;
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);
466 if (data->lvd_Font)
467 CloseFont(data->lvd_Font);
469 return;
472 /**************************
473 ** Listview::HitTest() **
474 **************************/
475 IPTR AROSListview__GM_HITTEST(Class *cl, Object *o, struct gpHitTest *msg)
477 IPTR retval;
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)
486 retval = 0UL;
490 return (retval);
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;
504 ULONG numentries;
505 UWORD shown;
506 /* pos of the selected inside the listview. Eg the first viewed has clickpos 0 */
507 UWORD clickpos;
508 LONG active;
509 UWORD activepos;
511 WORD updateoldactive = -1;
513 BOOL rerender = FALSE;
514 BOOL singleclick = FALSE, doubleclick = FALSE;
517 if (data->lvd_Flags & LVFLG_READONLY)
518 goto exit;
520 if (!msg->gpi_IEvent)
521 goto exit;
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;
552 doubleclick = TRUE;
553 D(bug("\tlv: doubleclick at pos %d\n", clickpos));
556 else
558 singleclick = TRUE;
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)
567 DoMethod
569 data->lvd_List,
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;
577 rerender = TRUE;
579 else
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;
596 rerender = TRUE;
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,
605 TAG_END);
607 *(msg->gpi_Termination) = IDCMP_GADGETUP;
608 D(bug("\t GMR_VERIFY retval set\n"));
609 retval = GMR_NOREUSE|GMR_VERIFY;
612 if (rerender)
614 struct RastPort *rp;
615 rp = ObtainGIRPort(msg->gpi_GInfo);
616 if (rp)
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);
626 ReleaseGIRPort(rp);
630 /* Tell subclasses that a singleclick occured */
631 if (singleclick)
633 DoMethod( o,
634 AROSM_Listview_SingleClick,
635 (IPTR) msg->gpi_GInfo,
636 clickpos + data->lvd_First);
640 /* Tell subclasses that a doubleclick occured */
641 if (doubleclick)
643 DoMethod( o,
644 AROSM_Listview_DoubleClick,
645 (IPTR) msg->gpi_GInfo,
646 clickpos + data->lvd_First);
649 } /* if (entry is shown) */
652 exit:
653 return (retval);
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;
666 return (retval);
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)
680 case GREDRAW_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))
687 return (0UL);
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,
696 container.Left,
697 container.Top,
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,
703 &container,
704 (data->lvd_Flags & LVFLG_READONLY)
708 RenderEntries(cl, o, msg,
709 data->lvd_First,
710 ShownEntries(data, &container),
711 FALSE
714 break;
716 case GREDRAW_UPDATE:
718 /* Redraw all damaged entries */
719 UWORD offset;
721 for (offset = data->lvd_DamageOffset; data->lvd_NumDamaged --; offset ++)
723 RenderEntries(cl, o, msg,
724 data->lvd_First + offset,
726 TRUE
731 } break;
735 return (1UL);
738 /*************************
739 ** Listview::Layout() **
740 **************************/
741 VOID AROSListview__GM_LAYOUT(Class *cl, Object *o, struct gpLayout *msg)
743 #undef RELFLAGS
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;
758 if (gi)
760 struct TagItem tags[] =
762 {AROSA_Listview_Visible, 0},
763 {TAG_END}
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)
791 return (IPTR)0;
794 /***********************
795 ** Listview::Insert() **
796 ***********************/
797 IPTR AROSListview__AROSM_Listview_Insert
799 Class *cl,
800 Object *o,
801 struct AROSP_Listview_Insert *msg
804 struct LVData *data = INST_DATA(cl, o);
806 return (IPTR)DoMethod(data->lvd_List,
807 AROSM_List_Insert,
808 (IPTR) msg->ItemArray,
809 msg->Position
813 /*****************************
814 ** Listview::InsertSingle() **
815 *****************************/
816 IPTR AROSListview__AROSM_Listview_InsertSingle
818 Class *cl,
819 Object *o,
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,
827 (IPTR) msg->Item,
828 msg->Position
832 /***********************
833 ** Listview::Remove() **
834 ***********************/
835 IPTR AROSListview__AROSM_Listview_Remove
837 Class *cl,
838 Object *o,
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,
846 msg->Position