2 Copyright © 2002-2013, The AROS Development Team. All rights reserved.
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>
19 #include "muimaster_intern.h"
25 extern struct Library
*MUIMasterBase
;
27 struct MUI_ListviewData
29 Object
*list
, *group
, *vert
;
30 struct Hook
*layout_hook
;
32 struct Hook selfnotify_hook
;
36 struct MUI_EventHandlerNode ehn
;
38 int mouse_click
; /* see below if mouse is held down */
48 LONG def_click_column
;
51 ListviewMulti prefs_multi
;
54 #define MOUSE_CLICK_ENTRY 1 /* on entry clicked */
55 #define MOUSE_CLICK_TITLE 2 /* on title clicked */
57 static void DoWheelMove(struct IClass
*cl
, Object
*obj
, LONG wheely
);
59 ULONG
Listview_Layout_Function(struct Hook
*hook
, Object
*obj
,
60 struct MUI_LayoutMsg
*lm
)
62 struct MUI_ListviewData
*data
= (struct MUI_ListviewData
*)hook
->h_Data
;
68 /* Calculate the minmax dimension of the group,
69 ** We only have a fixed number of children, so we need
72 lm
->lm_MinMax
.MinWidth
=
73 _minwidth(data
->list
) + _minwidth(data
->vert
);
74 lm
->lm_MinMax
.DefWidth
=
75 _defwidth(data
->list
) + _defwidth(data
->vert
);
76 lm
->lm_MinMax
.MaxWidth
=
77 _maxwidth(data
->list
) + _maxwidth(data
->vert
);
78 lm
->lm_MinMax
.MaxWidth
=
79 MIN(lm
->lm_MinMax
.MaxWidth
, MUI_MAXMAX
);
81 lm
->lm_MinMax
.MinHeight
=
82 MAX(_minheight(data
->list
), _minheight(data
->vert
));
83 lm
->lm_MinMax
.DefHeight
=
84 MAX(_defheight(data
->list
), lm
->lm_MinMax
.MinHeight
);
85 lm
->lm_MinMax
.MaxHeight
=
86 MIN(_maxheight(data
->list
), _maxheight(data
->vert
));
87 lm
->lm_MinMax
.MaxHeight
=
88 MIN(lm
->lm_MinMax
.MaxHeight
, MUI_MAXMAX
);
94 /* Now place the objects between
95 * (0, 0, lm->lm_Layout.Width - 1, lm->lm_Layout.Height - 1)
98 LONG vert_width
= _minwidth(data
->vert
);
99 LONG lay_width
= lm
->lm_Layout
.Width
;
100 LONG lay_height
= lm
->lm_Layout
.Height
;
104 /* We need all scrollbars and the button */
105 set(data
->vert
, MUIA_ShowMe
, TRUE
);
106 /* We could also overload MUIM_Show... */
107 cont_width
= lay_width
- vert_width
;
108 cont_height
= lay_height
;
110 MUI_Layout(data
->vert
, cont_width
, 0, vert_width
, cont_height
,
113 /* Layout the group a second time, note that setting _mwidth() and
114 _mheight() should be enough, or we invent a new flag */
115 MUI_Layout(data
->list
, 0, 0, cont_width
, cont_height
, 0);
122 #define PROP_VERT_FIRST 1
123 #define LIST_VERT_FIRST 4
124 #define LIST_VERT_VISIBLE 5
125 #define LIST_VERT_ENTRIES 6
127 ULONG
Listview_Function(struct Hook
*hook
, APTR dummyobj
, void **msg
)
129 struct MUI_ListviewData
*data
= (struct MUI_ListviewData
*)hook
->h_Data
;
130 SIPTR type
= (SIPTR
) msg
[0];
131 SIPTR val
= (SIPTR
) msg
[1];
133 D(bug("[ListView] List 0x%p, Event %d, value %ld\n", data
->list
, type
,
138 case PROP_VERT_FIRST
:
139 get(data
->vert
, MUIA_Prop_First
, &val
);
140 nnset(data
->list
, MUIA_List_VertProp_First
, val
);
143 case LIST_VERT_FIRST
:
144 nnset(data
->vert
, MUIA_Prop_First
, val
);
146 case LIST_VERT_VISIBLE
:
147 nnset(data
->vert
, MUIA_Prop_Visible
, val
);
149 case LIST_VERT_ENTRIES
:
150 nnset(data
->vert
, MUIA_Prop_Entries
, val
);
156 ULONG
SelfNotify_Function(struct Hook
*hook
, APTR obj
, void **msg
)
158 struct MUI_ListviewData
*data
= (struct MUI_ListviewData
*)hook
->h_Data
;
159 SIPTR attribute
= (SIPTR
) msg
[0];
160 SIPTR value
= (SIPTR
) msg
[1];
162 /* This allows avoiding notify loops */
163 data
->noforward
= TRUE
;
164 SetAttrs(obj
, MUIA_Group_Forward
, FALSE
, attribute
, value
, TAG_DONE
);
165 data
->noforward
= FALSE
;
170 /**************************************************************************
172 **************************************************************************/
173 IPTR
Listview__OM_NEW(struct IClass
*cl
, Object
*obj
, struct opSet
*msg
)
175 struct MUI_ListviewData
*data
;
176 struct TagItem
*tag
, *tags
;
177 struct Hook
*layout_hook
;
178 Object
*group
, *vert
;
180 (Object
*) GetTagData(MUIA_Listview_List
, (IPTR
) NULL
,
183 (IPTR
) GetTagData(MUIA_CycleChain
, (IPTR
) 0, msg
->ops_AttrList
);
184 LONG entries
= 0, first
= 0, visible
= 0;
189 layout_hook
= mui_alloc_struct(struct Hook
);
193 layout_hook
->h_Entry
= HookEntry
;
194 layout_hook
->h_SubEntry
= (HOOKFUNC
) Listview_Layout_Function
;
196 obj
= (Object
*) DoSuperNewTags(cl
, obj
, NULL
,
197 MUIA_Group_Horiz
, FALSE
,
198 MUIA_CycleChain
, cyclechain
,
201 Child
, (IPTR
) (group
= GroupObject
,
204 MUIA_Group_LayoutHook
, (IPTR
) layout_hook
,
206 Child
, (IPTR
) (vert
=
207 ScrollbarObject
, MUIA_Group_Horiz
, FALSE
, End
), End
),
212 mui_free(layout_hook
);
216 data
= INST_DATA(cl
, obj
);
217 layout_hook
->h_Data
= data
;
221 data
->layout_hook
= layout_hook
;
223 data
->hook
.h_Entry
= HookEntry
;
224 data
->hook
.h_SubEntry
= (HOOKFUNC
) Listview_Function
;
225 data
->hook
.h_Data
= data
;
227 data
->selfnotify_hook
.h_Entry
= HookEntry
;
228 data
->selfnotify_hook
.h_SubEntry
= (HOOKFUNC
) SelfNotify_Function
;
229 data
->selfnotify_hook
.h_Data
= data
;
230 data
->noforward
= FALSE
;
232 data
->last_active
= -1;
234 data
->ehn
.ehn_Events
= IDCMP_MOUSEBUTTONS
| IDCMP_RAWKEY
;
235 data
->ehn
.ehn_Priority
= 0;
236 data
->ehn
.ehn_Flags
= 0;
237 data
->ehn
.ehn_Object
= obj
;
238 data
->ehn
.ehn_Class
= cl
;
240 /* parse initial taglist */
241 for (tags
= msg
->ops_AttrList
; (tag
= NextTagItem(&tags
));)
245 case MUIA_Listview_DoubleClick
:
246 data
->doubleclick
= tag
->ti_Data
!= 0;
248 case MUIA_Listview_Input
:
249 data
->read_only
= !tag
->ti_Data
;
251 case MUIA_Listview_MultiSelect
:
252 data
->multiselect
= tag
->ti_Data
;
257 get(list
, MUIA_List_VertProp_First
, &first
);
258 get(list
, MUIA_List_VertProp_Visible
, &visible
);
259 get(list
, MUIA_List_VertProp_Entries
, &entries
);
262 ("[ListView 0x%p] List 0x%p, First %ld, Visible %ld, Entries %ld\n",
263 obj
, list
, first
, visible
, entries
));
266 MUIA_Prop_First
, first
,
267 MUIA_Prop_Visible
, visible
, MUIA_Prop_Entries
, entries
, TAG_DONE
);
269 DoMethod(vert
, MUIM_Notify
, MUIA_Prop_First
, MUIV_EveryTime
, (IPTR
) obj
,
270 4, MUIM_CallHook
, (IPTR
) &data
->hook
, PROP_VERT_FIRST
,
272 DoMethod(list
, MUIM_Notify
, MUIA_List_VertProp_First
, MUIV_EveryTime
,
273 (IPTR
) obj
, 4, MUIM_CallHook
, (IPTR
) &data
->hook
, LIST_VERT_FIRST
,
275 DoMethod(list
, MUIM_Notify
, MUIA_List_VertProp_Visible
, MUIV_EveryTime
,
276 (IPTR
) obj
, 4, MUIM_CallHook
, (IPTR
) &data
->hook
,
277 LIST_VERT_VISIBLE
, MUIV_TriggerValue
);
278 DoMethod(list
, MUIM_Notify
, MUIA_List_VertProp_Entries
, MUIV_EveryTime
,
279 (IPTR
) obj
, 4, MUIM_CallHook
, (IPTR
) &data
->hook
,
280 LIST_VERT_ENTRIES
, MUIV_TriggerValue
);
281 DoMethod(list
, MUIM_Notify
, MUIA_List_Active
, MUIV_EveryTime
,
282 (IPTR
) obj
, 4, MUIM_CallHook
, (IPTR
) &data
->selfnotify_hook
,
283 MUIA_List_Active
, MUIV_TriggerValue
);
288 /**************************************************************************
290 **************************************************************************/
291 IPTR
Listview__OM_DISPOSE(struct IClass
*cl
, Object
*obj
, Msg msg
)
293 struct MUI_ListviewData
*data
= INST_DATA(cl
, obj
);
295 mui_free(data
->layout_hook
); /* is always here */
296 return DoSuperMethodA(cl
, obj
, msg
);
299 /**************************************************************************
301 **************************************************************************/
302 void Listview__OM_SET(struct IClass
*cl
, Object
*obj
, struct opSet
*msg
)
304 struct TagItem
*tag
, *tags
;
305 IPTR no_notify
= GetTagData(MUIA_NoNotify
, FALSE
, msg
->ops_AttrList
);
306 struct MUI_ListviewData
*data
= INST_DATA(cl
, obj
);
310 DoSuperMethodA(cl
, obj
, (Msg
) msg
);
314 for (tags
= msg
->ops_AttrList
; (tag
= NextTagItem(&tags
));)
318 case MUIA_List_CompareHook
:
319 case MUIA_List_ConstructHook
:
320 case MUIA_List_DestructHook
:
321 case MUIA_List_DisplayHook
:
322 case MUIA_List_VertProp_First
:
323 case MUIA_List_Format
:
324 case MUIA_List_VertProp_Entries
:
325 case MUIA_List_VertProp_Visible
:
326 case MUIA_List_Active
:
327 case MUIA_List_First
:
328 case MUIA_List_Visible
:
329 case MUIA_List_Entries
:
330 case MUIA_List_Quiet
:
331 SetAttrs(data
->list
, MUIA_NoNotify
, no_notify
, tag
->ti_Tag
,
332 tag
->ti_Data
, TAG_DONE
);
334 case MUIA_Listview_DoubleClick
:
335 data
->doubleclick
= tag
->ti_Data
!= 0;
341 /**************************************************************************
343 **************************************************************************/
344 IPTR
Listview__OM_GET(struct IClass
*cl
, Object
*obj
, struct opGet
*msg
)
346 /* small macro to simplify return value storage */
347 #define STORE *(msg->opg_Storage)
348 struct MUI_ListviewData
*data
= INST_DATA(cl
, obj
);
350 switch (msg
->opg_AttrID
)
352 case MUIA_List_CompareHook
:
353 case MUIA_List_ConstructHook
:
354 case MUIA_List_DestructHook
:
355 case MUIA_List_DisplayHook
:
356 case MUIA_List_VertProp_First
:
357 case MUIA_List_Format
:
358 case MUIA_List_VertProp_Entries
:
359 case MUIA_List_VertProp_Visible
:
360 case MUIA_List_Active
:
361 case MUIA_List_First
:
362 case MUIA_List_Visible
:
363 case MUIA_List_Entries
:
364 case MUIA_List_Quiet
:
365 return GetAttr(msg
->opg_AttrID
, data
->list
, msg
->opg_Storage
);
366 case MUIA_Listview_DoubleClick
:
367 STORE
= data
->doubleclick
;
369 case MUIA_Listview_ClickColumn
:
370 STORE
= data
->click_column
;
372 case MUIA_Listview_List
:
373 STORE
= (IPTR
) data
->list
;
377 return DoSuperMethodA(cl
, obj
, (Msg
) msg
);
381 /**************************************************************************
383 **************************************************************************/
384 IPTR
Listview__MUIM_Setup(struct IClass
*cl
, Object
*obj
,
385 struct MUIP_Setup
*msg
)
387 struct MUI_ListviewData
*data
= INST_DATA(cl
, obj
);
389 if (!DoSuperMethodA(cl
, obj
, (Msg
) msg
))
392 data
->prefs_multi
= muiGlobalInfo(obj
)->mgi_Prefs
->list_multi
;
393 if (data
->multiselect
== MUIV_Listview_MultiSelect_Default
)
395 if (data
->prefs_multi
== LISTVIEW_MULTI_SHIFTED
)
396 data
->multiselect
= MUIV_Listview_MultiSelect_Shifted
;
398 data
->multiselect
= MUIV_Listview_MultiSelect_Always
;
401 DoMethod(_win(obj
), MUIM_Window_AddEventHandler
, (IPTR
) &data
->ehn
);
406 /**************************************************************************
408 **************************************************************************/
409 IPTR
Listview__MUIM_Cleanup(struct IClass
*cl
, Object
*obj
,
410 struct MUIP_Cleanup
*msg
)
412 struct MUI_ListviewData
*data
= INST_DATA(cl
, obj
);
414 DoMethod(_win(obj
), MUIM_Window_RemEventHandler
, (IPTR
) &data
->ehn
);
415 data
->mouse_click
= 0;
417 return DoSuperMethodA(cl
, obj
, (Msg
) msg
);
420 /**************************************************************************
422 **************************************************************************/
423 IPTR
Listview__MUIM_HandleEvent(struct IClass
*cl
, Object
*obj
,
424 struct MUIP_HandleEvent
*msg
)
426 struct MUI_ListviewData
*data
= INST_DATA(cl
, obj
);
427 Object
*list
= data
->list
;
428 struct MUI_List_TestPos_Result pos
;
429 LONG seltype
, old_active
, new_active
, visible
;
431 BOOL select
= FALSE
, multiselect
= FALSE
, clear
= FALSE
;
433 typeof(msg
->muikey
) muikey
= msg
->muikey
;
435 new_active
= old_active
= XGET(list
, MUIA_List_Active
);
436 visible
= XGET(list
, MUIA_List_Visible
);
438 if (muikey
!= MUIKEY_NONE
)
440 result
= MUI_EventHandlerRC_Eat
;
442 /* Make keys behave differently in read-only mode */
448 muikey
= MUIKEY_LINESTART
;
452 muikey
= MUIKEY_LINEEND
;
456 muikey
= MUIKEY_LEFT
;
461 muikey
= MUIKEY_RIGHT
;
469 if (data
->multiselect
!= MUIV_Listview_MultiSelect_None
474 seltype
= MUIV_List_Select_Toggle
;
475 data
->click_column
= data
->def_click_column
;
476 new_active
= MUIV_List_Active_Down
;
479 DoMethod(list
, MUIM_List_Jump
, 0);
483 new_active
= MUIV_List_Active_Top
;
487 new_active
= MUIV_List_Active_Bottom
;
491 case MUIKEY_WORDLEFT
:
492 DoMethod(list
, MUIM_List_Jump
, MUIV_List_Jump_Up
);
496 case MUIKEY_WORDRIGHT
:
497 DoMethod(list
, MUIM_List_Jump
, MUIV_List_Jump_Down
);
500 case MUIKEY_LINESTART
:
501 DoMethod(list
, MUIM_List_Jump
, MUIV_List_Jump_Top
);
505 DoMethod(list
, MUIM_List_Jump
, MUIV_List_Jump_Bottom
);
509 new_active
= MUIV_List_Active_Up
;
513 new_active
= MUIV_List_Active_Down
;
518 DoWheelMove(cl
, obj
, -visible
);
520 new_active
= MUIV_List_Active_PageUp
;
523 case MUIKEY_PAGEDOWN
:
525 DoWheelMove(cl
, obj
, visible
);
527 new_active
= MUIV_List_Active_PageDown
;
534 if (new_active
!= old_active
)
537 data
->multiselect
== MUIV_Listview_MultiSelect_None
;
538 seltype
= MUIV_List_Select_On
;
543 DoMethod(list
, MUIM_List_TestPos
, msg
->imsg
->MouseX
,
544 msg
->imsg
->MouseY
, (IPTR
) &pos
);
546 switch (msg
->imsg
->Class
)
548 case IDCMP_MOUSEBUTTONS
:
549 if (msg
->imsg
->Code
== SELECTDOWN
)
551 if (_isinobject(list
, msg
->imsg
->MouseX
, msg
->imsg
->MouseY
))
553 data
->mouse_click
= MOUSE_CLICK_ENTRY
;
555 if (!data
->read_only
)
557 new_active
= pos
.entry
;
560 data
->multiselect
== MUIV_Listview_MultiSelect_None
561 || (data
->multiselect
562 == MUIV_Listview_MultiSelect_Shifted
563 && (msg
->imsg
->Qualifier
564 & (IEQUALIFIER_LSHIFT
| IEQUALIFIER_RSHIFT
)) == 0);
566 MUIV_List_Select_On
: MUIV_List_Select_Toggle
;
567 select
= new_active
!= old_active
569 != MUIV_Listview_MultiSelect_None
;
571 /* Handle MUIA_Listview_ClickColumn */
572 data
->click_column
= pos
.column
;
574 if (data
->last_active
== pos
.entry
575 && DoubleClick(data
->last_secs
, data
->last_mics
,
576 msg
->imsg
->Seconds
, msg
->imsg
->Micros
))
578 set(obj
, MUIA_Listview_DoubleClick
, TRUE
);
579 data
->last_active
= -1;
580 data
->last_secs
= data
->last_mics
= 0;
584 data
->last_active
= pos
.entry
;
585 data
->last_secs
= msg
->imsg
->Seconds
;
586 data
->last_mics
= msg
->imsg
->Micros
;
589 DoMethod(_win(list
), MUIM_Window_RemEventHandler
,
591 data
->ehn
.ehn_Events
|=
592 (IDCMP_MOUSEMOVE
| IDCMP_INTUITICKS
);
593 DoMethod(_win(list
), MUIM_Window_AddEventHandler
,
600 if (msg
->imsg
->Code
== SELECTUP
&& data
->mouse_click
)
602 set(_win(obj
), MUIA_Window_ActiveObject
, (IPTR
)obj
);
603 data
->mouse_click
= 0;
606 DoMethod(_win(list
), MUIM_Window_RemEventHandler
,
608 data
->ehn
.ehn_Events
&=
609 ~(IDCMP_MOUSEMOVE
| IDCMP_INTUITICKS
);
610 DoMethod(_win(list
), MUIM_Window_AddEventHandler
,
615 case IDCMP_MOUSEMOVE
:
616 case IDCMP_INTUITICKS
:
617 if (pos
.flags
& MUI_LPR_ABOVE
)
618 new_active
= MUIV_List_Active_Up
;
619 else if (pos
.flags
& MUI_LPR_BELOW
)
620 new_active
= MUIV_List_Active_Down
;
622 new_active
= pos
.entry
;
624 select
= new_active
!= old_active
;
628 clear
= data
->multiselect
== MUIV_Listview_MultiSelect_None
;
630 seltype
= MUIV_List_Select_On
;
632 DoMethod(list
, MUIM_List_Select
, MUIV_List_Select_Active
,
633 MUIV_List_Select_Ask
, &seltype
);
638 if (_isinobject(data
->vert
, msg
->imsg
->MouseX
, msg
->imsg
->MouseY
))
640 else if (_isinobject(list
, msg
->imsg
->MouseX
, msg
->imsg
->MouseY
))
647 switch (msg
->imsg
->Code
)
649 case RAWKEY_NM_WHEEL_UP
:
650 DoWheelMove(cl
, obj
, -delta
);
653 case RAWKEY_NM_WHEEL_DOWN
:
654 DoWheelMove(cl
, obj
, delta
);
657 result
= MUI_EventHandlerRC_Eat
;
663 if (select
|| new_active
!= old_active
)
665 multiselect
= multiselect
666 || data
->multiselect
!= MUIV_Listview_MultiSelect_None
;
670 DoMethod(list
, MUIM_List_Select
, multiselect
?
671 MUIV_List_Select_All
: MUIV_List_Select_Active
,
672 MUIV_List_Select_Off
, NULL
);
674 if (multiselect
&& muikey
== MUIKEY_TOGGLE
)
676 DoMethod(list
, MUIM_List_Select
,
677 MUIV_List_Select_Active
,
678 MUIV_List_Select_Toggle
, NULL
);
681 if (new_active
!= old_active
)
682 set(list
, MUIA_List_Active
, new_active
);
685 DoMethod(list
, MUIM_List_Select
, MUIV_List_Select_Active
,
692 static void DoWheelMove(struct IClass
*cl
, Object
*obj
, LONG wheely
)
694 struct MUI_ListviewData
*data
= INST_DATA(cl
, obj
);
695 LONG
new, first
, entries
, visible
;
697 new = first
= XGET(data
->list
, MUIA_List_First
);
698 entries
= XGET(data
->list
, MUIA_List_Entries
);
699 visible
= XGET(data
->list
, MUIA_List_Visible
);
703 if (new > entries
- visible
)
705 new = entries
- visible
;
715 set(data
->list
, MUIA_List_First
, new);
719 BOOPSI_DISPATCHER(IPTR
, Listview_Dispatcher
, cl
, obj
, msg
)
721 switch (msg
->MethodID
)
724 Listview__OM_SET(cl
, obj
, (struct opSet
*)msg
);
727 return Listview__OM_GET(cl
, obj
, (struct opGet
*)msg
);
729 return Listview__OM_NEW(cl
, obj
, (struct opSet
*)msg
);
731 return Listview__OM_DISPOSE(cl
, obj
, msg
);
733 return Listview__MUIM_Setup(cl
, obj
, (struct MUIP_Setup
*)msg
);
735 return Listview__MUIM_Cleanup(cl
, obj
, (struct MUIP_Cleanup
*)msg
);
736 case MUIM_HandleEvent
:
737 return Listview__MUIM_HandleEvent(cl
, obj
,
738 (struct MUIP_HandleEvent
*)msg
);
739 case MUIM_List_Clear
:
740 case MUIM_List_CreateImage
:
741 case MUIM_List_DeleteImage
:
742 case MUIM_List_Exchange
:
743 case MUIM_List_GetEntry
:
744 case MUIM_List_Insert
:
745 case MUIM_List_InsertSingle
:
747 case MUIM_List_NextSelected
:
748 case MUIM_List_Redraw
:
749 case MUIM_List_Remove
:
750 case MUIM_List_Select
:
752 case MUIM_List_TestPos
:
754 struct MUI_ListviewData
*data
= INST_DATA(cl
, obj
);
756 return DoMethodA(data
->list
, msg
);
761 return DoSuperMethodA(cl
, obj
, msg
);
763 BOOPSI_DISPATCHER_END
768 const struct __MUIBuiltinClass _MUI_Listview_desc
=
772 sizeof(struct MUI_ListviewData
),
773 (void *) Listview_Dispatcher