Text.mui cleanup:
[AROS.git] / workbench / libs / muimaster / classes / listview.c
blob54ebb6832bee762a3f5a9c4ccf7bda14b559ca64
1 /*
2 Copyright © 2002-2014, 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 (IPTR) GetTagData(MUIA_CycleChain, (IPTR) 0, msg->ops_AttrList);
121 LONG entries = 0, first = 0, visible = 0;
123 if (!list)
124 return (IPTR) NULL;
126 vert = ScrollbarObject, MUIA_Group_Horiz, FALSE, End;
128 obj = (Object *) DoSuperNewTags(cl, obj, NULL,
129 MUIA_Group_Horiz, FALSE,
130 MUIA_CycleChain, cyclechain,
131 MUIA_InnerLeft, 0,
132 MUIA_InnerRight, 0,
133 Child, (IPTR) (group = HGroup,
134 MUIA_InnerLeft, 0,
135 MUIA_InnerRight, 0,
136 MUIA_Group_Spacing, 0,
137 End),
138 TAG_DONE);
140 if (!obj)
141 return (IPTR) NULL;
143 data = INST_DATA(cl, obj);
144 data->list = list;
145 data->vert = vert;
146 data->group = group;
148 data->hook.h_Entry = HookEntry;
149 data->hook.h_SubEntry = (HOOKFUNC) Listview_Function;
150 data->hook.h_Data = data;
152 data->selfnotify_hook.h_Entry = HookEntry;
153 data->selfnotify_hook.h_SubEntry = (HOOKFUNC) SelfNotify_Function;
154 data->selfnotify_hook.h_Data = data;
155 data->noforward = FALSE;
157 data->last_active = -1;
159 data->ehn.ehn_Events = IDCMP_MOUSEBUTTONS | IDCMP_RAWKEY;
160 data->ehn.ehn_Priority = 0;
161 data->ehn.ehn_Flags = 0;
162 data->ehn.ehn_Object = obj;
163 data->ehn.ehn_Class = cl;
165 /* parse initial taglist */
166 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
168 switch (tag->ti_Tag)
170 case MUIA_Listview_DoubleClick:
171 data->doubleclick = tag->ti_Data != 0;
172 break;
173 case MUIA_Listview_Input:
174 data->read_only = !tag->ti_Data;
175 break;
176 case MUIA_Listview_MultiSelect:
177 data->multiselect = tag->ti_Data;
178 break;
179 case MUIA_Listview_ScrollerPos:
180 data->scroller_pos = tag->ti_Data;
181 break;
185 /* Add list and/or scroller */
186 switch (data->scroller_pos)
188 case MUIV_Listview_ScrollerPos_None:
189 DoMethod(group, OM_ADDMEMBER, list);
190 break;
191 case MUIV_Listview_ScrollerPos_Left:
192 DoMethod(group, OM_ADDMEMBER, vert);
193 DoMethod(group, OM_ADDMEMBER, list);
194 break;
195 default:
196 DoMethod(group, OM_ADDMEMBER, list);
197 DoMethod(group, OM_ADDMEMBER, vert);
198 break;
201 get(list, MUIA_List_VertProp_First, &first);
202 get(list, MUIA_List_VertProp_Visible, &visible);
203 get(list, MUIA_List_VertProp_Entries, &entries);
205 D(bug
206 ("[ListView 0x%p] List 0x%p, First %ld, Visible %ld, Entries %ld\n",
207 obj, list, first, visible, entries));
209 SetAttrs(data->vert,
210 MUIA_Prop_First, first,
211 MUIA_Prop_Visible, visible, MUIA_Prop_Entries, entries, TAG_DONE);
213 DoMethod(vert, MUIM_Notify, MUIA_Prop_First, MUIV_EveryTime, (IPTR) obj,
214 4, MUIM_CallHook, (IPTR) &data->hook, PROP_VERT_FIRST,
215 MUIV_TriggerValue);
216 DoMethod(list, MUIM_Notify, MUIA_List_VertProp_First, MUIV_EveryTime,
217 (IPTR) obj, 4, MUIM_CallHook, (IPTR) &data->hook, LIST_VERT_FIRST,
218 MUIV_TriggerValue);
219 DoMethod(list, MUIM_Notify, MUIA_List_VertProp_Visible, MUIV_EveryTime,
220 (IPTR) obj, 4, MUIM_CallHook, (IPTR) &data->hook,
221 LIST_VERT_VISIBLE, MUIV_TriggerValue);
222 DoMethod(list, MUIM_Notify, MUIA_List_VertProp_Entries, MUIV_EveryTime,
223 (IPTR) obj, 4, MUIM_CallHook, (IPTR) &data->hook,
224 LIST_VERT_ENTRIES, MUIV_TriggerValue);
225 DoMethod(list, MUIM_Notify, MUIA_List_Active, MUIV_EveryTime,
226 (IPTR) obj, 4, MUIM_CallHook, (IPTR) &data->selfnotify_hook,
227 MUIA_List_Active, MUIV_TriggerValue);
229 return (IPTR) obj;
232 /**************************************************************************
233 OM_DISPOSE
234 **************************************************************************/
235 IPTR Listview__OM_DISPOSE(struct IClass *cl, Object *obj, Msg msg)
237 return DoSuperMethodA(cl, obj, msg);
240 /**************************************************************************
241 OM_SET
242 **************************************************************************/
243 IPTR Listview__OM_SET(struct IClass *cl, Object *obj, struct opSet *msg)
245 struct TagItem *tag, *tags;
246 IPTR no_notify = GetTagData(MUIA_NoNotify, FALSE, msg->ops_AttrList);
247 struct MUI_ListviewData *data = INST_DATA(cl, obj);
249 if (data->noforward)
251 return DoSuperMethodA(cl, obj, (Msg) msg);
254 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
256 switch (tag->ti_Tag)
258 case MUIA_List_CompareHook:
259 case MUIA_List_ConstructHook:
260 case MUIA_List_DestructHook:
261 case MUIA_List_DisplayHook:
262 case MUIA_List_VertProp_First:
263 case MUIA_List_Format:
264 case MUIA_List_VertProp_Entries:
265 case MUIA_List_VertProp_Visible:
266 case MUIA_List_Active:
267 case MUIA_List_First:
268 case MUIA_List_Visible:
269 case MUIA_List_Entries:
270 case MUIA_List_Quiet:
271 SetAttrs(data->list, MUIA_NoNotify, no_notify, tag->ti_Tag,
272 tag->ti_Data, TAG_DONE);
273 break;
274 case MUIA_Listview_DoubleClick:
275 data->doubleclick = tag->ti_Data != 0;
276 break;
277 case MUIA_Listview_SelectChange:
278 data->select_change = tag->ti_Data != 0;
279 break;
283 return DoSuperMethodA(cl, obj, (Msg) msg);
286 /**************************************************************************
287 OM_GET
288 **************************************************************************/
289 IPTR Listview__OM_GET(struct IClass *cl, Object *obj, struct opGet *msg)
291 /* small macro to simplify return value storage */
292 #define STORE *(msg->opg_Storage)
293 struct MUI_ListviewData *data = INST_DATA(cl, obj);
295 switch (msg->opg_AttrID)
297 case MUIA_List_CompareHook:
298 case MUIA_List_ConstructHook:
299 case MUIA_List_DestructHook:
300 case MUIA_List_DisplayHook:
301 case MUIA_List_VertProp_First:
302 case MUIA_List_Format:
303 case MUIA_List_VertProp_Entries:
304 case MUIA_List_VertProp_Visible:
305 case MUIA_List_Active:
306 case MUIA_List_First:
307 case MUIA_List_Visible:
308 case MUIA_List_Entries:
309 case MUIA_List_Quiet:
310 return GetAttr(msg->opg_AttrID, data->list, msg->opg_Storage);
311 case MUIA_Listview_DoubleClick:
312 STORE = data->doubleclick;
313 return 1;
314 case MUIA_Listview_ClickColumn:
315 STORE = data->click_column;
316 return 1;
317 case MUIA_Listview_List:
318 STORE = (IPTR) data->list;
319 return 1;
320 case MUIA_Listview_SelectChange:
321 STORE = data->select_change;
322 return 1;
325 return DoSuperMethodA(cl, obj, (Msg) msg);
326 #undef STORE
329 /**************************************************************************
330 MUIM_Setup
331 **************************************************************************/
332 IPTR Listview__MUIM_Setup(struct IClass *cl, Object *obj,
333 struct MUIP_Setup *msg)
335 struct MUI_ListviewData *data = INST_DATA(cl, obj);
337 if (!DoSuperMethodA(cl, obj, (Msg) msg))
338 return 0;
340 data->prefs_multi = muiGlobalInfo(obj)->mgi_Prefs->list_multi;
341 if (data->multiselect == MUIV_Listview_MultiSelect_Default)
343 if (data->prefs_multi == LISTVIEW_MULTI_SHIFTED)
344 data->multiselect = MUIV_Listview_MultiSelect_Shifted;
345 else
346 data->multiselect = MUIV_Listview_MultiSelect_Always;
349 DoMethod(_win(obj), MUIM_Window_AddEventHandler, (IPTR) &data->ehn);
351 return 1;
354 /**************************************************************************
355 MUIM_Cleanup
356 **************************************************************************/
357 IPTR Listview__MUIM_Cleanup(struct IClass *cl, Object *obj,
358 struct MUIP_Cleanup *msg)
360 struct MUI_ListviewData *data = INST_DATA(cl, obj);
362 DoMethod(_win(obj), MUIM_Window_RemEventHandler, (IPTR) &data->ehn);
363 data->mouse_click = 0;
365 return DoSuperMethodA(cl, obj, (Msg) msg);
368 /**************************************************************************
369 MUIM_HandleEvent
370 **************************************************************************/
371 IPTR Listview__MUIM_HandleEvent(struct IClass *cl, Object *obj,
372 struct MUIP_HandleEvent *msg)
374 struct MUI_ListviewData *data = INST_DATA(cl, obj);
375 Object *list = data->list;
376 struct MUI_List_TestPos_Result pos;
377 LONG seltype, old_active, new_active, visible, first, last, i;
378 IPTR result = 0;
379 BOOL select = FALSE, clear = FALSE, range_select = FALSE;
380 WORD delta;
381 typeof(msg->muikey) muikey = msg->muikey;
383 new_active = old_active = XGET(list, MUIA_List_Active);
384 visible = XGET(list, MUIA_List_Visible);
386 if (muikey != MUIKEY_NONE)
388 result = MUI_EventHandlerRC_Eat;
390 /* Make keys behave differently in read-only mode */
391 if (data->read_only)
393 switch (muikey)
395 case MUIKEY_TOP:
396 muikey = MUIKEY_LINESTART;
397 break;
399 case MUIKEY_BOTTOM:
400 muikey = MUIKEY_LINEEND;
401 break;
403 case MUIKEY_UP:
404 muikey = MUIKEY_LEFT;
405 break;
407 case MUIKEY_DOWN:
408 case MUIKEY_PRESS:
409 muikey = MUIKEY_RIGHT;
410 break;
414 switch (muikey)
416 case MUIKEY_TOGGLE:
417 if (data->multiselect != MUIV_Listview_MultiSelect_None
418 && !data->read_only)
420 select = TRUE;
421 data->click_column = data->def_click_column;
422 new_active = MUIV_List_Active_Down;
424 else
426 DoMethod(list, MUIM_List_Jump, 0);
427 muikey = MUIKEY_NONE;
429 break;
431 case MUIKEY_TOP:
432 new_active = MUIV_List_Active_Top;
433 break;
435 case MUIKEY_BOTTOM:
436 new_active = MUIV_List_Active_Bottom;
437 break;
439 case MUIKEY_LEFT:
440 case MUIKEY_WORDLEFT:
441 DoMethod(list, MUIM_List_Jump, MUIV_List_Jump_Up);
442 break;
444 case MUIKEY_RIGHT:
445 case MUIKEY_WORDRIGHT:
446 DoMethod(list, MUIM_List_Jump, MUIV_List_Jump_Down);
447 break;
449 case MUIKEY_LINESTART:
450 DoMethod(list, MUIM_List_Jump, MUIV_List_Jump_Top);
451 break;
453 case MUIKEY_LINEEND:
454 DoMethod(list, MUIM_List_Jump, MUIV_List_Jump_Bottom);
455 break;
457 case MUIKEY_UP:
458 new_active = MUIV_List_Active_Up;
459 break;
461 case MUIKEY_DOWN:
462 new_active = MUIV_List_Active_Down;
463 break;
465 case MUIKEY_PAGEUP:
466 if (data->read_only)
467 DoWheelMove(cl, obj, -visible);
468 else
469 new_active = MUIV_List_Active_PageUp;
470 break;
472 case MUIKEY_PAGEDOWN:
473 if (data->read_only)
474 DoWheelMove(cl, obj, visible);
475 else
476 new_active = MUIV_List_Active_PageDown;
477 break;
479 default:
480 result = 0;
483 else if (msg->imsg)
485 DoMethod(list, MUIM_List_TestPos, msg->imsg->MouseX,
486 msg->imsg->MouseY, (IPTR) &pos);
488 switch (msg->imsg->Class)
490 case IDCMP_MOUSEBUTTONS:
491 if (msg->imsg->Code == SELECTDOWN)
493 if (_isinobject(list, msg->imsg->MouseX, msg->imsg->MouseY))
495 data->mouse_click = MOUSE_CLICK_ENTRY;
497 if (!data->read_only && pos.entry != -1)
499 new_active = pos.entry;
501 clear = (data->multiselect
502 == MUIV_Listview_MultiSelect_Shifted
503 && (msg->imsg->Qualifier
504 & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)) == 0);
505 seltype = clear ?
506 MUIV_List_Select_On: MUIV_List_Select_Toggle;
507 select = data->multiselect
508 != MUIV_Listview_MultiSelect_None;
510 /* Handle MUIA_Listview_ClickColumn */
511 data->click_column = pos.column;
513 /* Handle double clicking */
514 if (data->last_active == pos.entry
515 && DoubleClick(data->last_secs, data->last_mics,
516 msg->imsg->Seconds, msg->imsg->Micros))
518 set(obj, MUIA_Listview_DoubleClick, TRUE);
519 data->last_active = -1;
520 data->last_secs = data->last_mics = 0;
522 else
524 data->last_active = pos.entry;
525 data->last_secs = msg->imsg->Seconds;
526 data->last_mics = msg->imsg->Micros;
529 /* Look out for mouse movement and timer events while
530 mouse button is down */
531 DoMethod(_win(list), MUIM_Window_RemEventHandler,
532 (IPTR) &data->ehn);
533 data->ehn.ehn_Events |=
534 (IDCMP_MOUSEMOVE | IDCMP_INTUITICKS);
535 DoMethod(_win(list), MUIM_Window_AddEventHandler,
536 (IPTR) &data->ehn);
540 else
542 /* Activate object */
543 if (msg->imsg->Code == SELECTUP && data->mouse_click)
545 set(_win(obj), MUIA_Window_ActiveObject, (IPTR)obj);
546 data->mouse_click = 0;
549 /* Restore normal event mask */
550 DoMethod(_win(list), MUIM_Window_RemEventHandler,
551 (IPTR) &data->ehn);
552 data->ehn.ehn_Events &=
553 ~(IDCMP_MOUSEMOVE | IDCMP_INTUITICKS);
554 DoMethod(_win(list), MUIM_Window_AddEventHandler,
555 (IPTR) &data->ehn);
557 break;
559 case IDCMP_MOUSEMOVE:
560 case IDCMP_INTUITICKS:
561 if (pos.flags & MUI_LPR_ABOVE)
562 new_active = MUIV_List_Active_Up;
563 else if (pos.flags & MUI_LPR_BELOW)
564 new_active = MUIV_List_Active_Down;
565 else
566 new_active = pos.entry;
568 select = new_active != old_active
569 && data->multiselect != MUIV_Listview_MultiSelect_None;
570 if (select)
572 DoMethod(list, MUIM_List_Select, MUIV_List_Select_Active,
573 MUIV_List_Select_Ask, &seltype);
574 range_select = new_active >= 0;
577 break;
579 case IDCMP_RAWKEY:
580 /* Scroll wheel */
581 if (_isinobject(data->vert, msg->imsg->MouseX, msg->imsg->MouseY))
582 delta = 1;
583 else if (_isinobject(list, msg->imsg->MouseX, msg->imsg->MouseY))
584 delta = 4;
585 else
586 delta = 0;
588 if (delta != 0)
590 switch (msg->imsg->Code)
592 case RAWKEY_NM_WHEEL_UP:
593 DoWheelMove(cl, obj, -delta);
594 break;
596 case RAWKEY_NM_WHEEL_DOWN:
597 DoWheelMove(cl, obj, delta);
598 break;
600 result = MUI_EventHandlerRC_Eat;
602 break;
606 /* Change selected and active entries */
607 set(obj, MUIA_Listview_SelectChange, TRUE);
608 if (clear)
610 DoMethod(list, MUIM_List_Select, MUIV_List_Select_All,
611 MUIV_List_Select_Off, NULL);
614 if (muikey == MUIKEY_TOGGLE)
616 DoMethod(list, MUIM_List_Select,
617 MUIV_List_Select_Active,
618 MUIV_List_Select_Toggle, NULL);
619 select = FALSE;
622 if (new_active != old_active)
623 set(list, MUIA_List_Active, new_active);
625 if (select)
627 if (range_select)
629 if (old_active < new_active)
630 first = old_active + 1, last = new_active;
631 else
632 first = new_active, last = old_active - 1;
633 for (i = first; i <= last; i++)
634 DoMethod(list, MUIM_List_Select, i, seltype, NULL);
636 else
637 DoMethod(list, MUIM_List_Select, MUIV_List_Select_Active, seltype,
638 NULL);
640 set(obj, MUIA_Listview_SelectChange, FALSE);
642 return result;
645 static void DoWheelMove(struct IClass *cl, Object *obj, LONG wheely)
647 struct MUI_ListviewData *data = INST_DATA(cl, obj);
648 LONG new, first, entries, visible;
650 new = first = XGET(data->list, MUIA_List_First);
651 entries = XGET(data->list, MUIA_List_Entries);
652 visible = XGET(data->list, MUIA_List_Visible);
654 new += wheely;
656 if (new > entries - visible)
658 new = entries - visible;
661 if (new < 0)
663 new = 0;
666 if (new != first)
668 set(data->list, MUIA_List_First, new);
672 BOOPSI_DISPATCHER(IPTR, Listview_Dispatcher, cl, obj, msg)
674 switch (msg->MethodID)
676 case OM_SET:
677 return Listview__OM_SET(cl, obj, (struct opSet *)msg);
678 case OM_GET:
679 return Listview__OM_GET(cl, obj, (struct opGet *)msg);
680 case OM_NEW:
681 return Listview__OM_NEW(cl, obj, (struct opSet *)msg);
682 case OM_DISPOSE:
683 return Listview__OM_DISPOSE(cl, obj, msg);
684 case MUIM_Setup:
685 return Listview__MUIM_Setup(cl, obj, (struct MUIP_Setup *)msg);
686 case MUIM_Cleanup:
687 return Listview__MUIM_Cleanup(cl, obj, (struct MUIP_Cleanup *)msg);
688 case MUIM_HandleEvent:
689 return Listview__MUIM_HandleEvent(cl, obj,
690 (struct MUIP_HandleEvent *)msg);
691 case MUIM_List_Clear:
692 case MUIM_List_CreateImage:
693 case MUIM_List_DeleteImage:
694 case MUIM_List_Exchange:
695 case MUIM_List_GetEntry:
696 case MUIM_List_Insert:
697 case MUIM_List_InsertSingle:
698 case MUIM_List_Jump:
699 case MUIM_List_NextSelected:
700 case MUIM_List_Redraw:
701 case MUIM_List_Remove:
702 case MUIM_List_Select:
703 case MUIM_List_Sort:
704 case MUIM_List_TestPos:
706 struct MUI_ListviewData *data = INST_DATA(cl, obj);
708 return DoMethodA(data->list, msg);
713 return DoSuperMethodA(cl, obj, msg);
715 BOOPSI_DISPATCHER_END
718 * Class descriptor.
720 const struct __MUIBuiltinClass _MUI_Listview_desc =
722 MUIC_Listview,
723 MUIC_Group,
724 sizeof(struct MUI_ListviewData),
725 (void *) Listview_Dispatcher