- Assume the left/select mouse button has been released if the
[AROS.git] / workbench / libs / muimaster / classes / listview.c
blob299ba3875cdd28e2643cd4d956cec14f0f1e065f
1 /*
2 Copyright © 2002-2015, The AROS Development Team. All rights reserved.
3 $Id$
4 */
6 #include <aros/debug.h>
8 #include <graphics/gfx.h>
9 #include <graphics/view.h>
10 #include <devices/rawkeycodes.h>
11 #include <clib/alib_protos.h>
12 #include <proto/exec.h>
13 #include <proto/graphics.h>
14 #include <proto/utility.h>
15 #include <proto/intuition.h>
16 #include <proto/muimaster.h>
18 #include "mui.h"
19 #include "muimaster_intern.h"
20 #include "support.h"
21 #include "window.h"
22 #include "list.h"
23 #include "prefs.h"
25 extern struct Library *MUIMasterBase;
27 struct MUI_ListviewData
29 Object *list, *group, *vert;
30 struct Hook hook;
31 struct Hook selfnotify_hook;
32 BOOL noforward;
33 BOOL read_only;
34 BOOL select_change;
35 IPTR multiselect;
36 IPTR scroller_pos;
37 struct MUI_EventHandlerNode ehn;
39 int mouse_click; /* see below if mouse is held down */
41 /* double click */
42 ULONG last_secs;
43 ULONG last_mics;
44 ULONG last_active;
45 BOOL doubleclick;
47 /* clicked column */
48 LONG click_column;
49 LONG def_click_column;
51 /* user prefs */
52 ListviewMulti prefs_multi;
55 #define MOUSE_CLICK_ENTRY 1 /* on entry clicked */
56 #define MOUSE_CLICK_TITLE 2 /* on title clicked */
58 static void DoWheelMove(struct IClass *cl, Object *obj, LONG wheely);
60 #define PROP_VERT_FIRST 1
61 #define LIST_VERT_FIRST 4
62 #define LIST_VERT_VISIBLE 5
63 #define LIST_VERT_ENTRIES 6
65 ULONG Listview_Function(struct Hook *hook, APTR dummyobj, void **msg)
67 struct MUI_ListviewData *data = (struct MUI_ListviewData *)hook->h_Data;
68 SIPTR type = (SIPTR) msg[0];
69 SIPTR val = (SIPTR) msg[1];
71 D(bug("[ListView] List 0x%p, Event %d, value %ld\n", data->list, type,
72 val));
74 switch (type)
76 case PROP_VERT_FIRST:
77 get(data->vert, MUIA_Prop_First, &val);
78 nnset(data->list, MUIA_List_VertProp_First, val);
79 break;
81 case LIST_VERT_FIRST:
82 nnset(data->vert, MUIA_Prop_First, val);
83 break;
84 case LIST_VERT_VISIBLE:
85 nnset(data->vert, MUIA_Prop_Visible, val);
86 break;
87 case LIST_VERT_ENTRIES:
88 nnset(data->vert, MUIA_Prop_Entries, val);
89 break;
91 return 0;
94 ULONG SelfNotify_Function(struct Hook *hook, APTR obj, void **msg)
96 struct MUI_ListviewData *data = (struct MUI_ListviewData *)hook->h_Data;
97 SIPTR attribute = (SIPTR) msg[0];
98 SIPTR value = (SIPTR) msg[1];
100 /* This allows avoiding notify loops */
101 data->noforward = TRUE;
102 SetAttrs(obj, MUIA_Group_Forward, FALSE, attribute, value, TAG_DONE);
103 data->noforward = FALSE;
105 return 0;
108 /**************************************************************************
109 OM_NEW
110 **************************************************************************/
111 IPTR Listview__OM_NEW(struct IClass *cl, Object *obj, struct opSet *msg)
113 struct MUI_ListviewData *data;
114 struct TagItem *tag, *tags;
115 Object *group, *vert;
116 Object *list =
117 (Object *) GetTagData(MUIA_Listview_List, (IPTR) NULL,
118 msg->ops_AttrList);
119 IPTR cyclechain;
120 LONG entries = 0, first = 0, visible = 0;
122 if (!list)
123 return (IPTR) NULL;
125 cyclechain =
126 (IPTR) GetTagData(MUIA_CycleChain, (IPTR) 0, msg->ops_AttrList);
128 vert = ScrollbarObject, MUIA_Group_Horiz, FALSE, End;
130 obj = (Object *) DoSuperNewTags(cl, obj, NULL,
131 MUIA_Group_Horiz, FALSE,
132 MUIA_CycleChain, cyclechain,
133 MUIA_InnerLeft, 0,
134 MUIA_InnerRight, 0,
135 Child, (IPTR) (group = HGroup,
136 MUIA_InnerLeft, 0,
137 MUIA_InnerRight, 0,
138 MUIA_Group_Spacing, 0,
139 End),
140 TAG_DONE);
142 if (!obj)
143 return (IPTR) NULL;
145 data = INST_DATA(cl, obj);
146 data->list = list;
147 data->vert = vert;
148 data->group = group;
150 data->hook.h_Entry = HookEntry;
151 data->hook.h_SubEntry = (HOOKFUNC) Listview_Function;
152 data->hook.h_Data = data;
154 data->selfnotify_hook.h_Entry = HookEntry;
155 data->selfnotify_hook.h_SubEntry = (HOOKFUNC) SelfNotify_Function;
156 data->selfnotify_hook.h_Data = data;
157 data->noforward = FALSE;
159 data->last_active = -1;
161 data->ehn.ehn_Events = IDCMP_MOUSEBUTTONS | IDCMP_RAWKEY;
162 data->ehn.ehn_Priority = 0;
163 data->ehn.ehn_Flags = 0;
164 data->ehn.ehn_Object = obj;
165 data->ehn.ehn_Class = cl;
167 /* parse initial taglist */
168 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
170 switch (tag->ti_Tag)
172 case MUIA_Listview_DoubleClick:
173 data->doubleclick = tag->ti_Data != 0;
174 break;
175 case MUIA_Listview_Input:
176 data->read_only = !tag->ti_Data;
177 break;
178 case MUIA_Listview_MultiSelect:
179 data->multiselect = tag->ti_Data;
180 break;
181 case MUIA_Listview_ScrollerPos:
182 data->scroller_pos = tag->ti_Data;
183 break;
187 /* Add list and/or scroller */
188 switch (data->scroller_pos)
190 case MUIV_Listview_ScrollerPos_None:
191 DoMethod(group, OM_ADDMEMBER, list);
192 break;
193 case MUIV_Listview_ScrollerPos_Left:
194 DoMethod(group, OM_ADDMEMBER, vert);
195 DoMethod(group, OM_ADDMEMBER, list);
196 break;
197 default:
198 DoMethod(group, OM_ADDMEMBER, list);
199 DoMethod(group, OM_ADDMEMBER, vert);
200 break;
203 get(list, MUIA_List_VertProp_First, &first);
204 get(list, MUIA_List_VertProp_Visible, &visible);
205 get(list, MUIA_List_VertProp_Entries, &entries);
207 D(bug
208 ("[ListView 0x%p] List 0x%p, First %ld, Visible %ld, Entries %ld\n",
209 obj, list, first, visible, entries));
211 SetAttrs(data->vert,
212 MUIA_Prop_First, first,
213 MUIA_Prop_Visible, visible, MUIA_Prop_Entries, entries, TAG_DONE);
215 DoMethod(vert, MUIM_Notify, MUIA_Prop_First, MUIV_EveryTime, (IPTR) obj,
216 4, MUIM_CallHook, (IPTR) &data->hook, PROP_VERT_FIRST,
217 MUIV_TriggerValue);
218 DoMethod(list, MUIM_Notify, MUIA_List_VertProp_First, MUIV_EveryTime,
219 (IPTR) obj, 4, MUIM_CallHook, (IPTR) &data->hook, LIST_VERT_FIRST,
220 MUIV_TriggerValue);
221 DoMethod(list, MUIM_Notify, MUIA_List_VertProp_Visible, MUIV_EveryTime,
222 (IPTR) obj, 4, MUIM_CallHook, (IPTR) &data->hook,
223 LIST_VERT_VISIBLE, MUIV_TriggerValue);
224 DoMethod(list, MUIM_Notify, MUIA_List_VertProp_Entries, MUIV_EveryTime,
225 (IPTR) obj, 4, MUIM_CallHook, (IPTR) &data->hook,
226 LIST_VERT_ENTRIES, MUIV_TriggerValue);
227 DoMethod(list, MUIM_Notify, MUIA_List_Active, MUIV_EveryTime,
228 (IPTR) obj, 4, MUIM_CallHook, (IPTR) &data->selfnotify_hook,
229 MUIA_List_Active, MUIV_TriggerValue);
231 return (IPTR) obj;
234 /**************************************************************************
235 OM_DISPOSE
236 **************************************************************************/
237 IPTR Listview__OM_DISPOSE(struct IClass *cl, Object *obj, Msg msg)
239 return DoSuperMethodA(cl, obj, msg);
242 /**************************************************************************
243 OM_SET
244 **************************************************************************/
245 IPTR Listview__OM_SET(struct IClass *cl, Object *obj, struct opSet *msg)
247 struct TagItem *tag, *tags;
248 IPTR no_notify = GetTagData(MUIA_NoNotify, FALSE, msg->ops_AttrList);
249 struct MUI_ListviewData *data = INST_DATA(cl, obj);
251 if (data->noforward)
253 return DoSuperMethodA(cl, obj, (Msg) msg);
256 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
258 switch (tag->ti_Tag)
260 case MUIA_List_CompareHook:
261 case MUIA_List_ConstructHook:
262 case MUIA_List_DestructHook:
263 case MUIA_List_DisplayHook:
264 case MUIA_List_VertProp_First:
265 case MUIA_List_Format:
266 case MUIA_List_VertProp_Entries:
267 case MUIA_List_VertProp_Visible:
268 case MUIA_List_Active:
269 case MUIA_List_First:
270 case MUIA_List_Visible:
271 case MUIA_List_Entries:
272 case MUIA_List_Quiet:
273 SetAttrs(data->list, MUIA_NoNotify, no_notify, tag->ti_Tag,
274 tag->ti_Data, TAG_DONE);
275 break;
276 case MUIA_Listview_DoubleClick:
277 data->doubleclick = tag->ti_Data != 0;
278 break;
279 case MUIA_Listview_SelectChange:
280 data->select_change = tag->ti_Data != 0;
281 break;
282 case MUIA_Disabled:
283 /* Stop listening for events we only listen to when mouse button is
284 down: we will not be informed of the button being released */
285 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
286 (IPTR) &data->ehn);
287 data->ehn.ehn_Events &=
288 ~(IDCMP_MOUSEMOVE | IDCMP_INTUITICKS | IDCMP_INACTIVEWINDOW);
289 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
290 (IPTR) &data->ehn);
291 break;
295 return DoSuperMethodA(cl, obj, (Msg) msg);
298 /**************************************************************************
299 OM_GET
300 **************************************************************************/
301 IPTR Listview__OM_GET(struct IClass *cl, Object *obj, struct opGet *msg)
303 /* small macro to simplify return value storage */
304 #define STORE *(msg->opg_Storage)
305 struct MUI_ListviewData *data = INST_DATA(cl, obj);
307 switch (msg->opg_AttrID)
309 case MUIA_List_CompareHook:
310 case MUIA_List_ConstructHook:
311 case MUIA_List_DestructHook:
312 case MUIA_List_DisplayHook:
313 case MUIA_List_VertProp_First:
314 case MUIA_List_Format:
315 case MUIA_List_VertProp_Entries:
316 case MUIA_List_VertProp_Visible:
317 case MUIA_List_Active:
318 case MUIA_List_First:
319 case MUIA_List_Visible:
320 case MUIA_List_Entries:
321 case MUIA_List_Quiet:
322 return GetAttr(msg->opg_AttrID, data->list, msg->opg_Storage);
323 case MUIA_Listview_DoubleClick:
324 STORE = data->doubleclick;
325 return 1;
326 case MUIA_Listview_ClickColumn:
327 STORE = data->click_column;
328 return 1;
329 case MUIA_Listview_List:
330 STORE = (IPTR) data->list;
331 return 1;
332 case MUIA_Listview_SelectChange:
333 STORE = data->select_change;
334 return 1;
337 return DoSuperMethodA(cl, obj, (Msg) msg);
338 #undef STORE
341 /**************************************************************************
342 MUIM_Setup
343 **************************************************************************/
344 IPTR Listview__MUIM_Setup(struct IClass *cl, Object *obj,
345 struct MUIP_Setup *msg)
347 struct MUI_ListviewData *data = INST_DATA(cl, obj);
349 if (!DoSuperMethodA(cl, obj, (Msg) msg))
350 return 0;
352 data->prefs_multi = muiGlobalInfo(obj)->mgi_Prefs->list_multi;
353 if (data->multiselect == MUIV_Listview_MultiSelect_Default)
355 if (data->prefs_multi == LISTVIEW_MULTI_SHIFTED)
356 data->multiselect = MUIV_Listview_MultiSelect_Shifted;
357 else
358 data->multiselect = MUIV_Listview_MultiSelect_Always;
361 DoMethod(_win(obj), MUIM_Window_AddEventHandler, (IPTR) &data->ehn);
363 return 1;
366 /**************************************************************************
367 MUIM_Cleanup
368 **************************************************************************/
369 IPTR Listview__MUIM_Cleanup(struct IClass *cl, Object *obj,
370 struct MUIP_Cleanup *msg)
372 struct MUI_ListviewData *data = INST_DATA(cl, obj);
374 DoMethod(_win(obj), MUIM_Window_RemEventHandler, (IPTR) &data->ehn);
375 data->mouse_click = 0;
377 return DoSuperMethodA(cl, obj, (Msg) msg);
380 /**************************************************************************
381 MUIM_HandleEvent
382 **************************************************************************/
383 IPTR Listview__MUIM_HandleEvent(struct IClass *cl, Object *obj,
384 struct MUIP_HandleEvent *msg)
386 struct MUI_ListviewData *data = INST_DATA(cl, obj);
387 Object *list = data->list;
388 struct MUI_List_TestPos_Result pos;
389 LONG seltype, old_active, new_active, visible, first, last, i;
390 IPTR result = 0;
391 BOOL select = FALSE, clear = FALSE, range_select = FALSE, changing;
392 WORD delta;
393 typeof(msg->muikey) muikey = msg->muikey;
395 new_active = old_active = XGET(list, MUIA_List_Active);
396 visible = XGET(list, MUIA_List_Visible);
398 if (muikey != MUIKEY_NONE)
400 result = MUI_EventHandlerRC_Eat;
402 /* Make keys behave differently in read-only mode */
403 if (data->read_only)
405 switch (muikey)
407 case MUIKEY_TOP:
408 muikey = MUIKEY_LINESTART;
409 break;
411 case MUIKEY_BOTTOM:
412 muikey = MUIKEY_LINEEND;
413 break;
415 case MUIKEY_UP:
416 muikey = MUIKEY_LEFT;
417 break;
419 case MUIKEY_DOWN:
420 case MUIKEY_PRESS:
421 muikey = MUIKEY_RIGHT;
422 break;
426 switch (muikey)
428 case MUIKEY_TOGGLE:
429 if (data->multiselect != MUIV_Listview_MultiSelect_None
430 && !data->read_only)
432 select = TRUE;
433 data->click_column = data->def_click_column;
434 new_active = MUIV_List_Active_Down;
436 else
438 DoMethod(list, MUIM_List_Jump, 0);
439 muikey = MUIKEY_NONE;
441 break;
443 case MUIKEY_TOP:
444 new_active = MUIV_List_Active_Top;
445 break;
447 case MUIKEY_BOTTOM:
448 new_active = MUIV_List_Active_Bottom;
449 break;
451 case MUIKEY_LEFT:
452 case MUIKEY_WORDLEFT:
453 DoMethod(list, MUIM_List_Jump, MUIV_List_Jump_Up);
454 break;
456 case MUIKEY_RIGHT:
457 case MUIKEY_WORDRIGHT:
458 DoMethod(list, MUIM_List_Jump, MUIV_List_Jump_Down);
459 break;
461 case MUIKEY_LINESTART:
462 DoMethod(list, MUIM_List_Jump, MUIV_List_Jump_Top);
463 break;
465 case MUIKEY_LINEEND:
466 DoMethod(list, MUIM_List_Jump, MUIV_List_Jump_Bottom);
467 break;
469 case MUIKEY_UP:
470 new_active = MUIV_List_Active_Up;
471 break;
473 case MUIKEY_DOWN:
474 new_active = MUIV_List_Active_Down;
475 break;
477 case MUIKEY_PAGEUP:
478 if (data->read_only)
479 DoWheelMove(cl, obj, -visible);
480 else
481 new_active = MUIV_List_Active_PageUp;
482 break;
484 case MUIKEY_PAGEDOWN:
485 if (data->read_only)
486 DoWheelMove(cl, obj, visible);
487 else
488 new_active = MUIV_List_Active_PageDown;
489 break;
491 default:
492 result = 0;
495 else if (msg->imsg)
497 DoMethod(list, MUIM_List_TestPos, msg->imsg->MouseX,
498 msg->imsg->MouseY, (IPTR) &pos);
500 switch (msg->imsg->Class)
502 case IDCMP_MOUSEBUTTONS:
503 if (msg->imsg->Code == SELECTDOWN)
505 if (_isinobject(list, msg->imsg->MouseX, msg->imsg->MouseY))
507 data->mouse_click = MOUSE_CLICK_ENTRY;
509 if (!data->read_only && pos.entry != -1)
511 new_active = pos.entry;
513 clear = (data->multiselect
514 == MUIV_Listview_MultiSelect_Shifted
515 && (msg->imsg->Qualifier
516 & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)) == 0);
517 seltype = clear ?
518 MUIV_List_Select_On: MUIV_List_Select_Toggle;
519 select = data->multiselect
520 != MUIV_Listview_MultiSelect_None;
522 /* Handle MUIA_Listview_ClickColumn */
523 data->click_column = pos.column;
524 superset(cl, obj, MUIA_Listview_ClickColumn,
525 data->click_column);
527 /* Handle double clicking */
528 if (data->last_active == pos.entry
529 && DoubleClick(data->last_secs, data->last_mics,
530 msg->imsg->Seconds, msg->imsg->Micros))
532 set(obj, MUIA_Listview_DoubleClick, TRUE);
533 data->last_active = -1;
534 data->last_secs = data->last_mics = 0;
536 else
538 data->last_active = pos.entry;
539 data->last_secs = msg->imsg->Seconds;
540 data->last_mics = msg->imsg->Micros;
543 /* Look out for mouse movement, timer and
544 inactive-window events while mouse button is
545 down */
546 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
547 (IPTR) &data->ehn);
548 data->ehn.ehn_Events |= (IDCMP_MOUSEMOVE
549 | IDCMP_INTUITICKS |IDCMP_INACTIVEWINDOW);
550 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
551 (IPTR) &data->ehn);
555 else
557 /* Activate object */
558 if (msg->imsg->Code == SELECTUP && data->mouse_click)
560 set(_win(obj), MUIA_Window_ActiveObject, (IPTR)obj);
561 data->mouse_click = 0;
564 /* Restore normal event mask */
565 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
566 (IPTR) &data->ehn);
567 data->ehn.ehn_Events &= ~(IDCMP_MOUSEMOVE | IDCMP_INTUITICKS
568 | IDCMP_INACTIVEWINDOW);
569 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
570 (IPTR) &data->ehn);
572 break;
574 case IDCMP_MOUSEMOVE:
575 case IDCMP_INTUITICKS:
576 if (pos.flags & MUI_LPR_ABOVE)
577 new_active = MUIV_List_Active_Up;
578 else if (pos.flags & MUI_LPR_BELOW)
579 new_active = MUIV_List_Active_Down;
580 else
581 new_active = pos.entry;
583 select = new_active != old_active
584 && data->multiselect != MUIV_Listview_MultiSelect_None;
585 if (select)
587 DoMethod(list, MUIM_List_Select, MUIV_List_Select_Active,
588 MUIV_List_Select_Ask, &seltype);
589 range_select = new_active >= 0;
592 break;
594 case IDCMP_INACTIVEWINDOW:
595 /* Stop listening for events we only listen to when mouse button is
596 down: we will not be informed of the button being released */
597 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
598 (IPTR) &data->ehn);
599 data->ehn.ehn_Events &=
600 ~(IDCMP_MOUSEMOVE | IDCMP_INTUITICKS | IDCMP_INACTIVEWINDOW);
601 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
602 (IPTR) &data->ehn);
603 break;
605 case IDCMP_RAWKEY:
606 /* Scroll wheel */
607 if (_isinobject(data->vert, msg->imsg->MouseX, msg->imsg->MouseY))
608 delta = 1;
609 else if (_isinobject(list, msg->imsg->MouseX, msg->imsg->MouseY))
610 delta = 4;
611 else
612 delta = 0;
614 if (delta != 0)
616 switch (msg->imsg->Code)
618 case RAWKEY_NM_WHEEL_UP:
619 DoWheelMove(cl, obj, -delta);
620 break;
622 case RAWKEY_NM_WHEEL_DOWN:
623 DoWheelMove(cl, obj, delta);
624 break;
626 result = MUI_EventHandlerRC_Eat;
628 break;
632 /* Decide in advance if any selections may change */
633 changing = clear || muikey == MUIKEY_TOGGLE || select;
635 /* Change selected and active entries */
636 if (changing)
637 set(obj, MUIA_Listview_SelectChange, TRUE);
639 if (clear)
641 DoMethod(list, MUIM_List_Select, MUIV_List_Select_All,
642 MUIV_List_Select_Off, NULL);
645 if (muikey == MUIKEY_TOGGLE)
647 DoMethod(list, MUIM_List_Select,
648 MUIV_List_Select_Active,
649 MUIV_List_Select_Toggle, NULL);
650 select = FALSE;
653 if (new_active != old_active)
654 set(list, MUIA_List_Active, new_active);
656 if (select)
658 if (range_select)
660 if (old_active < new_active)
661 first = old_active + 1, last = new_active;
662 else
663 first = new_active, last = old_active - 1;
664 for (i = first; i <= last; i++)
665 DoMethod(list, MUIM_List_Select, i, seltype, NULL);
667 else
668 DoMethod(list, MUIM_List_Select, MUIV_List_Select_Active, seltype,
669 NULL);
672 if (changing)
673 set(obj, MUIA_Listview_SelectChange, FALSE);
675 return result;
678 static void DoWheelMove(struct IClass *cl, Object *obj, LONG wheely)
680 struct MUI_ListviewData *data = INST_DATA(cl, obj);
681 LONG new, first, entries, visible;
683 new = first = XGET(data->list, MUIA_List_First);
684 entries = XGET(data->list, MUIA_List_Entries);
685 visible = XGET(data->list, MUIA_List_Visible);
687 new += wheely;
689 if (new > entries - visible)
691 new = entries - visible;
694 if (new < 0)
696 new = 0;
699 if (new != first)
701 set(data->list, MUIA_List_First, new);
705 BOOPSI_DISPATCHER(IPTR, Listview_Dispatcher, cl, obj, msg)
707 switch (msg->MethodID)
709 case OM_SET:
710 return Listview__OM_SET(cl, obj, (struct opSet *)msg);
711 case OM_GET:
712 return Listview__OM_GET(cl, obj, (struct opGet *)msg);
713 case OM_NEW:
714 return Listview__OM_NEW(cl, obj, (struct opSet *)msg);
715 case OM_DISPOSE:
716 return Listview__OM_DISPOSE(cl, obj, msg);
717 case MUIM_Setup:
718 return Listview__MUIM_Setup(cl, obj, (struct MUIP_Setup *)msg);
719 case MUIM_Cleanup:
720 return Listview__MUIM_Cleanup(cl, obj, (struct MUIP_Cleanup *)msg);
721 case MUIM_HandleEvent:
722 return Listview__MUIM_HandleEvent(cl, obj,
723 (struct MUIP_HandleEvent *)msg);
724 case MUIM_List_Clear:
725 case MUIM_List_CreateImage:
726 case MUIM_List_DeleteImage:
727 case MUIM_List_Exchange:
728 case MUIM_List_GetEntry:
729 case MUIM_List_Insert:
730 case MUIM_List_InsertSingle:
731 case MUIM_List_Jump:
732 case MUIM_List_NextSelected:
733 case MUIM_List_Redraw:
734 case MUIM_List_Remove:
735 case MUIM_List_Select:
736 case MUIM_List_Sort:
737 case MUIM_List_TestPos:
739 struct MUI_ListviewData *data = INST_DATA(cl, obj);
741 return DoMethodA(data->list, msg);
746 return DoSuperMethodA(cl, obj, msg);
748 BOOPSI_DISPATCHER_END
751 * Class descriptor.
753 const struct __MUIBuiltinClass _MUI_Listview_desc =
755 MUIC_Listview,
756 MUIC_Group,
757 sizeof(struct MUI_ListviewData),
758 (void *) Listview_Dispatcher