2 Copyright © 2002-2015, 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
;
31 struct Hook selfnotify_hook
;
37 struct MUI_EventHandlerNode ehn
;
39 int mouse_click
; /* see below if mouse is held down */
49 LONG def_click_column
;
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
,
77 get(data
->vert
, MUIA_Prop_First
, &val
);
78 nnset(data
->list
, MUIA_List_VertProp_First
, val
);
82 nnset(data
->vert
, MUIA_Prop_First
, val
);
84 case LIST_VERT_VISIBLE
:
85 nnset(data
->vert
, MUIA_Prop_Visible
, val
);
87 case LIST_VERT_ENTRIES
:
88 nnset(data
->vert
, MUIA_Prop_Entries
, val
);
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
;
108 /**************************************************************************
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
;
117 (Object
*) GetTagData(MUIA_Listview_List
, (IPTR
) NULL
,
120 LONG entries
= 0, first
= 0, visible
= 0;
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
,
135 Child
, (IPTR
) (group
= HGroup
,
138 MUIA_Group_Spacing
, 0,
145 data
= INST_DATA(cl
, obj
);
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
));)
172 case MUIA_Listview_DoubleClick
:
173 data
->doubleclick
= tag
->ti_Data
!= 0;
175 case MUIA_Listview_Input
:
176 data
->read_only
= !tag
->ti_Data
;
178 case MUIA_Listview_MultiSelect
:
179 data
->multiselect
= tag
->ti_Data
;
181 case MUIA_Listview_ScrollerPos
:
182 data
->scroller_pos
= tag
->ti_Data
;
187 /* Add list and/or scroller */
188 switch (data
->scroller_pos
)
190 case MUIV_Listview_ScrollerPos_None
:
191 DoMethod(group
, OM_ADDMEMBER
, list
);
193 case MUIV_Listview_ScrollerPos_Left
:
194 DoMethod(group
, OM_ADDMEMBER
, vert
);
195 DoMethod(group
, OM_ADDMEMBER
, list
);
198 DoMethod(group
, OM_ADDMEMBER
, list
);
199 DoMethod(group
, OM_ADDMEMBER
, vert
);
203 get(list
, MUIA_List_VertProp_First
, &first
);
204 get(list
, MUIA_List_VertProp_Visible
, &visible
);
205 get(list
, MUIA_List_VertProp_Entries
, &entries
);
208 ("[ListView 0x%p] List 0x%p, First %ld, Visible %ld, Entries %ld\n",
209 obj
, list
, first
, visible
, entries
));
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
,
218 DoMethod(list
, MUIM_Notify
, MUIA_List_VertProp_First
, MUIV_EveryTime
,
219 (IPTR
) obj
, 4, MUIM_CallHook
, (IPTR
) &data
->hook
, LIST_VERT_FIRST
,
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
);
234 /**************************************************************************
236 **************************************************************************/
237 IPTR
Listview__OM_DISPOSE(struct IClass
*cl
, Object
*obj
, Msg msg
)
239 return DoSuperMethodA(cl
, obj
, msg
);
242 /**************************************************************************
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
);
253 return DoSuperMethodA(cl
, obj
, (Msg
) msg
);
256 for (tags
= msg
->ops_AttrList
; (tag
= NextTagItem(&tags
));)
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
);
276 case MUIA_Listview_DoubleClick
:
277 data
->doubleclick
= tag
->ti_Data
!= 0;
279 case MUIA_Listview_SelectChange
:
280 data
->select_change
= tag
->ti_Data
!= 0;
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
,
287 data
->ehn
.ehn_Events
&=
288 ~(IDCMP_MOUSEMOVE
| IDCMP_INTUITICKS
| IDCMP_INACTIVEWINDOW
);
289 DoMethod(_win(obj
), MUIM_Window_AddEventHandler
,
295 return DoSuperMethodA(cl
, obj
, (Msg
) msg
);
298 /**************************************************************************
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
;
326 case MUIA_Listview_ClickColumn
:
327 STORE
= data
->click_column
;
329 case MUIA_Listview_List
:
330 STORE
= (IPTR
) data
->list
;
332 case MUIA_Listview_SelectChange
:
333 STORE
= data
->select_change
;
337 return DoSuperMethodA(cl
, obj
, (Msg
) msg
);
341 /**************************************************************************
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
))
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
;
358 data
->multiselect
= MUIV_Listview_MultiSelect_Always
;
361 DoMethod(_win(obj
), MUIM_Window_AddEventHandler
, (IPTR
) &data
->ehn
);
366 /**************************************************************************
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 /**************************************************************************
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
;
391 BOOL select
= FALSE
, clear
= FALSE
, range_select
= FALSE
, changing
;
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 */
408 muikey
= MUIKEY_LINESTART
;
412 muikey
= MUIKEY_LINEEND
;
416 muikey
= MUIKEY_LEFT
;
421 muikey
= MUIKEY_RIGHT
;
429 if (data
->multiselect
!= MUIV_Listview_MultiSelect_None
433 data
->click_column
= data
->def_click_column
;
434 new_active
= MUIV_List_Active_Down
;
438 DoMethod(list
, MUIM_List_Jump
, 0);
439 muikey
= MUIKEY_NONE
;
444 new_active
= MUIV_List_Active_Top
;
448 new_active
= MUIV_List_Active_Bottom
;
452 case MUIKEY_WORDLEFT
:
453 DoMethod(list
, MUIM_List_Jump
, MUIV_List_Jump_Up
);
457 case MUIKEY_WORDRIGHT
:
458 DoMethod(list
, MUIM_List_Jump
, MUIV_List_Jump_Down
);
461 case MUIKEY_LINESTART
:
462 DoMethod(list
, MUIM_List_Jump
, MUIV_List_Jump_Top
);
466 DoMethod(list
, MUIM_List_Jump
, MUIV_List_Jump_Bottom
);
470 new_active
= MUIV_List_Active_Up
;
474 new_active
= MUIV_List_Active_Down
;
479 DoWheelMove(cl
, obj
, -visible
);
481 new_active
= MUIV_List_Active_PageUp
;
484 case MUIKEY_PAGEDOWN
:
486 DoWheelMove(cl
, obj
, visible
);
488 new_active
= MUIV_List_Active_PageDown
;
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);
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
,
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;
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
546 DoMethod(_win(obj
), MUIM_Window_RemEventHandler
,
548 data
->ehn
.ehn_Events
|= (IDCMP_MOUSEMOVE
549 | IDCMP_INTUITICKS
|IDCMP_INACTIVEWINDOW
);
550 DoMethod(_win(obj
), MUIM_Window_AddEventHandler
,
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
,
567 data
->ehn
.ehn_Events
&= ~(IDCMP_MOUSEMOVE
| IDCMP_INTUITICKS
568 | IDCMP_INACTIVEWINDOW
);
569 DoMethod(_win(obj
), MUIM_Window_AddEventHandler
,
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
;
581 new_active
= pos
.entry
;
583 select
= new_active
!= old_active
584 && data
->multiselect
!= MUIV_Listview_MultiSelect_None
;
587 DoMethod(list
, MUIM_List_Select
, MUIV_List_Select_Active
,
588 MUIV_List_Select_Ask
, &seltype
);
589 range_select
= new_active
>= 0;
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
,
599 data
->ehn
.ehn_Events
&=
600 ~(IDCMP_MOUSEMOVE
| IDCMP_INTUITICKS
| IDCMP_INACTIVEWINDOW
);
601 DoMethod(_win(obj
), MUIM_Window_AddEventHandler
,
607 if (_isinobject(data
->vert
, msg
->imsg
->MouseX
, msg
->imsg
->MouseY
))
609 else if (_isinobject(list
, msg
->imsg
->MouseX
, msg
->imsg
->MouseY
))
616 switch (msg
->imsg
->Code
)
618 case RAWKEY_NM_WHEEL_UP
:
619 DoWheelMove(cl
, obj
, -delta
);
622 case RAWKEY_NM_WHEEL_DOWN
:
623 DoWheelMove(cl
, obj
, delta
);
626 result
= MUI_EventHandlerRC_Eat
;
632 /* Decide in advance if any selections may change */
633 changing
= clear
|| muikey
== MUIKEY_TOGGLE
|| select
;
635 /* Change selected and active entries */
637 set(obj
, MUIA_Listview_SelectChange
, TRUE
);
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
);
653 if (new_active
!= old_active
)
654 set(list
, MUIA_List_Active
, new_active
);
660 if (old_active
< new_active
)
661 first
= old_active
+ 1, last
= new_active
;
663 first
= new_active
, last
= old_active
- 1;
664 for (i
= first
; i
<= last
; i
++)
665 DoMethod(list
, MUIM_List_Select
, i
, seltype
, NULL
);
668 DoMethod(list
, MUIM_List_Select
, MUIV_List_Select_Active
, seltype
,
673 set(obj
, MUIA_Listview_SelectChange
, FALSE
);
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
);
689 if (new > entries
- visible
)
691 new = entries
- visible
;
701 set(data
->list
, MUIA_List_First
, new);
705 BOOPSI_DISPATCHER(IPTR
, Listview_Dispatcher
, cl
, obj
, msg
)
707 switch (msg
->MethodID
)
710 return Listview__OM_SET(cl
, obj
, (struct opSet
*)msg
);
712 return Listview__OM_GET(cl
, obj
, (struct opGet
*)msg
);
714 return Listview__OM_NEW(cl
, obj
, (struct opSet
*)msg
);
716 return Listview__OM_DISPOSE(cl
, obj
, msg
);
718 return Listview__MUIM_Setup(cl
, obj
, (struct MUIP_Setup
*)msg
);
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
:
732 case MUIM_List_NextSelected
:
733 case MUIM_List_Redraw
:
734 case MUIM_List_Remove
:
735 case MUIM_List_Select
:
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
753 const struct __MUIBuiltinClass _MUI_Listview_desc
=
757 sizeof(struct MUI_ListviewData
),
758 (void *) Listview_Dispatcher