muimaster.library: support Listview_List in List
[AROS.git] / workbench / libs / muimaster / classes / list.c
blob0b7918939f33c3e7596cd7e9577c9ca15284180b
1 /*
2 Copyright © 2002-2015, The AROS Development Team. All rights reserved.
3 $Id$
4 */
6 #include <string.h>
7 #include <stdlib.h>
9 #include <exec/memory.h>
10 #include <graphics/gfx.h>
11 #include <graphics/gfxmacros.h>
12 #include <graphics/view.h>
13 #include <devices/rawkeycodes.h>
14 #include <clib/alib_protos.h>
15 #include <proto/exec.h>
16 #include <proto/graphics.h>
17 #include <proto/utility.h>
18 #include <proto/dos.h>
19 #include <proto/intuition.h>
20 #include <proto/muimaster.h>
22 /* #define MYDEBUG 1 */
23 #include "debug.h"
24 #include "mui.h"
25 #include "muimaster_intern.h"
26 #include "support.h"
27 #include "imspec.h"
28 #include "textengine.h"
29 #include "listimage.h"
30 #include "prefs.h"
32 extern struct Library *MUIMasterBase;
34 #define ENTRY_TITLE (-1)
36 #define FORMAT_TEMPLATE "DELTA=D/N,PREPARSE=P/K,WEIGHT=W/N,MINWIDTH=MIW/N," \
37 "MAXWIDTH=MAW/N,COL=C/N,BAR/S"
39 #define BAR_WIDTH 2
41 enum
43 ARG_DELTA,
44 ARG_PREPARSE,
45 ARG_WEIGHT,
46 ARG_MINWIDTH,
47 ARG_MAXWIDTH,
48 ARG_COL,
49 ARG_BAR,
50 ARG_CNT
54 struct ListEntry
56 APTR data;
57 LONG *widths; /* Widths of the columns */
58 LONG width; /* Line width */
59 LONG height; /* Line height */
60 WORD flags; /* see below */
63 #define ENTRY_SELECTED (1<<0)
66 struct ColumnInfo
68 int colno; /* Column number */
69 int user_width; /* user set width; -1 if entry width */
70 int min_width; /* min width percentage */
71 int max_width; /* min width percentage */
72 int weight;
73 int delta; /* ignored for the first and last column, defaults to 4 */
74 int bar;
75 STRPTR preparse;
76 int entries_width; /* width of the entries (maximum of all widths) */
79 struct MUI_ImageSpec_intern;
81 struct MUI_ListData
83 /* bool attrs */
84 ULONG flags;
86 APTR intern_pool; /* The internal pool which the class has allocated */
87 LONG intern_puddle_size;
88 LONG intern_thresh_size;
89 APTR pool; /* the pool which is used to allocate list entries */
91 struct Hook *construct_hook;
92 struct Hook *compare_hook;
93 struct Hook *destruct_hook;
94 struct Hook *display_hook;
95 struct Hook *multi_test_hook;
97 struct Hook default_compare_hook;
99 /* List management, currently we use a simple flat array, which is not
100 * good if many entries are inserted/deleted */
101 LONG entries_num; /* Number of Entries in the list */
102 LONG entries_allocated;
103 struct ListEntry **entries;
105 LONG entries_first; /* first visible entry */
106 LONG entries_visible; /* number of visible entries,
107 * determined at MUIM_Layout */
108 LONG entries_active;
109 LONG insert_position; /* pos of the last insertion */
111 LONG entry_maxheight; /* Maximum height of an entry */
112 ULONG entry_minheight; /* from MUIA_List_MinLineHeight */
114 LONG entries_totalheight;
115 LONG entries_maxwidth;
117 LONG vertprop_entries;
118 LONG vertprop_visible;
119 LONG vertprop_first;
121 LONG confirm_entries_num; /* These are the correct entries num, used
122 * so you cannot set MUIA_List_Entries to
123 * wrong values */
125 LONG entries_top_pixel; /* Where the entries start */
127 /* Column managment, is allocated by ParseListFormat() and freed
128 * by CleanListFormat() */
129 STRPTR format;
130 LONG columns; /* Number of columns the list has */
131 struct ColumnInfo *ci;
132 STRPTR *preparses;
133 STRPTR *strings; /* the strings for the display function, one
134 * more than needed (for the entry position) */
136 /* Titlestuff */
137 int title_height; /* The complete height of the title */
138 STRPTR title; /* On single column lists this is the title,
139 * otherwise 1. NULL for no title(s) */
141 /* Cursor images */
142 struct MUI_ImageSpec_intern *list_cursor;
143 struct MUI_ImageSpec_intern *list_select;
144 struct MUI_ImageSpec_intern *list_selcur;
146 /* Render optimization */
147 int update; /* 1 - update everything, 2 - redraw entry at update_pos,
148 * 3 - scroll to current entries_first (old value is in
149 * update_pos) */
150 int update_pos;
152 LONG drop_mark_y;
154 /* list images */
155 struct MinList images;
157 /* user prefs */
158 ListviewRefresh prefs_refresh;
159 UWORD prefs_linespacing;
160 BOOL prefs_smoothed;
161 UWORD prefs_smoothval;
163 /* render space handling */
164 Object *area;
165 BOOL area_replaced;
167 /***************************/
168 /* Former Listview members */
169 /***************************/
171 Object *vert;
172 IPTR scroller_pos;
173 BOOL read_only;
174 IPTR multiselect;
176 /* clicked column */
177 LONG click_column;
178 LONG def_click_column;
180 LONG mouse_click; /* see below if mouse is held down */
182 /* double click */
183 ULONG last_secs;
184 ULONG last_mics;
185 ULONG last_active;
187 struct MUI_EventHandlerNode ehn;
189 /* user prefs */
190 ListviewMulti prefs_multi;
192 BOOL select_change;
193 BOOL doubleclick;
198 #define MOUSE_CLICK_ENTRY 1 /* on entry clicked */
199 #define MOUSE_CLICK_TITLE 2 /* on title clicked */
201 #define LIST_ADJUSTWIDTH (1<<0)
202 #define LIST_ADJUSTHEIGHT (1<<1)
203 #define LIST_AUTOVISIBLE (1<<2)
204 #define LIST_DRAGSORTABLE (1<<3)
205 #define LIST_SHOWDROPMARKS (1<<4)
206 #define LIST_QUIET (1<<5)
209 /****** List.mui/MUIA_List_CompareHook ***************************************
211 * NAME
212 * MUIA_List_CompareHook -- (V4) [IS.], struct Hook *
214 * FUNCTION
215 * The provided hook indicates the sort ordering of two list entries.
216 * The hook receives list-entry data pointers as its second and third
217 * arguments. The hook should return a negative value if the first entry
218 * should be placed before the second entry, a positive value if the
219 * first entry should be placed after the second entry, and zero if the
220 * entries are equal.
222 * In addition to being used internally for sorting operations, this hook
223 * will be called when MUIM_List_Compare is externally invoked.
225 * If this attribute is not specified or is set to NULL, all list entries
226 * must be strings.
228 ******************************************************************************
232 /****** List.mui/MUIA_List_MultiTestHook *************************************
234 * NAME
235 * MUIA_List_MultiTestHook -- (V4) [IS.], struct Hook *
237 * FUNCTION
238 * The provided hook indicates whether a particular list entry
239 * may be multiselected. The hook receives the list-entry data pointer as
240 * its third argument, and returns a Boolean value. If this attribute is
241 * not specified or is set to NULL, all list entries are considered
242 * multi-selectable.
244 * Whenever an entry is about to be selected, this hook is called if
245 * there are other entries already selected. If the hook returns TRUE,
246 * the entry may be multi-selected; if the hook returns FALSE, the entry
247 * remains unselected.
249 * Additionally, if a non-multi-selectable entry has been selected (as
250 * the only selected entry in the list), any attempt to select an
251 * additional entry will fail.
253 ******************************************************************************
257 /**************************************************************************
258 Allocate a single list entry, does not initialize it (except the pointer)
259 **************************************************************************/
260 static struct ListEntry *AllocListEntry(struct MUI_ListData *data)
262 ULONG *mem;
263 struct ListEntry *le;
264 int size = sizeof(struct ListEntry) + sizeof(LONG) * data->columns + 4;
265 /* sizeinfo */
266 LONG j;
268 mem = AllocPooled(data->pool, size);
269 if (!mem)
270 return NULL;
271 D(bug("List AllocListEntry %p, %ld bytes\n", mem, size));
273 mem[0] = size; /* Save the size */
274 le = (struct ListEntry *)(mem + 1);
275 le->widths = (LONG *) (le + 1);
277 /* Initialize fields */
278 le->height = 0;
279 le->width = 0;
280 le->flags = 0;
281 for (j = 0; j < data->columns; j++)
282 le->widths[j] = 0;
284 return le;
287 /**************************************************************************
288 Deallocate a single list entry, does not deinitialize it
289 **************************************************************************/
290 static void FreeListEntry(struct MUI_ListData *data,
291 struct ListEntry *entry)
293 ULONG *mem = ((ULONG *) entry) - 1;
294 D(bug("FreeListEntry %p size=%ld\n", mem, mem[0]));
295 FreePooled(data->pool, mem, mem[0]);
298 /**************************************************************************
299 Ensures that there can be at least the given amount of entries within
300 the list. Returns 0 if not. It also allocates the space for the title.
301 It can be accessed with data->entries[ENTRY_TITLE]
302 **************************************************************************/
303 static int SetListSize(struct MUI_ListData *data, LONG size)
305 struct ListEntry **new_entries;
306 int new_entries_allocated;
308 if (size + 1 <= data->entries_allocated)
309 return 1;
311 new_entries_allocated = data->entries_allocated * 2 + 4;
312 if (new_entries_allocated < size + 1)
313 new_entries_allocated = size + 1 + 10; /* 10 is just random */
315 D(bug("List %p : SetListSize allocating %ld bytes\n", data,
316 new_entries_allocated * sizeof(struct ListEntry *)));
317 new_entries =
318 AllocVec(new_entries_allocated * sizeof(struct ListEntry *), 0);
319 if (NULL == new_entries)
320 return 0;
321 if (data->entries)
323 CopyMem(data->entries - 1, new_entries,
324 (data->entries_num + 1) * sizeof(struct ListEntry *));
325 FreeVec(data->entries - 1);
327 data->entries = new_entries + 1;
328 data->entries_allocated = new_entries_allocated;
329 return 1;
332 /**************************************************************************
333 Prepares the insertion of count entries at pos.
334 This function doesn't care if there is enough space in the datastructure.
335 SetListSize() must be used first.
336 With current implementation, this call will never fail
337 **************************************************************************/
338 static int PrepareInsertListEntries(struct MUI_ListData *data, int pos,
339 int count)
341 memmove(&data->entries[pos + count], &data->entries[pos],
342 (data->entries_num - pos) * sizeof(struct ListEntry *));
343 return 1;
346 /**************************************************************************
347 Removes count (already deinitalized) list entries starting az pos.
348 **************************************************************************/
349 static void RemoveListEntries(struct MUI_ListData *data, int pos, int count)
351 // FIXME: segfault if entries_num = pos = count = 1
352 memmove(&data->entries[pos], &data->entries[pos + count],
353 (data->entries_num - (pos + count)) * sizeof(struct ListEntry *));
356 /**************************************************************************
357 Frees all memory allocated by ParseListFormat()
358 **************************************************************************/
359 static void FreeListFormat(struct MUI_ListData *data)
361 int i;
363 if (data->ci)
365 for (i = 0; i < data->columns; i++)
367 FreeVec(data->ci[i].preparse);
368 data->ci[i].preparse = NULL;
370 FreeVec(data->ci);
371 data->ci = NULL;
373 FreeVec(data->preparses);
374 data->preparses = NULL;
375 if (data->strings)
377 FreeVec(data->strings - 1);
378 data->strings = NULL;
380 data->columns = 0;
383 /**************************************************************************
384 Parses the given format string (also frees a previously parsed format).
385 Return 0 on failure.
386 **************************************************************************/
387 static int ParseListFormat(struct MUI_ListData *data, STRPTR format)
389 int new_columns, i;
390 STRPTR ptr;
391 STRPTR format_sep;
392 char c;
394 IPTR args[ARG_CNT];
395 struct RDArgs *rdargs;
397 if (!format)
398 format = (STRPTR) "";
400 ptr = format;
402 FreeListFormat(data);
404 new_columns = 1;
406 /* Count the number of columns first */
407 while ((c = *ptr++))
408 if (c == ',')
409 new_columns++;
411 if (!(data->preparses =
412 AllocVec((new_columns + 10) * sizeof(STRPTR), 0)))
413 return 0;
415 if (!(data->strings = AllocVec((new_columns + 1 + 10)
416 * sizeof(STRPTR), 0))) /* hold enough space also for the entry pos,
417 * used by orginal MUI and also some
418 * security space */
419 return 0;
421 if (!(data->ci = AllocVec(new_columns * sizeof(struct ColumnInfo), 0)))
422 return 0;
424 // set defaults
425 for (i = 0; i < new_columns; i++)
427 data->ci[i].colno = -1; // -1 means: use unassigned column
428 data->ci[i].weight = 100;
429 data->ci[i].delta = 4;
430 data->ci[i].min_width = -1;
431 data->ci[i].max_width = -1;
432 data->ci[i].user_width = -1;
433 data->ci[i].bar = FALSE;
434 data->ci[i].preparse = NULL;
437 if ((format_sep = StrDup(format)) != 0)
439 for (i = 0; format_sep[i] != '\0'; i++)
441 if (format_sep[i] == ',')
442 format_sep[i] = '\0';
445 if ((rdargs = AllocDosObject(DOS_RDARGS, NULL)) != 0)
447 ptr = format_sep;
448 i = 0;
451 rdargs->RDA_Source.CS_Buffer = ptr;
452 rdargs->RDA_Source.CS_Length = strlen(ptr);
453 rdargs->RDA_Source.CS_CurChr = 0;
454 rdargs->RDA_DAList = 0;
455 rdargs->RDA_Buffer = NULL;
456 rdargs->RDA_BufSiz = 0;
457 rdargs->RDA_ExtHelp = NULL;
458 rdargs->RDA_Flags = 0;
460 memset(args, 0, sizeof args);
461 if (ReadArgs(FORMAT_TEMPLATE, args, rdargs))
463 if (args[ARG_COL])
464 data->ci[i].colno = *(LONG *) args[ARG_COL];
465 if (args[ARG_WEIGHT])
466 data->ci[i].weight = *(LONG *) args[ARG_WEIGHT];
467 if (args[ARG_DELTA])
468 data->ci[i].delta = *(LONG *) args[ARG_DELTA];
469 if (args[ARG_MINWIDTH])
470 data->ci[i].min_width =
471 *(LONG *) args[ARG_MINWIDTH];
472 if (args[ARG_MAXWIDTH])
473 data->ci[i].max_width =
474 *(LONG *) args[ARG_MAXWIDTH];
475 data->ci[i].bar = args[ARG_BAR];
476 if (args[ARG_PREPARSE])
477 data->ci[i].preparse =
478 StrDup((STRPTR) args[ARG_PREPARSE]);
480 FreeArgs(rdargs);
482 ptr += strlen(ptr) + 1;
483 i++;
485 while (i < new_columns);
486 FreeDosObject(DOS_RDARGS, rdargs);
488 FreeVec(format_sep);
491 for (i = 0; i < new_columns; i++)
493 D(bug("colno %d weight %d delta %d preparse %s\n",
494 data->ci[i].colno, data->ci[i].weight, data->ci[i].delta,
495 data->ci[i].preparse));
498 data->columns = new_columns;
499 data->strings++; /* Skip entry pos */
501 return 1;
504 /**************************************************************************
505 Call the MUIM_List_Display for the given entry. It fills out
506 data->string and data->preparses
507 **************************************************************************/
508 static void DisplayEntry(struct IClass *cl, Object *obj, int entry_pos)
510 struct MUI_ListData *data = INST_DATA(cl, obj);
511 APTR entry_data;
512 int col;
514 for (col = 0; col < data->columns; col++)
515 data->preparses[col] = data->ci[col].preparse;
517 if (entry_pos == ENTRY_TITLE)
519 if ((data->columns == 1) && (data->title != (STRPTR) 1))
521 *data->strings = data->title;
522 return;
524 entry_data = NULL; /* it's a title request */
526 else
527 entry_data = data->entries[entry_pos]->data;
529 /* Get the display formation */
530 DoMethod(obj, MUIM_List_Display, (IPTR) entry_data,
531 (IPTR) data->strings, entry_pos, (IPTR) data->preparses);
534 /**************************************************************************
535 Determine the dims of a single entry and adapt the columninfo according
536 to it. pos might be ENTRY_TITLE. Returns 0 if pos entry needs to
537 be redrawn after this operation, 1 if all entries need to be redrawn.
538 **************************************************************************/
539 static int CalcDimsOfEntry(struct IClass *cl, Object *obj, int pos)
541 struct MUI_ListData *data = INST_DATA(cl, obj);
542 struct ListEntry *entry = data->entries[pos];
543 int j;
544 int ret = 0;
546 if (!entry)
547 return ret;
549 if (!(_flags(obj) & MADF_SETUP))
550 return ret;
552 DisplayEntry(cl, obj, pos);
554 /* Set height to at least minheight */
555 if (data->entries[pos]->height < data->entry_minheight)
556 data->entries[pos]->height = data->entry_minheight;
558 for (j = 0; j < data->columns; j++)
560 ZText *text =
561 zune_text_new(data->preparses[j], data->strings[j],
562 ZTEXT_ARG_NONE, 0);
563 if (text != NULL)
565 zune_text_get_bounds(text, obj);
567 if (text->height > data->entries[pos]->height)
569 data->entries[pos]->height = text->height;
570 /* entry height changed, redraw all entries later */
571 ret = 1;
573 data->entries[pos]->widths[j] = text->width;
575 if (text->width > data->ci[j].entries_width)
577 /* This columns width is bigger than the other in the same
578 * columns, so we store this value
580 data->ci[j].entries_width = text->width;
581 /* column width changed, redraw all entries later */
582 ret = 1;
585 zune_text_destroy(text);
588 if (data->entries[pos]->height > data->entry_maxheight)
590 data->entry_maxheight = data->entries[pos]->height;
591 /* maximum entry height changed, redraw all entries later */
592 ret = 1;
595 return ret;
598 /**************************************************************************
599 Determine the widths of the entries
600 **************************************************************************/
601 static void CalcWidths(struct IClass *cl, Object *obj)
603 int i, j;
604 struct MUI_ListData *data = INST_DATA(cl, obj);
606 if (!(_flags(obj) & MADF_SETUP))
607 return;
609 for (j = 0; j < data->columns; j++)
610 data->ci[j].entries_width = 0;
612 data->entry_maxheight = 0;
613 data->entries_totalheight = 0;
614 data->entries_maxwidth = 0;
616 for (i = (data->title ? ENTRY_TITLE : 0); i < data->entries_num; i++)
618 CalcDimsOfEntry(cl, obj, i);
619 data->entries_totalheight += data->entries[i]->height;
622 for (j = 0; j < data->columns; j++)
623 data->entries_maxwidth += data->ci[j].entries_width;
625 if (!data->entry_maxheight)
626 data->entry_maxheight = 1;
629 /**************************************************************************
630 Calculates the number of visible entry lines. Returns 1 if it has
631 changed
632 **************************************************************************/
633 static int CalcVertVisible(struct IClass *cl, Object *obj)
635 struct MUI_ListData *data = INST_DATA(cl, obj);
636 int old_entries_visible = data->entries_visible;
637 int old_entries_top_pixel = data->entries_top_pixel;
639 data->entries_visible = (_mheight(data->area) - data->title_height)
640 / (data->entry_maxheight /* + data->prefs_linespacing */ );
642 /* Distribute extra vertical space evenly between top and bottom of
643 * list */
645 data->entries_top_pixel = _mtop(data->area) + data->title_height
646 + (_mheight(data->area) - data->title_height
648 data->entries_visible *
649 (data->entry_maxheight /* + data->prefs_linespacing */ )) / 2;
651 return (old_entries_visible != data->entries_visible)
652 || (old_entries_top_pixel != data->entries_top_pixel);
655 /**************************************************************************
656 Default hook to compare two list entries. Works for strings only.
657 **************************************************************************/
658 AROS_UFH3S(int, default_compare_func,
659 AROS_UFHA(struct Hook *, h, A0),
660 AROS_UFHA(char *, s2, A2),
661 AROS_UFHA(char *, s1, A1))
663 AROS_USERFUNC_INIT
665 return Stricmp(s1, s2);
667 AROS_USERFUNC_EXIT
670 /**************************************************************************
671 OM_NEW
672 **************************************************************************/
673 IPTR List__OM_NEW(struct IClass *cl, Object *obj, struct opSet *msg)
675 struct MUI_ListData *data;
676 struct TagItem *tag;
677 struct TagItem *tags;
678 APTR *array = NULL;
679 LONG new_entries_active = MUIV_List_Active_Off;
680 struct TagItem rectattrs[2] = {{TAG_IGNORE, TAG_IGNORE }, {TAG_DONE, TAG_DONE}};
681 Object *vert, *area;
683 /* search for MUIA_Frame as it has to be passed to rectangle object */
684 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
686 if (tag->ti_Tag == MUIA_Frame)
688 rectattrs[0].ti_Tag = MUIA_Frame;
689 rectattrs[0].ti_Data = tag->ti_Data;
690 tag->ti_Tag = TAG_IGNORE;
691 break;
695 obj = (Object *) DoSuperNewTags(cl, obj, NULL,
696 MUIA_Group_Horiz, TRUE,
697 MUIA_InnerLeft, 0,
698 MUIA_InnerRight, 0,
699 MUIA_Group_Spacing, 0,
700 MUIA_Font, MUIV_Font_List,
701 MUIA_ShowSelState, FALSE,
702 MUIA_InputMode, MUIV_InputMode_RelVerify,
703 MUIA_Background, MUII_ListBack,
704 TAG_MORE, (IPTR) msg->ops_AttrList,
705 TAG_DONE);
707 if (!obj)
708 return FALSE;
710 data = INST_DATA(cl, obj);
712 data->columns = 1;
713 data->entries_active = MUIV_List_Active_Off;
714 data->intern_puddle_size = 2008;
715 data->intern_thresh_size = 1024;
716 data->default_compare_hook.h_Entry = (HOOKFUNC) default_compare_func;
717 data->default_compare_hook.h_SubEntry = 0;
718 data->compare_hook = &(data->default_compare_hook);
719 data->flags = LIST_SHOWDROPMARKS;
720 data->area_replaced = FALSE;
722 data->last_active = -1;
724 data->ehn.ehn_Events = IDCMP_MOUSEBUTTONS | IDCMP_RAWKEY;
725 data->ehn.ehn_Priority = 0;
726 data->ehn.ehn_Flags = 0;
727 data->ehn.ehn_Object = obj;
728 data->ehn.ehn_Class = cl;
730 area = (Object *)GetTagData(MUIA_List_ListArea, (IPTR) 0, msg->ops_AttrList);
732 if (!area)
733 area = RectangleObject, TAG_MORE, (IPTR) rectattrs, End;
734 else
735 data->area_replaced = TRUE;
736 data->area = area;
738 vert = ScrollbarObject, MUIA_Group_Horiz, FALSE, End;
739 data->vert = vert;
741 /* parse initial taglist */
742 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
744 switch (tag->ti_Tag)
746 case MUIA_List_Active:
747 new_entries_active = tag->ti_Data;
748 break;
750 case MUIA_List_Pool:
751 data->pool = (APTR) tag->ti_Data;
752 break;
754 case MUIA_List_PoolPuddleSize:
755 data->intern_puddle_size = tag->ti_Data;
756 break;
758 case MUIA_List_PoolThreshSize:
759 data->intern_thresh_size = tag->ti_Data;
760 break;
762 case MUIA_List_CompareHook:
763 data->compare_hook = (struct Hook *)tag->ti_Data;
764 if (data->compare_hook == NULL)
765 data->compare_hook = &data->default_compare_hook;
766 break;
768 case MUIA_List_ConstructHook:
769 data->construct_hook = (struct Hook *)tag->ti_Data;
770 break;
772 case MUIA_List_DestructHook:
773 data->destruct_hook = (struct Hook *)tag->ti_Data;
774 break;
776 case MUIA_List_DisplayHook:
777 data->display_hook = (struct Hook *)tag->ti_Data;
778 break;
780 case MUIA_List_MultiTestHook:
781 data->multi_test_hook = (struct Hook *)tag->ti_Data;
782 break;
784 case MUIA_List_SourceArray:
785 array = (APTR *) tag->ti_Data;
786 break;
788 case MUIA_List_Format:
789 data->format = StrDup((STRPTR) tag->ti_Data);
790 break;
792 case MUIA_List_Title:
793 data->title = (STRPTR) tag->ti_Data;
794 break;
796 case MUIA_List_MinLineHeight:
797 data->entry_minheight = tag->ti_Data;
798 break;
800 case MUIA_List_AdjustHeight:
801 _handle_bool_tag(data->flags, tag->ti_Data, LIST_ADJUSTHEIGHT);
802 break;
804 case MUIA_List_AdjustWidth:
805 _handle_bool_tag(data->flags, tag->ti_Data, LIST_ADJUSTWIDTH);
806 break;
808 case MUIA_List_AutoVisible:
809 _handle_bool_tag(data->flags, tag->ti_Data, LIST_AUTOVISIBLE);
810 break;
812 case MUIA_List_ShowDropMarks:
813 _handle_bool_tag(data->flags, tag->ti_Data, LIST_SHOWDROPMARKS);
814 break;
816 case MUIA_List_DragSortable:
817 _handle_bool_tag(data->flags, tag->ti_Data, LIST_DRAGSORTABLE);
818 set(obj, MUIA_Draggable, tag->ti_Data);
819 break;
821 case MUIA_Listview_ScrollerPos:
822 data->scroller_pos = tag->ti_Data;
823 break;
825 case MUIA_Listview_Input:
826 data->read_only = !tag->ti_Data;
827 break;
829 case MUIA_Listview_MultiSelect:
830 data->multiselect = tag->ti_Data;
831 break;
833 case MUIA_Listview_DoubleClick:
834 data->doubleclick = tag->ti_Data != 0;
835 break;
839 /* Add list and/or scroller */
840 switch (data->scroller_pos)
842 case MUIV_Listview_ScrollerPos_None:
843 DoMethod(obj, OM_ADDMEMBER, area);
844 break;
845 case MUIV_Listview_ScrollerPos_Left:
846 DoMethod(obj, OM_ADDMEMBER, vert);
847 DoMethod(obj, OM_ADDMEMBER, area);
848 break;
849 default:
850 DoMethod(obj, OM_ADDMEMBER, area);
851 DoMethod(obj, OM_ADDMEMBER, vert);
852 break;
855 if (vert)
857 LONG entries = 0, first = 0, visible = 0;
859 get(obj, MUIA_List_VertProp_First, &first);
860 get(obj, MUIA_List_VertProp_Visible, &visible);
861 get(obj, MUIA_List_VertProp_Entries, &entries);
863 SetAttrs(data->vert,
864 MUIA_Prop_First, first,
865 MUIA_Prop_Visible, visible, MUIA_Prop_Entries, entries, TAG_DONE);
867 /* Pass prop object as DestObj (based on code in NList) */
868 DoMethod(obj, MUIM_Notify, MUIA_List_VertProp_First, MUIV_EveryTime,
869 (IPTR) vert, 3, MUIM_NoNotifySet, MUIA_Prop_First, MUIV_TriggerValue);
870 DoMethod(obj, MUIM_Notify, MUIA_List_VertProp_Visible, MUIV_EveryTime,
871 (IPTR) vert, 3, MUIM_NoNotifySet, MUIA_Prop_Visible, MUIV_TriggerValue);
872 DoMethod(obj, MUIM_Notify, MUIA_List_VertProp_Entries, MUIV_EveryTime,
873 (IPTR) vert, 3, MUIM_NoNotifySet, MUIA_Prop_Entries, MUIV_TriggerValue);
876 if (!data->pool)
878 /* No memory pool given, so we create our own */
879 data->pool = data->intern_pool =
880 CreatePool(0, data->intern_puddle_size,
881 data->intern_thresh_size);
882 if (!data->pool)
884 CoerceMethod(cl, obj, OM_DISPOSE);
885 return 0;
889 /* parse the list format */
890 if (!(ParseListFormat(data, data->format)))
892 CoerceMethod(cl, obj, OM_DISPOSE);
893 return 0;
896 /* This is neccessary for at least the title */
897 if (!SetListSize(data, 0))
899 CoerceMethod(cl, obj, OM_DISPOSE);
900 return 0;
903 if (data->title)
905 if (!(data->entries[ENTRY_TITLE] = AllocListEntry(data)))
907 CoerceMethod(cl, obj, OM_DISPOSE);
908 return 0;
911 else
912 data->entries[ENTRY_TITLE] = NULL;
914 if (array)
916 int i;
917 /* Count the number of elements */
918 for (i = 0; array[i] != NULL; i++)
920 /* Insert them */
921 DoMethod(obj, MUIM_List_Insert, (IPTR) array, i,
922 MUIV_List_Insert_Top);
925 if ((data->entries_num) && (new_entries_active != MUIV_List_Active_Off))
927 switch (new_entries_active)
929 case MUIV_List_Active_Top:
930 new_entries_active = 0;
931 break;
933 case MUIV_List_Active_Bottom:
934 new_entries_active = data->entries_num - 1;
935 break;
938 if (new_entries_active < 0)
939 new_entries_active = 0;
940 else if (new_entries_active >= data->entries_num)
941 new_entries_active = data->entries_num - 1;
943 data->entries_active = new_entries_active;
944 /* Selected entry will be moved into visible area */
947 NewList((struct List *)&data->images);
949 D(bug("List_New(%lx)\n", obj));
951 return (IPTR) obj;
954 /**************************************************************************
955 OM_DISPOSE
956 **************************************************************************/
957 IPTR List__OM_DISPOSE(struct IClass *cl, Object *obj, Msg msg)
959 struct MUI_ListData *data = INST_DATA(cl, obj);
961 D(bug("List Dispose\n"));
963 /* Call destruct method for every entry and free the entries manually
964 * to avoid notification */
965 while (data->confirm_entries_num)
967 struct ListEntry *lentry =
968 data->entries[--data->confirm_entries_num];
969 DoMethod(obj, MUIM_List_Destruct, (IPTR) lentry->data,
970 (IPTR) data->pool);
971 FreeListEntry(data, lentry);
974 if (data->intern_pool)
975 DeletePool(data->intern_pool);
976 if (data->entries)
977 FreeVec(data->entries - 1);
978 /* title is currently before all other elements */
980 FreeListFormat(data);
981 FreeVec(data->format);
983 return DoSuperMethodA(cl, obj, msg);
987 /**************************************************************************
988 OM_SET
989 **************************************************************************/
990 IPTR List__OM_SET(struct IClass *cl, Object *obj, struct opSet *msg)
992 struct MUI_ListData *data = INST_DATA(cl, obj);
993 struct TagItem *tag;
994 struct TagItem *tags;
996 /* parse taglist */
997 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
999 switch (tag->ti_Tag)
1001 case MUIA_List_CompareHook:
1002 data->compare_hook = (struct Hook *)tag->ti_Data;
1003 if (data->compare_hook == NULL)
1004 data->compare_hook = &data->default_compare_hook;
1005 break;
1007 case MUIA_List_ConstructHook:
1008 data->construct_hook = (struct Hook *)tag->ti_Data;
1009 break;
1011 case MUIA_List_DestructHook:
1012 data->destruct_hook = (struct Hook *)tag->ti_Data;
1013 break;
1015 case MUIA_List_DisplayHook:
1016 data->display_hook = (struct Hook *)tag->ti_Data;
1017 break;
1019 case MUIA_List_MultiTestHook:
1020 data->multi_test_hook = (struct Hook *)tag->ti_Data;
1021 if (data->multi_test_hook != NULL)
1023 /* Clearing current selections is the easiest way to keep
1024 * selections consistent with the new hook */
1025 DoMethod(obj, MUIM_List_Select, MUIV_List_Select_All,
1026 MUIV_List_Select_Off, NULL);
1028 break;
1030 case MUIA_List_Title:
1031 data->title = (STRPTR) tag->ti_Data;
1032 DoMethod(obj, MUIM_List_Redraw, MUIV_List_Redraw_All);
1033 break;
1035 case MUIA_List_VertProp_First:
1036 data->vertprop_first = tag->ti_Data;
1037 if (data->entries_first != tag->ti_Data)
1039 set(obj, MUIA_List_First, tag->ti_Data);
1041 break;
1043 case MUIA_List_Format:
1044 data->format = StrDup((STRPTR) tag->ti_Data);
1045 ParseListFormat(data, data->format);
1046 // FIXME: should we check for errors?
1047 DoMethod(obj, MUIM_List_Redraw, MUIV_List_Redraw_All);
1048 break;
1050 case MUIA_List_VertProp_Entries:
1051 data->vertprop_entries = tag->ti_Data;
1052 break;
1054 case MUIA_List_VertProp_Visible:
1055 data->vertprop_visible = tag->ti_Data;
1056 data->entries_visible = tag->ti_Data;
1057 break;
1059 case MUIA_List_Active:
1061 LONG new_entries_active = tag->ti_Data;
1063 if ((data->entries_num)
1064 && (new_entries_active != MUIV_List_Active_Off))
1066 switch (new_entries_active)
1068 case MUIV_List_Active_Top:
1069 new_entries_active = 0;
1070 break;
1072 case MUIV_List_Active_Bottom:
1073 new_entries_active = data->entries_num - 1;
1074 break;
1076 case MUIV_List_Active_Up:
1077 new_entries_active = data->entries_active - 1;
1078 break;
1080 case MUIV_List_Active_Down:
1081 new_entries_active = data->entries_active + 1;
1082 break;
1084 case MUIV_List_Active_PageUp:
1085 new_entries_active =
1086 data->entries_active - data->entries_visible;
1087 break;
1089 case MUIV_List_Active_PageDown:
1090 new_entries_active =
1091 data->entries_active + data->entries_visible;
1092 break;
1095 if (new_entries_active < 0)
1096 new_entries_active = 0;
1097 else if (new_entries_active >= data->entries_num)
1098 new_entries_active = data->entries_num - 1;
1100 else
1101 new_entries_active = -1;
1103 if (data->entries_active != new_entries_active)
1105 LONG old = data->entries_active;
1106 data->entries_active = new_entries_active;
1108 /* Selectchange stuff */
1109 if (new_entries_active != -1)
1111 DoMethod(obj, MUIM_List_SelectChange,
1112 new_entries_active, MUIV_List_Select_On, 0);
1113 DoMethod(obj, MUIM_List_SelectChange,
1114 new_entries_active, MUIV_List_Select_Active, 0);
1116 else
1117 DoMethod(obj, MUIM_List_SelectChange,
1118 MUIV_List_Active_Off, MUIV_List_Select_Off, 0);
1120 data->update = 2;
1121 data->update_pos = old;
1122 MUI_Redraw(obj, MADF_DRAWUPDATE);
1123 data->update = 2;
1124 data->update_pos = data->entries_active;
1125 MUI_Redraw(obj, MADF_DRAWUPDATE);
1127 /* Make new active entry visible (if there is one and
1128 list is visible) */
1129 if (new_entries_active != -1
1130 && (_flags(obj) & MADF_SETUP))
1132 DoMethod(obj, MUIM_List_Jump,
1133 MUIV_List_Jump_Active);
1137 break;
1139 case MUIA_List_First:
1140 data->update_pos = data->entries_first;
1141 data->update = 3;
1142 data->entries_first = tag->ti_Data;
1144 MUI_Redraw(obj, MADF_DRAWUPDATE);
1145 if ((data->vertprop_first != tag->ti_Data)
1146 && (!(data->flags & LIST_QUIET)))
1148 set(obj, MUIA_List_VertProp_First, tag->ti_Data);
1150 break;
1152 case MUIA_List_Visible: /* Shouldn't be settable? */
1153 if (data->vertprop_visible != tag->ti_Data)
1154 set(obj, MUIA_List_VertProp_Visible, tag->ti_Data);
1155 break;
1157 case MUIA_List_Entries:
1158 if (data->confirm_entries_num == tag->ti_Data)
1160 data->entries_num = tag->ti_Data;
1161 if (!(data->flags & LIST_QUIET))
1163 set(obj, MUIA_List_VertProp_Entries, data->entries_num);
1166 else
1168 D(bug("Bug: confirm_entries != MUIA_List_Entries!\n"));
1170 break;
1172 case MUIA_List_Quiet:
1173 _handle_bool_tag(data->flags, tag->ti_Data, LIST_QUIET);
1174 if (!tag->ti_Data)
1176 DoMethod(obj, MUIM_List_Redraw, MUIV_List_Redraw_All);
1177 if (data->entries_num != XGET(obj, MUIA_List_VertProp_Entries))
1178 set(obj, MUIA_List_VertProp_Entries, data->entries_num);
1179 if (data->vertprop_first !=
1180 XGET(obj, MUIA_List_VertProp_First))
1181 set(obj, MUIA_List_VertProp_First, data->vertprop_first);
1183 break;
1185 case MUIA_List_AutoVisible:
1186 _handle_bool_tag(data->flags, tag->ti_Data, LIST_AUTOVISIBLE);
1187 break;
1189 case MUIA_List_ShowDropMarks:
1190 _handle_bool_tag(data->flags, tag->ti_Data, LIST_SHOWDROPMARKS);
1191 break;
1193 case MUIA_List_DragSortable:
1194 _handle_bool_tag(data->flags, tag->ti_Data, LIST_DRAGSORTABLE);
1195 set(obj, MUIA_Draggable, tag->ti_Data);
1196 break;
1198 case MUIA_Selected:
1199 /* Swallow this so the Area class doesn't redraw us */
1200 tag->ti_Tag = TAG_IGNORE;
1201 break;
1203 case MUIA_Disabled:
1204 if (_flags(obj) & MADF_SETUP)
1206 /* Stop listening for events we only listen to when mouse
1207 button is down: we will not be informed of the button
1208 being released */
1209 DoMethod(_win(obj), MUIM_Window_RemEventHandler, (IPTR) &data->ehn);
1210 data->ehn.ehn_Events &= ~(IDCMP_MOUSEMOVE | IDCMP_INTUITICKS | IDCMP_INACTIVEWINDOW);
1211 DoMethod(_win(obj), MUIM_Window_AddEventHandler, (IPTR) &data->ehn);
1213 break;
1215 case MUIA_Listview_DoubleClick: /* private set */
1216 data->doubleclick = tag->ti_Data != 0;
1217 break;
1219 case MUIA_Listview_SelectChange: /* private set */
1220 data->select_change = tag->ti_Data != 0;
1221 break;
1223 case MUIA_Listview_ScrollerPos: /* private set */
1224 data->scroller_pos = tag->ti_Data;
1225 break;
1227 case MUIA_Listview_Input: /* private set */
1228 data->read_only = !tag->ti_Data;
1229 break;
1231 case MUIA_Listview_MultiSelect: /* private set */
1232 data->multiselect = tag->ti_Data;
1233 break;
1237 return DoSuperMethodA(cl, obj, (Msg) msg);
1240 /**************************************************************************
1241 OM_GET
1242 **************************************************************************/
1243 IPTR List__OM_GET(struct IClass *cl, Object *obj, struct opGet *msg)
1245 /* small macro to simplify return value storage */
1246 #define STORE *(msg->opg_Storage)
1247 struct MUI_ListData *data = INST_DATA(cl, obj);
1249 switch (msg->opg_AttrID)
1251 case MUIA_List_Entries:
1252 STORE = data->entries_num;
1253 return 1;
1254 case MUIA_List_First:
1255 STORE = data->entries_first;
1256 return 1;
1257 case MUIA_List_Active:
1258 STORE = data->entries_active;
1259 return 1;
1260 case MUIA_List_InsertPosition:
1261 STORE = data->insert_position;
1262 return 1;
1263 case MUIA_List_Title:
1264 STORE = (IPTR) data->title;
1265 return 1;
1266 case MUIA_List_VertProp_Entries:
1267 STORE = data->vertprop_entries;
1268 return 1;
1269 case MUIA_List_VertProp_Visible:
1270 case MUIA_List_Visible:
1271 STORE = data->vertprop_visible;
1272 return 1;
1273 case MUIA_List_VertProp_First:
1274 STORE = data->vertprop_first;
1275 return 1;
1276 case MUIA_List_Format:
1277 STORE = (IPTR) data->format;
1278 return 1;
1279 case MUIA_List_AutoVisible:
1280 STORE = data->flags & LIST_AUTOVISIBLE;
1281 return 1;
1282 case MUIA_List_ShowDropMarks:
1283 STORE = data->flags & LIST_SHOWDROPMARKS;
1284 return 1;
1285 case MUIA_List_DragSortable:
1286 STORE = data->flags & LIST_DRAGSORTABLE;
1287 return 1;
1288 case MUIA_Listview_ClickColumn:
1289 STORE = data->click_column;
1290 return 1;
1291 case MUIA_Listview_DoubleClick:
1292 STORE = data->doubleclick;
1293 return 1;
1294 case MUIA_Listview_SelectChange:
1295 STORE = data->select_change;
1296 return 1;
1297 case MUIA_Listview_List:
1298 STORE = (IPTR)obj;
1299 return 1;
1302 if (DoSuperMethodA(cl, obj, (Msg) msg))
1303 return 1;
1304 return 0;
1305 #undef STORE
1308 /**************************************************************************
1309 MUIM_Setup
1310 **************************************************************************/
1311 IPTR List__MUIM_Setup(struct IClass *cl, Object *obj,
1312 struct MUIP_Setup *msg)
1314 struct MUI_ListData *data = INST_DATA(cl, obj);
1316 if (!DoSuperMethodA(cl, obj, (Msg) msg))
1317 return 0;
1319 data->prefs_refresh = muiGlobalInfo(obj)->mgi_Prefs->list_refresh;
1320 data->prefs_linespacing =
1321 muiGlobalInfo(obj)->mgi_Prefs->list_linespacing;
1322 data->prefs_smoothed = muiGlobalInfo(obj)->mgi_Prefs->list_smoothed;
1323 data->prefs_smoothval = muiGlobalInfo(obj)->mgi_Prefs->list_smoothval;
1325 CalcWidths(cl, obj);
1327 data->list_cursor =
1328 zune_imspec_setup(MUII_ListCursor, muiRenderInfo(obj));
1329 data->list_select =
1330 zune_imspec_setup(MUII_ListSelect, muiRenderInfo(obj));
1331 data->list_selcur =
1332 zune_imspec_setup(MUII_ListSelCur, muiRenderInfo(obj));
1334 data->prefs_multi = muiGlobalInfo(obj)->mgi_Prefs->list_multi;
1335 if (data->multiselect == MUIV_Listview_MultiSelect_Default)
1337 if (data->prefs_multi == LISTVIEW_MULTI_SHIFTED)
1338 data->multiselect = MUIV_Listview_MultiSelect_Shifted;
1339 else
1340 data->multiselect = MUIV_Listview_MultiSelect_Always;
1343 DoMethod(_win(obj), MUIM_Window_AddEventHandler, (IPTR) &data->ehn);
1345 return 1;
1348 /**************************************************************************
1349 MUIM_Cleanup
1350 **************************************************************************/
1351 IPTR List__MUIM_Cleanup(struct IClass *cl, Object *obj,
1352 struct MUIP_Cleanup *msg)
1354 struct MUI_ListData *data = INST_DATA(cl, obj);
1355 struct ListImage *li = List_First(&data->images);
1357 while (li)
1359 struct ListImage *next = Node_Next(li);
1360 DoMethod(obj, MUIM_List_DeleteImage, (IPTR) li);
1361 li = next;
1364 zune_imspec_cleanup(data->list_cursor);
1365 zune_imspec_cleanup(data->list_select);
1366 zune_imspec_cleanup(data->list_selcur);
1368 DoMethod(_win(obj), MUIM_Window_RemEventHandler, (IPTR) &data->ehn);
1369 data->mouse_click = 0;
1371 return DoSuperMethodA(cl, obj, (Msg) msg);
1374 /**************************************************************************
1375 MUIM_AskMinMax
1376 **************************************************************************/
1377 IPTR List__MUIM_AskMinMax(struct IClass *cl, Object *obj,
1378 struct MUIP_AskMinMax *msg)
1380 struct MUI_ListData *data = INST_DATA(cl, obj);
1382 DoSuperMethodA(cl, obj, (Msg) msg);
1385 if ((data->flags & LIST_ADJUSTWIDTH) && (data->entries_num > 0))
1387 msg->MinMaxInfo->MinWidth += data->entries_maxwidth;
1388 msg->MinMaxInfo->DefWidth += data->entries_maxwidth;
1389 msg->MinMaxInfo->MaxWidth += data->entries_maxwidth;
1391 else
1393 msg->MinMaxInfo->MinWidth += 40;
1394 msg->MinMaxInfo->DefWidth += 100;
1395 msg->MinMaxInfo->MaxWidth = MUI_MAXMAX;
1398 if (data->entries_num > 0)
1400 if (data->flags & LIST_ADJUSTHEIGHT)
1402 msg->MinMaxInfo->MinHeight += data->entries_totalheight;
1403 msg->MinMaxInfo->DefHeight += data->entries_totalheight;
1404 msg->MinMaxInfo->MaxHeight += data->entries_totalheight;
1406 else
1408 ULONG h = data->entry_maxheight + data->prefs_linespacing;
1409 msg->MinMaxInfo->MinHeight += 2 * h + data->prefs_linespacing;
1410 msg->MinMaxInfo->DefHeight += 8 * h + data->prefs_linespacing;
1411 msg->MinMaxInfo->MaxHeight = MUI_MAXMAX;
1414 else
1416 msg->MinMaxInfo->MinHeight += 36;
1417 msg->MinMaxInfo->DefHeight += 96;
1418 msg->MinMaxInfo->MaxHeight = MUI_MAXMAX;
1420 D(bug("List %p minheigh=%d, line maxh=%d\n",
1421 obj, msg->MinMaxInfo->MinHeight, data->entry_maxheight));
1422 return TRUE;
1425 /****i* List.mui/MUIM_Layout *************************************************
1427 * NAME
1428 * MUIM_Layout
1430 ******************************************************************************
1434 IPTR List__MUIM_Layout(struct IClass *cl, Object *obj,
1435 struct MUIP_Layout *msg)
1437 struct MUI_ListData *data = INST_DATA(cl, obj);
1438 ULONG rc = DoSuperMethodA(cl, obj, (Msg) msg);
1439 LONG new_entries_first = data->entries_first;
1441 /* Calc the numbers of entries visible */
1442 CalcVertVisible(cl, obj);
1444 /* Ensure active entry is visible if requested */
1445 if (data->entries_active + 1 >=
1446 (data->entries_first + data->entries_visible)
1447 && (data->flags & LIST_AUTOVISIBLE) != 0)
1448 new_entries_first =
1449 data->entries_active - data->entries_visible + 1;
1451 /* Ensure there are no unnecessary empty lines */
1452 if ((new_entries_first + data->entries_visible >=
1453 data->entries_num)
1454 && (data->entries_visible <= data->entries_num))
1455 new_entries_first = data->entries_num - data->entries_visible;
1457 /* Always show the start of the list if it isn't long enough to fill the
1458 view */
1459 if (data->entries_num <= data->entries_visible)
1460 new_entries_first = 0;
1462 if (new_entries_first < 0)
1463 new_entries_first = 0;
1465 set(obj, new_entries_first != data->entries_first ?
1466 MUIA_List_First : TAG_IGNORE, new_entries_first);
1468 /* So the notify happens */
1469 set(obj, MUIA_List_VertProp_Visible, data->entries_visible);
1471 return rc;
1475 /**************************************************************************
1476 MUIM_Show
1477 **************************************************************************/
1478 IPTR List__MUIM_Show(struct IClass *cl, Object *obj,
1479 struct MUIP_Show *msg)
1481 struct MUI_ListData *data = INST_DATA(cl, obj);
1482 ULONG rc = DoSuperMethodA(cl, obj, (Msg) msg);
1484 zune_imspec_show(data->list_cursor, obj);
1485 zune_imspec_show(data->list_select, obj);
1486 zune_imspec_show(data->list_selcur, obj);
1487 return rc;
1491 /**************************************************************************
1492 MUIM_Hide
1493 **************************************************************************/
1494 IPTR List__MUIM_Hide(struct IClass *cl, Object *obj,
1495 struct MUIP_Hide *msg)
1497 struct MUI_ListData *data = INST_DATA(cl, obj);
1499 zune_imspec_hide(data->list_cursor);
1500 zune_imspec_hide(data->list_select);
1501 zune_imspec_hide(data->list_selcur);
1503 return DoSuperMethodA(cl, obj, (Msg) msg);
1507 /**************************************************************************
1508 Draw an entry at entry_pos at the given row. To draw the title, set pos to
1509 ENTRY_TITLE
1510 **************************************************************************/
1511 static VOID List_DrawEntry(struct IClass *cl, Object *obj, int entry_pos,
1512 int y)
1514 struct MUI_ListData *data = INST_DATA(cl, obj);
1515 int col, x1, x2;
1517 /* To be sure we don't draw anything if there is no title */
1518 if (entry_pos == ENTRY_TITLE && !data->title)
1519 return;
1521 DisplayEntry(cl, obj, entry_pos);
1522 x1 = _mleft(data->area);
1524 for (col = 0; col < data->columns; col++)
1526 ZText *text;
1527 x2 = x1 + data->ci[col].entries_width;
1529 if ((text =
1530 zune_text_new(data->preparses[col], data->strings[col],
1531 ZTEXT_ARG_NONE, 0)))
1533 /* Could be made simpler, as we don't really need the bounds */
1534 zune_text_get_bounds(text, obj);
1535 /* Note, this was MPEN_SHADOW before */
1536 SetAPen(_rp(obj), muiRenderInfo(obj)->mri_Pens[MPEN_TEXT]);
1537 zune_text_draw(text, obj, x1, x2, y); /* totally wrong! */
1538 zune_text_destroy(text);
1540 x1 = x2 + data->ci[col].delta + (data->ci[col].bar ? BAR_WIDTH : 0);
1544 /**************************************************************************
1545 MUIM_Draw
1546 **************************************************************************/
1547 IPTR List__MUIM_Draw(struct IClass *cl, Object *obj, struct MUIP_Draw *msg)
1549 struct MUI_ListData *data = INST_DATA(cl, obj);
1550 int entry_pos, y;
1551 APTR clip;
1552 int start, end;
1553 BOOL scroll_caused_damage = FALSE;
1554 struct MUI_ImageSpec_intern *highlight;
1555 IPTR ret = (IPTR)0;
1557 if (data->flags & LIST_QUIET)
1558 return 0;
1560 ret = DoSuperMethodA(cl, obj, (Msg) msg);
1562 if (data->area_replaced)
1563 return ret;
1565 /* Calculate the title height */
1566 if (data->title)
1568 data->title_height = data->entries[ENTRY_TITLE]->height + 2;
1570 else
1572 data->title_height = 0;
1575 /* Calc the numbers of entries visible */
1576 CalcVertVisible(cl, obj);
1578 if ((msg->flags & MADF_DRAWUPDATE) == 0 || data->update == 1)
1580 DoMethod(obj, MUIM_DrawBackground, _mleft(data->area), _mtop(data->area),
1581 _mwidth(data->area), _mheight(data->area),
1582 0, data->entries_first * data->entry_maxheight, 0);
1585 clip = MUI_AddClipping(muiRenderInfo(obj), _mleft(data->area), _mtop(data->area),
1586 _mwidth(data->area), _mheight(data->area));
1588 if ((msg->flags & MADF_DRAWUPDATE) == 0 || data->update == 1)
1590 y = _mtop(data->area);
1591 /* Draw Title
1593 if (data->title_height && data->title)
1595 List_DrawEntry(cl, obj, ENTRY_TITLE, y);
1596 y += data->entries[ENTRY_TITLE]->height;
1597 SetAPen(_rp(obj), _pens(obj)[MPEN_SHADOW]);
1598 Move(_rp(obj), _mleft(data->area), y);
1599 Draw(_rp(obj), _mright(data->area), y);
1600 SetAPen(_rp(obj), _pens(obj)[MPEN_SHINE]);
1601 y++;
1602 Move(_rp(obj), _mleft(data->area), y);
1603 Draw(_rp(obj), _mright(data->area), y);
1607 y = data->entries_top_pixel;
1609 start = data->entries_first;
1610 end = data->entries_first + data->entries_visible;
1612 if ((msg->flags & MADF_DRAWUPDATE) && data->update == 3)
1614 int diffy = data->entries_first - data->update_pos;
1615 int top, bottom;
1616 if (abs(diffy) < data->entries_visible)
1618 scroll_caused_damage =
1619 (_rp(obj)->Layer->Flags & LAYERREFRESH) ? FALSE : TRUE;
1621 ScrollRaster(_rp(obj), 0, diffy * data->entry_maxheight,
1622 _mleft(data->area), y,
1623 _mright(data->area),
1624 y + data->entry_maxheight * data->entries_visible);
1626 scroll_caused_damage =
1627 scroll_caused_damage
1628 && (_rp(obj)->Layer->Flags & LAYERREFRESH);
1630 if (diffy > 0)
1632 start = end - diffy;
1633 y += data->entry_maxheight * (data->entries_visible -
1634 diffy);
1636 else
1637 end = start - diffy;
1640 top = y;
1641 bottom = y + (end - start) * data->entry_maxheight;
1643 DoMethod(obj, MUIM_DrawBackground, _mleft(data->area), top,
1644 _mwidth(data->area), bottom - top + 1,
1646 top - _mtop(data->area) + data->entries_first * data->entry_maxheight,
1650 for (entry_pos = start;
1651 entry_pos < end && entry_pos < data->entries_num; entry_pos++)
1653 struct ListEntry *entry = data->entries[entry_pos];
1655 if (!(msg->flags & MADF_DRAWUPDATE) ||
1656 ((msg->flags & MADF_DRAWUPDATE) && data->update == 1) ||
1657 ((msg->flags & MADF_DRAWUPDATE) && data->update == 3) ||
1658 ((msg->flags & MADF_DRAWUPDATE) && data->update == 2
1659 && data->update_pos == entry_pos))
1661 /* Choose appropriate highlight image */
1663 if (entry_pos == data->entries_active
1664 && entry->flags & ENTRY_SELECTED)
1665 highlight = data->list_selcur;
1666 else if (entry_pos == data->entries_active)
1667 highlight = data->list_cursor;
1668 else if (entry->flags & ENTRY_SELECTED)
1669 highlight = data->list_select;
1670 else
1671 highlight = NULL;
1673 /* Draw highlight or background */
1675 if (highlight != NULL)
1677 zune_imspec_draw(highlight, muiRenderInfo(obj),
1678 _mleft(data->area), y, _mwidth(data->area), data->entry_maxheight,
1679 0, y - data->entries_top_pixel, 0);
1681 else if ((msg->flags & MADF_DRAWUPDATE) && data->update == 2
1682 && data->update_pos == entry_pos)
1684 DoMethod(obj, MUIM_DrawBackground, _mleft(data->area), y,
1685 _mwidth(data->area), data->entry_maxheight, 0,
1686 y - _mtop(data->area) +
1687 data->entries_first * data->entry_maxheight, 0);
1690 List_DrawEntry(cl, obj, entry_pos, y);
1692 y += data->entry_maxheight;
1695 MUI_RemoveClipping(muiRenderInfo(obj), clip);
1697 data->update = 0;
1699 if (scroll_caused_damage)
1701 if (MUI_BeginRefresh(muiRenderInfo(obj), 0))
1703 /* Theoretically it might happen that more damage is caused
1704 after ScrollRaster. By something else, like window movement
1705 in front of our window. Therefore refresh root object of
1706 window, not just this object */
1708 Object *o = NULL;
1710 get(_win(obj), MUIA_Window_RootObject, &o);
1711 MUI_Redraw(o, MADF_DRAWOBJECT);
1713 MUI_EndRefresh(muiRenderInfo(obj), 0);
1717 ULONG x1 = _mleft(data->area);
1718 ULONG col;
1719 y = _mtop(data->area);
1721 if (data->title_height && data->title)
1723 for (col = 0; col < data->columns; col++)
1725 ULONG halfdelta = data->ci[col].delta / 2;
1726 x1 += data->ci[col].entries_width + halfdelta;
1728 if (x1 + (data->ci[col].bar ? BAR_WIDTH : 0) > _mright(data->area))
1729 break;
1731 if (data->ci[col].bar)
1733 SetAPen(_rp(obj), _pens(obj)[MPEN_SHINE]);
1734 Move(_rp(obj), x1, y);
1735 Draw(_rp(obj), x1,
1736 y + data->entries[ENTRY_TITLE]->height - 1);
1737 SetAPen(_rp(obj), _pens(obj)[MPEN_SHADOW]);
1738 Move(_rp(obj), x1 + 1, y);
1739 Draw(_rp(obj), x1 + 1,
1740 y + data->entries[ENTRY_TITLE]->height - 1);
1742 x1 += BAR_WIDTH;
1744 x1 += data->ci[col].delta - halfdelta;
1746 y += data->entries[ENTRY_TITLE]->height + 1;
1749 x1 = _mleft(data->area);
1751 for (col = 0; col < data->columns; col++)
1753 ULONG halfdelta = data->ci[col].delta / 2;
1754 x1 += data->ci[col].entries_width + halfdelta;
1756 if (x1 + (data->ci[col].bar ? BAR_WIDTH : 0) > _mright(data->area))
1757 break;
1759 if (data->ci[col].bar)
1761 SetAPen(_rp(obj), _pens(obj)[MPEN_SHINE]);
1762 Move(_rp(obj), x1, y);
1763 Draw(_rp(obj), x1, _mbottom(data->area));
1764 SetAPen(_rp(obj), _pens(obj)[MPEN_SHADOW]);
1765 Move(_rp(obj), x1 + 1, y);
1766 Draw(_rp(obj), x1 + 1, _mbottom(data->area));
1768 x1 += BAR_WIDTH;
1771 x1 += data->ci[col].delta - halfdelta;
1774 return 0;
1777 /****** List.mui/MUIM_List_Clear *********************************************
1779 * NAME
1780 * MUIM_List_Clear (V4)
1782 * SYNOPSIS
1783 * DoMethod(obj, MUIM_List_Clear);
1785 * FUNCTION
1786 * Removes all entries from the list.
1788 ******************************************************************************
1792 IPTR List__MUIM_Clear(struct IClass *cl, Object *obj,
1793 struct MUIP_List_Clear *msg)
1795 struct MUI_ListData *data = INST_DATA(cl, obj);
1797 while (data->confirm_entries_num)
1799 struct ListEntry *lentry =
1800 data->entries[--data->confirm_entries_num];
1801 DoMethod(obj, MUIM_List_Destruct, (IPTR) lentry->data,
1802 (IPTR) data->pool);
1803 FreeListEntry(data, lentry);
1805 /* Should never fail when shrinking */
1806 SetListSize(data, 0);
1809 if (data->confirm_entries_num != data->entries_num)
1811 SetAttrs(obj, MUIA_List_Entries, 0, MUIA_List_First, 0,
1812 /* Notify only when no entry was active */
1813 data->entries_active !=
1814 MUIV_List_Active_Off ? MUIA_List_Active : TAG_DONE,
1815 MUIV_List_Active_Off, TAG_DONE);
1817 data->update = 1;
1818 MUI_Redraw(obj, MADF_DRAWUPDATE);
1821 return 0;
1824 /**************************************************************************
1825 MUIM_List_Exchange
1826 **************************************************************************/
1827 IPTR List__MUIM_Exchange(struct IClass *cl, Object *obj,
1828 struct MUIP_List_Exchange *msg)
1830 struct MUI_ListData *data = INST_DATA(cl, obj);
1831 LONG pos1, pos2;
1833 switch (msg->pos1)
1835 case MUIV_List_Exchange_Top:
1836 pos1 = 0;
1837 break;
1838 case MUIV_List_Exchange_Active:
1839 pos1 = data->entries_active;
1840 break;
1841 case MUIV_List_Exchange_Bottom:
1842 pos1 = data->entries_num - 1;
1843 break;
1844 default:
1845 pos1 = msg->pos1;
1848 switch (msg->pos2)
1850 case MUIV_List_Exchange_Top:
1851 pos2 = 0;
1852 break;
1853 case MUIV_List_Exchange_Active:
1854 pos2 = data->entries_active;
1855 break;
1856 case MUIV_List_Exchange_Bottom:
1857 pos2 = data->entries_num - 1;
1858 break;
1859 case MUIV_List_Exchange_Next:
1860 pos2 = pos1 + 1;
1861 break;
1862 case MUIV_List_Exchange_Previous:
1863 pos2 = pos1 - 1;
1864 break;
1865 default:
1866 pos2 = msg->pos2;
1869 if (pos1 >= 0 && pos1 < data->entries_num && pos2 >= 0
1870 && pos2 < data->entries_num && pos1 != pos2)
1872 struct ListEntry *save = data->entries[pos1];
1873 data->entries[pos1] = data->entries[pos2];
1874 data->entries[pos2] = save;
1876 data->update = 2;
1877 data->update_pos = pos1;
1878 MUI_Redraw(obj, MADF_DRAWUPDATE);
1880 data->update = 2;
1881 data->update_pos = pos2;
1882 MUI_Redraw(obj, MADF_DRAWUPDATE);
1884 return TRUE;
1886 else
1888 return FALSE;
1892 /**************************************************************************
1893 MUIM_List_Redraw
1894 **************************************************************************/
1895 IPTR List__MUIM_Redraw(struct IClass *cl, Object *obj,
1896 struct MUIP_List_Redraw *msg)
1898 struct MUI_ListData *data = INST_DATA(cl, obj);
1900 if (!(data->flags & LIST_QUIET))
1902 if (msg->pos == MUIV_List_Redraw_All)
1904 data->update = 1;
1905 CalcWidths(cl, obj);
1906 MUI_Redraw(obj, MADF_DRAWUPDATE);
1908 else
1910 LONG pos = -1;
1911 if (msg->pos == MUIV_List_Redraw_Active)
1912 pos = data->entries_active;
1913 else if (msg->pos == MUIV_List_Redraw_Entry)
1915 LONG i;
1916 for (i = 0; i < data->entries_num; i++)
1917 if (data->entries[i]->data == msg->entry)
1919 pos = i;
1920 break;
1923 else
1924 pos = msg->pos;
1926 if (pos != -1)
1928 if (CalcDimsOfEntry(cl, obj, pos))
1929 data->update = 1;
1930 else
1932 data->update = 2;
1933 data->update_pos = pos;
1935 MUI_Redraw(obj, MADF_DRAWUPDATE);
1939 return 0;
1942 /**************************************************************************
1943 MUIM_List_Remove
1944 **************************************************************************/
1945 IPTR List__MUIM_Remove(struct IClass *cl, Object *obj,
1946 struct MUIP_List_Remove *msg)
1948 struct MUI_ListData *data = INST_DATA(cl, obj);
1949 LONG pos, cur;
1950 LONG new_act;
1951 struct ListEntry *lentry;
1952 //int rem_count = 1;
1954 if (!data->entries_num)
1955 return 0;
1957 switch (msg->pos)
1959 case MUIV_List_Remove_First:
1960 pos = 0;
1961 break;
1963 case MUIV_List_Remove_Active:
1964 pos = data->entries_active;
1965 break;
1967 case MUIV_List_Remove_Last:
1968 pos = data->entries_num - 1;
1969 break;
1971 case MUIV_List_Remove_Selected:
1972 /* TODO: needs special handling */
1973 pos = data->entries_active;
1974 break;
1976 default:
1977 pos = msg->pos;
1978 break;
1981 if (pos < 0 || pos >= data->entries_num)
1982 return 0;
1984 new_act = data->entries_active;
1986 if (pos == new_act && new_act == data->entries_num - 1)
1987 new_act--; /* might become MUIV_List_Active_Off */
1989 lentry = data->entries[pos];
1990 DoMethod(obj, MUIM_List_Destruct, (IPTR) lentry->data,
1991 (IPTR) data->pool);
1993 cur = pos + 1;
1995 RemoveListEntries(data, pos, cur - pos);
1996 data->confirm_entries_num -= cur - pos;
1998 /* ensure that the active element is in a valid range */
1999 if (new_act >= data->entries_num)
2000 new_act = data->entries_num - 1;
2002 SetAttrs(obj, MUIA_List_Entries, data->confirm_entries_num,
2003 (new_act >= pos) || (new_act != data->entries_active) ?
2004 MUIA_List_Active : TAG_DONE,
2005 new_act, /* Inform only if neccessary (for notify) */
2006 TAG_DONE);
2008 data->update = 1;
2009 MUI_Redraw(obj, MADF_DRAWUPDATE);
2011 return 0;
2014 /**************************************************************************
2015 MUIM_List_Select
2016 **************************************************************************/
2017 IPTR List__MUIM_Select(struct IClass *cl, Object *obj,
2018 struct MUIP_List_Select *msg)
2020 struct MUI_ListData *data = INST_DATA(cl, obj);
2021 LONG pos, i, count, selcount=0, state=0;
2022 BOOL multi_allowed = TRUE, new_select_state = FALSE;
2024 /* Establish the range of entries affected */
2025 switch (msg->pos)
2027 case MUIV_List_Select_Active:
2028 pos = data->entries_active;
2029 if (pos == MUIV_List_Active_Off)
2030 count = 0;
2031 else
2032 count = 1;
2033 break;
2035 case MUIV_List_Select_All:
2036 pos = 0;
2037 count = data->entries_num;
2038 break;
2040 default:
2041 pos = msg->pos;
2042 count = 1;
2043 if (pos < 0 || pos >= data->entries_num)
2044 return 0;
2045 break;
2048 if (msg->seltype != MUIV_List_Select_Ask && data->multi_test_hook != NULL)
2050 /* Disallow selection of an additional entry if there is a currently
2051 selected entry that is not multi-selectable (in such case there
2052 will only be one entry currently selected, so no need to iterate) */
2053 i = MUIV_List_NextSelected_Start;
2054 DoMethod(obj, MUIM_List_NextSelected, (IPTR) &i);
2055 if (i != MUIV_List_NextSelected_End)
2056 selcount++;
2057 if (data->multi_test_hook != NULL && selcount != 0)
2058 multi_allowed = CallHookPkt(data->multi_test_hook, NULL,
2059 data->entries[i]->data);
2062 /* Change or check state of each entry in the range */
2063 for (i = pos; i < pos + count; i++)
2065 state = data->entries[i]->flags & ENTRY_SELECTED;
2066 switch (msg->seltype)
2068 case MUIV_List_Select_Off:
2069 new_select_state = FALSE;
2070 break;
2072 case MUIV_List_Select_On:
2073 new_select_state = TRUE;
2074 break;
2076 case MUIV_List_Select_Toggle:
2077 new_select_state = !state;
2078 break;
2080 default:
2081 if (data->entries[i]->flags & ENTRY_SELECTED)
2082 selcount++;
2083 break;
2086 if (msg->seltype != MUIV_List_Select_Ask)
2088 /* Disallow selection if entry is not multi-selectable and
2089 * there are already selected entries */
2090 if (data->multi_test_hook != NULL && new_select_state)
2091 new_select_state = multi_allowed && (selcount == 0 ||
2092 CallHookPkt(data->multi_test_hook, NULL,
2093 data->entries[i]->data));
2095 if (new_select_state)
2096 data->entries[i]->flags |= ENTRY_SELECTED;
2097 else
2098 data->entries[i]->flags &= ~ENTRY_SELECTED;
2102 /* Report old state or number of selected entries */
2103 if (msg->info)
2105 if (msg->pos == MUIV_List_Select_All
2106 && msg->seltype == MUIV_List_Select_Ask)
2107 *msg->info = selcount;
2108 else
2109 *msg->info = state;
2112 /* Redraw unless it was just an enquiry */
2113 if (msg->seltype != MUIV_List_Select_Ask)
2115 if (count > 1)
2116 data->update = 1;
2117 else
2119 data->update = 2;
2120 data->update_pos = pos;
2122 MUI_Redraw(obj, MADF_DRAWUPDATE);
2125 return 0;
2128 /**************************************************************************
2129 MUIM_List_Insert
2130 **************************************************************************/
2132 IPTR List__MUIM_Insert(struct IClass *cl, Object *obj,
2133 struct MUIP_List_Insert *msg)
2135 struct MUI_ListData *data = INST_DATA(cl, obj);
2136 LONG pos, count, sort;
2138 count = msg->count;
2139 sort = 0;
2141 if (count == -1)
2143 /* Count the number of entries */
2144 for (count = 0; msg->entries[count] != NULL; count++)
2148 if (count <= 0)
2149 return ~0;
2151 switch (msg->pos)
2153 case MUIV_List_Insert_Top:
2154 pos = 0;
2155 break;
2157 case MUIV_List_Insert_Active:
2158 if (data->entries_active != -1)
2159 pos = data->entries_active;
2160 else
2161 pos = 0;
2162 break;
2164 case MUIV_List_Insert_Sorted:
2165 pos = data->entries_num;
2166 sort = 1; /* we sort'em later */
2167 break;
2169 case MUIV_List_Insert_Bottom:
2170 pos = data->entries_num;
2171 break;
2173 default:
2174 if (msg->pos > data->entries_num)
2175 pos = data->entries_num;
2176 else if (msg->pos < 0)
2177 pos = 0;
2178 else
2179 pos = msg->pos;
2180 break;
2183 if (!(SetListSize(data, data->entries_num + count)))
2184 return ~0;
2186 LONG until = pos + count;
2187 APTR *toinsert = msg->entries;
2189 if (!(PrepareInsertListEntries(data, pos, count)))
2190 return ~0;
2192 while (pos < until)
2194 struct ListEntry *lentry;
2196 if (!(lentry = AllocListEntry(data)))
2198 /* Panic, but we must be in a consistent state, so remove
2199 * the space where the following list entries should have gone
2201 RemoveListEntries(data, pos, until - pos);
2202 return ~0;
2205 /* now call the construct method which returns us a pointer which
2206 we need to store */
2207 lentry->data = (APTR) DoMethod(obj, MUIM_List_Construct,
2208 (IPTR) * toinsert, (IPTR) data->pool);
2209 if (!lentry->data)
2211 FreeListEntry(data, lentry);
2212 RemoveListEntries(data, pos, until - pos);
2214 /* TODO: Also check for visible stuff like below */
2215 if (data->entries_num != data->confirm_entries_num)
2216 set(obj, MUIA_List_Entries, data->confirm_entries_num);
2217 return ~0;
2220 data->entries[pos] = lentry;
2221 data->confirm_entries_num++;
2223 if (_flags(obj) & MADF_SETUP)
2225 /* We have to calculate the width and height of the newly
2226 * inserted entry. This has to be done after inserting the
2227 * element into the list */
2228 CalcDimsOfEntry(cl, obj, pos);
2231 toinsert++;
2232 pos++;
2235 /* Recalculate the number of visible entries */
2236 if (_flags(obj) & MADF_SETUP)
2237 CalcVertVisible(cl, obj);
2239 if (data->entries_num != data->confirm_entries_num)
2241 SetAttrs(obj,
2242 MUIA_List_Entries, data->confirm_entries_num,
2243 MUIA_List_Visible, data->entries_visible, TAG_DONE);
2246 /* If the array is already sorted, we could do a simple insert
2247 * sort and would be much faster than with qsort.
2248 * If an array is not yet sorted, does a MUIV_List_Insert_Sorted
2249 * sort the whole array?
2251 * I think, we better sort the whole array:
2253 if (sort)
2255 DoMethod(obj, MUIM_List_Sort);
2256 /* TODO: which pos to return here !? */
2257 /* MUIM_List_Sort already called MUI_Redraw */
2259 else
2261 data->update = 1;
2262 MUI_Redraw(obj, MADF_DRAWUPDATE);
2264 data->insert_position = pos;
2266 return (ULONG) pos;
2269 /**************************************************************************
2270 MUIM_List_InsertSingle
2271 **************************************************************************/
2272 IPTR List__MUIM_InsertSingle(struct IClass *cl, Object *obj,
2273 struct MUIP_List_InsertSingle *msg)
2275 return DoMethod(obj, MUIM_List_Insert, (IPTR) & msg->entry, 1,
2276 msg->pos);
2279 /**************************************************************************
2280 MUIM_List_GetEntry
2281 **************************************************************************/
2282 IPTR List__MUIM_GetEntry(struct IClass *cl, Object *obj,
2283 struct MUIP_List_GetEntry *msg)
2285 struct MUI_ListData *data = INST_DATA(cl, obj);
2286 int pos = msg->pos;
2288 if (pos == MUIV_List_GetEntry_Active)
2289 pos = data->entries_active;
2291 if (pos < 0 || pos >= data->entries_num)
2293 *msg->entry = NULL;
2294 return 0;
2296 *msg->entry = data->entries[pos]->data;
2297 return (IPTR) *msg->entry;
2300 /**************************************************************************
2301 MUIM_List_Construct
2302 **************************************************************************/
2303 IPTR List__MUIM_Construct(struct IClass *cl, Object *obj,
2304 struct MUIP_List_Construct *msg)
2306 struct MUI_ListData *data = INST_DATA(cl, obj);
2308 if (NULL == data->construct_hook)
2309 return (IPTR) msg->entry;
2310 if ((IPTR) data->construct_hook == MUIV_List_ConstructHook_String)
2312 int len = msg->entry ? strlen((STRPTR) msg->entry) : 0;
2313 ULONG *mem = AllocPooled(msg->pool, len + 5);
2315 if (NULL == mem)
2316 return 0;
2317 mem[0] = len + 5;
2318 if (msg->entry != NULL)
2319 strcpy((STRPTR) (mem + 1), (STRPTR) msg->entry);
2320 else
2321 *(STRPTR) (mem + 1) = 0;
2322 return (IPTR) (mem + 1);
2324 return CallHookPkt(data->construct_hook, msg->pool, msg->entry);
2327 /**************************************************************************
2328 MUIM_List_Destruct
2329 **************************************************************************/
2330 IPTR List__MUIM_Destruct(struct IClass *cl, Object *obj,
2331 struct MUIP_List_Destruct *msg)
2333 struct MUI_ListData *data = INST_DATA(cl, obj);
2335 if (NULL == data->destruct_hook)
2336 return 0;
2338 if ((IPTR) data->destruct_hook == MUIV_List_DestructHook_String)
2340 ULONG *mem = ((ULONG *) msg->entry) - 1;
2341 FreePooled(msg->pool, mem, mem[0]);
2343 else
2345 CallHookPkt(data->destruct_hook, msg->pool, msg->entry);
2347 return 0;
2350 /****** List.mui/MUIM_List_Compare *******************************************
2352 * NAME
2353 * MUIM_List_Compare (V20)
2355 * SYNOPSIS
2356 * DoMethod(obj, MUIM_List_Compare, APTR entry1, APTR entry2,
2357 * LONG sort_type1, LONG sort_type2);
2359 * FUNCTION
2360 * Compare two list entries according to the current comparison hook
2361 * (MUIA_List_CompareHook).
2363 * INPUTS
2364 * entry1 - the first entry data.
2365 * entry2 - the second entry data.
2366 * sort_type1 - undocumented.
2367 * sort_type2 - undocumented.
2369 * SEE ALSO
2370 * MUIA_List_CompareHook, MUIM_List_Sort.
2372 ******************************************************************************
2376 IPTR List__MUIM_Compare(struct IClass *cl, Object *obj,
2377 struct MUIP_List_Compare *msg)
2379 struct MUI_ListData *data = INST_DATA(cl, obj);
2381 return CallHookPkt(data->compare_hook, msg->entry2, msg->entry1);
2384 /**************************************************************************
2385 MUIM_List_Display
2386 **************************************************************************/
2387 IPTR List__MUIM_Display(struct IClass *cl, Object *obj,
2388 struct MUIP_List_Display *msg)
2390 struct MUI_ListData *data = INST_DATA(cl, obj);
2392 if (NULL == data->display_hook)
2394 if (msg->entry)
2395 *msg->array = msg->entry;
2396 else
2397 *msg->array = 0;
2398 return 1;
2401 *((ULONG *) (msg->array - 1)) = msg->entry_pos;
2402 return CallHookPkt(data->display_hook, msg->array, msg->entry);
2405 /**************************************************************************
2406 MUIM_List_SelectChange
2407 **************************************************************************/
2408 IPTR List__MUIM_SelectChange(struct IClass *cl, Object *obj,
2409 struct MUIP_List_SelectChange *msg)
2411 return 1;
2414 /**************************************************************************
2415 MUIM_List_CreateImage
2416 Called by a List subclass in its Setup method.
2417 Connects an Area subclass object to the list, much like an object gets
2418 connected to a window. List calls Setup and AskMinMax on that object,
2419 keeps a reference to it (that reference will be returned).
2420 Text engine will dereference that pointer and draw the object with its
2421 default size.
2422 **************************************************************************/
2423 IPTR List__MUIM_CreateImage(struct IClass *cl, Object *obj,
2424 struct MUIP_List_CreateImage *msg)
2426 struct MUI_ListData *data = INST_DATA(cl, obj);
2427 struct ListImage *li;
2429 if (!msg->obj)
2430 return 0;
2432 /* List must be already setup in Setup of your subclass */
2433 if (!(_flags(obj) & MADF_SETUP))
2434 return 0;
2435 li = AllocPooled(data->pool, sizeof(struct ListImage));
2436 if (!li)
2437 return 0;
2438 li->obj = msg->obj;
2440 AddTail((struct List *)&data->images, (struct Node *)li);
2441 DoMethod(li->obj, MUIM_ConnectParent, (IPTR) obj);
2442 DoSetupMethod(li->obj, muiRenderInfo(obj));
2445 return (IPTR) li;
2448 /**************************************************************************
2449 MUIM_List_DeleteImage
2450 **************************************************************************/
2451 IPTR List__MUIM_DeleteImage(struct IClass *cl, Object *obj,
2452 struct MUIP_List_DeleteImage *msg)
2454 struct MUI_ListData *data = INST_DATA(cl, obj);
2455 struct ListImage *li = (struct ListImage *)msg->listimg;
2457 if (li)
2459 DoMethod(li->obj, MUIM_Cleanup);
2460 DoMethod(li->obj, MUIM_DisconnectParent);
2461 Remove((struct Node *)li);
2462 FreePooled(data->pool, li, sizeof(struct ListImage));
2465 return 0;
2468 /****** List.mui/MUIM_List_Jump **********************************************
2470 * NAME
2471 * MUIM_List_Jump (V4)
2473 * SYNOPSIS
2474 * DoMethod(obj, MUIM_List_Jump, LONG pos);
2476 * FUNCTION
2477 * Scrolls the list so that a particular entry is visible.
2479 * INPUTS
2480 * pos - index of entry that should become visible, or one of these
2481 * special values:
2482 * MUIV_List_Jump_Active: show the active entry.
2483 * MUIV_List_Jump_Top: show the first entry.
2484 * MUIV_List_Jump_Bottom: show the last entry.
2485 * MUIV_List_Jump_Up: show the previous hidden entry.
2486 * MUIV_List_Jump_Down: show the next hidden entry.
2488 ******************************************************************************
2492 IPTR List__MUIM_Jump(struct IClass *cl, Object *obj,
2493 struct MUIP_List_Jump *msg)
2495 struct MUI_ListData *data = INST_DATA(cl, obj);
2496 LONG pos = msg->pos;
2498 switch (pos)
2500 case MUIV_List_Jump_Top:
2501 pos = 0;
2502 break;
2504 case MUIV_List_Jump_Active:
2505 pos = data->entries_active;
2506 break;
2508 case MUIV_List_Jump_Bottom:
2509 pos = data->entries_num - 1;
2510 break;
2512 case MUIV_List_Jump_Down:
2513 pos = data->entries_first + data->entries_visible;
2514 break;
2516 case MUIV_List_Jump_Up:
2517 pos = data->entries_first - 1;
2518 break;
2521 if (pos >= data->entries_num)
2523 pos = data->entries_num - 1;
2525 if (pos < 0)
2526 pos = 0;
2528 if (pos < data->entries_first)
2530 set(obj, MUIA_List_First, pos);
2532 else if (pos >= data->entries_first + data->entries_visible)
2534 pos -= (data->entries_visible - 1);
2535 if (pos < 0)
2536 pos = 0;
2537 if (pos != data->entries_first)
2539 set(obj, MUIA_List_First, pos);
2543 return TRUE;
2546 /****** List.mui/MUIM_List_Sort **********************************************
2548 * NAME
2549 * MUIM_List_Sort (V4)
2551 * SYNOPSIS
2552 * DoMethod(obj, MUIM_List_Sort);
2554 * FUNCTION
2555 * Sort the list's entries according to the current comparison hook
2556 * (MUIA_List_CompareHook).
2558 * SEE ALSO
2559 * MUIA_List_CompareHook, MUIM_List_Compare.
2561 ******************************************************************************
2565 IPTR List__MUIM_Sort(struct IClass *cl, Object *obj,
2566 struct MUIP_List_Sort *msg)
2568 struct MUI_ListData *data = INST_DATA(cl, obj);
2570 int i, j, max;
2571 struct MUIP_List_Compare cmpmsg =
2572 { MUIM_List_Compare, NULL, NULL, 0, 0 };
2574 if (data->entries_num > 1)
2577 Simple sort algorithm. Feel free to improve it.
2579 for (i = 0; i < data->entries_num - 1; i++)
2581 max = i;
2582 for (j = i + 1; j < data->entries_num; j++)
2584 cmpmsg.entry1 = data->entries[max]->data;
2585 cmpmsg.entry2 = data->entries[j]->data;
2586 if ((LONG) DoMethodA(obj, (Msg) & cmpmsg) > 0)
2588 max = j;
2591 if (i != max)
2593 APTR tmp = data->entries[i];
2594 data->entries[i] = data->entries[max];
2595 data->entries[max] = tmp;
2600 data->update = 1;
2601 MUI_Redraw(obj, MADF_DRAWUPDATE);
2603 return 0;
2606 /****** List.mui/MUIM_List_Move **********************************************
2608 * NAME
2609 * MUIM_List_Move (V9)
2611 * SYNOPSIS
2612 * DoMethod(obj, MUIM_List_Move, LONG from, LONG to);
2614 * FUNCTION
2615 * Move a list entry to a new position.
2617 * INPUTS
2618 * from - the current index of the entry that should be moved, or one of
2619 * these special values:
2620 * MUIV_List_Move_Active: the active entry.
2621 * MUIV_List_Move_Top: the first entry.
2622 * MUIV_List_Move_Bottom: the last entry.
2623 * to - the index of the entry's new position, or one of
2624 * these special values:
2625 * MUIV_List_Move_Active: the active entry.
2626 * MUIV_List_Move_Top: the first entry.
2627 * MUIV_List_Move_Bottom: the last entry.
2629 ******************************************************************************
2633 IPTR List__MUIM_Move(struct IClass *cl, Object *obj,
2634 struct MUIP_List_Move *msg)
2636 struct MUI_ListData *data = INST_DATA(cl, obj);
2638 LONG from, to;
2639 int i;
2641 /* Normalise special 'from' values */
2642 switch (msg->from)
2644 case MUIV_List_Move_Top:
2645 from = 0;
2646 break;
2647 case MUIV_List_Move_Active:
2648 from = data->entries_active;
2649 break;
2650 case MUIV_List_Move_Bottom:
2651 from = data->entries_num - 1;
2652 break;
2653 default:
2654 from = msg->from;
2657 /* Normalise special 'to' values */
2658 switch (msg->to)
2660 case MUIV_List_Move_Top:
2661 to = 0;
2662 break;
2663 case MUIV_List_Move_Active:
2664 to = data->entries_active;
2665 break;
2666 case MUIV_List_Move_Bottom:
2667 to = data->entries_num - 1;
2668 break;
2669 case MUIV_List_Move_Next:
2670 to = from + 1;
2671 break;
2672 case MUIV_List_Move_Previous:
2673 to = from - 1;
2674 break;
2675 default:
2676 to = msg->to;
2679 /* Check that values are within valid bounds */
2680 if (from > data->entries_num - 1 || from < 0
2681 || to > data->entries_num - 1 || to < 0 || from == to)
2682 return (IPTR) FALSE;
2684 /* Shift all entries in the range between the 'from' and 'to' positions */
2685 if (from < to)
2687 struct ListEntry *backup = data->entries[from];
2688 for (i = from; i < to; i++)
2689 data->entries[i] = data->entries[i + 1];
2690 data->entries[to] = backup;
2692 else
2694 struct ListEntry *backup = data->entries[from];
2695 for (i = from; i > to; i--)
2696 data->entries[i] = data->entries[i - 1];
2697 data->entries[to] = backup;
2700 /* Update index of active entry */
2701 if (from == data->entries_active)
2702 data->entries_active = to;
2703 else if (data->entries_active > from && data->entries_active < to)
2704 data->entries_active--;
2705 else if (data->entries_active < from && data->entries_active >= to)
2706 data->entries_active++;
2708 /* Reflect list changes visually */
2709 data->update = 1;
2710 MUI_Redraw(obj, MADF_DRAWUPDATE);
2712 return TRUE;
2715 /**************************************************************************
2716 MUIM_List_NextSelected
2717 **************************************************************************/
2718 IPTR List__MUIM_NextSelected(struct IClass *cl, Object *obj,
2719 struct MUIP_List_NextSelected *msg)
2721 struct MUI_ListData *data = INST_DATA(cl, obj);
2722 LONG pos, i;
2723 BOOL found = FALSE;
2725 /* Get the first entry to check */
2726 pos = *msg->pos;
2727 if (pos == MUIV_List_NextSelected_Start)
2728 pos = 0;
2729 else
2730 pos++;
2732 /* Find the next selected entry */
2733 for (i = pos; i < data->entries_num && !found; i++)
2735 if (data->entries[i]->flags & ENTRY_SELECTED)
2737 pos = i;
2738 found = TRUE;
2742 /* Return index of selected entry, or indicate there are no more */
2743 if (!found)
2744 pos = MUIV_List_NextSelected_End;
2745 *msg->pos = pos;
2747 return TRUE;
2750 /**************************************************************************
2751 MUIM_List_TestPos
2752 **************************************************************************/
2753 IPTR List__MUIM_TestPos(struct IClass *cl, Object *obj,
2754 struct MUIP_List_TestPos *msg)
2756 struct MUI_ListData *data = INST_DATA(cl, obj);
2757 struct MUI_List_TestPos_Result *result = msg->res;
2758 LONG col = -1, row = -1;
2759 UWORD flags = 0, i;
2760 LONG mx = msg->x - _left(data->area);
2761 LONG entries_visible;
2763 if (data->entries_visible <= data->entries_num)
2764 entries_visible = data->entries_visible;
2765 else
2766 entries_visible = data->entries_num;
2767 LONG ey = msg->y - data->entries_top_pixel;
2768 /* y coordinates transformed to the entries */
2770 /* Now check if it was clicked on a title or on entries */
2771 if (ey < 0)
2772 flags |= MUI_LPR_ABOVE;
2773 else if (ey >= entries_visible * data->entry_maxheight)
2774 flags |= MUI_LPR_BELOW;
2775 else
2777 /* Identify row */
2778 row = ey / data->entry_maxheight + data->entries_first;
2779 result->yoffset =
2780 ey % data->entry_maxheight - data->entry_maxheight / 2;
2783 if (mx < 0)
2784 flags |= MUI_LPR_LEFT;
2785 else if (mx >= _width(data->area))
2786 flags |= MUI_LPR_RIGHT;
2787 else
2789 /* Identify column */
2790 if (data->entries_num > 0 && data->columns > 0)
2792 LONG width_sum = 0;
2793 col = data->columns - 1;
2794 for (i = 0; i < data->columns; i++)
2796 result->xoffset = mx - width_sum;
2797 width_sum +=
2798 data->ci[i].entries_width +
2799 data->ci[i].delta +
2800 (data->ci[i].bar ? BAR_WIDTH : 0);
2801 D(bug("[List/MUIM_TestPos] i %d "
2802 "width %d width_sum %d mx %d\n",
2803 i, data->ci[i].entries_width, width_sum, mx));
2804 if (mx < width_sum)
2806 col = i;
2807 D(bug("[List/MUIM_TestPos] Column hit %d\n", col));
2808 break;
2814 result->entry = row;
2815 result->column = col;
2816 result->flags = flags;
2818 return TRUE;
2821 /****i* List.mui/MUIM_DragQuery **********************************************
2823 * NAME
2824 * MUIM_DragQuery
2826 ******************************************************************************
2830 IPTR List__MUIM_DragQuery(struct IClass *cl, Object *obj,
2831 struct MUIP_DragQuery *msg)
2833 if (msg->obj == obj)
2834 return MUIV_DragQuery_Accept;
2835 else
2836 return MUIV_DragQuery_Refuse;
2840 /****i* List.mui/MUIM_DragFinish *********************************************
2842 * NAME
2843 * MUIM_DragFinish
2845 ******************************************************************************
2849 IPTR List__MUIM_DragFinish(struct IClass *cl, Object *obj,
2850 struct MUIP_DragFinish *msg)
2852 struct MUI_ListData *data = INST_DATA(cl, obj);
2854 data->drop_mark_y = -1;
2856 return DoSuperMethodA(cl, obj, (Msg) msg);
2859 /****i* List.mui/MUIM_DragReport *********************************************
2861 * NAME
2862 * MUIM_DragReport
2864 ******************************************************************************
2868 IPTR List__MUIM_DragReport(struct IClass *cl, Object *obj,
2869 struct MUIP_DragReport *msg)
2871 struct MUI_ListData *data = INST_DATA(cl, obj);
2872 struct MUI_List_TestPos_Result pos;
2873 struct RastPort *rp = _rp(obj);
2874 LONG n, y;
2875 UWORD old_pattern;
2877 /* Choose new drop mark position */
2879 DoMethod(obj, MUIM_List_TestPos, msg->x, msg->y, (IPTR) &pos);
2880 if (pos.entry != -1)
2882 n = pos.entry;
2883 if (pos.yoffset > 0)
2884 n++;
2886 else if ((pos.flags & MUI_LPR_ABOVE) != 0)
2887 n = data->entries_first;
2888 else
2890 n = MIN(data->entries_visible, data->entries_num)
2891 - data->entries_first;
2894 /* Clear old drop mark */
2896 if ((data->flags & LIST_SHOWDROPMARKS) != 0)
2898 y = data->entries_top_pixel + (n - data->entries_first)
2899 * data->entry_maxheight;
2900 if (y != data->drop_mark_y)
2902 DoMethod(obj, MUIM_DrawBackground, _mleft(data->area), data->drop_mark_y,
2903 _mwidth(data->area), 1, 0, 0, 0);
2905 /* Draw new drop mark and store its position */
2907 SetABPenDrMd(rp, _pens(obj)[MPEN_SHINE], _pens(obj)[MPEN_SHADOW],
2908 JAM2);
2909 old_pattern = rp->LinePtrn;
2910 SetDrPt(rp, 0xF0F0);
2911 Move(rp, _mleft(data->area), y);
2912 Draw(rp, _mright(data->area), y);
2913 SetDrPt(rp, old_pattern);
2914 data->drop_mark_y = y;
2918 return TRUE;
2922 /****i* List.mui/MUIM_DragDrop ***********************************************
2924 * NAME
2925 * MUIM_DragDrop
2927 ******************************************************************************
2931 IPTR List__MUIM_DragDrop(struct IClass *cl, Object *obj,
2932 struct MUIP_DragDrop *msg)
2934 struct MUI_ListData *data = INST_DATA(cl, obj);
2935 struct MUI_List_TestPos_Result pos;
2936 LONG n;
2938 /* Find drop position */
2940 DoMethod(obj, MUIM_List_TestPos, msg->x, msg->y, (IPTR) &pos);
2941 if (pos.entry != -1)
2943 /* Change drop position when coords move past centre of entry, not
2944 * entry boundary */
2946 n = pos.entry;
2947 if (pos.yoffset > 0)
2948 n++;
2950 /* Ensure that dropped entry will be positioned between the two
2951 * entries that are above and below the drop mark, rather than
2952 * strictly at the numeric index shown */
2954 if (n > data->entries_active)
2955 n--;
2957 else if ((pos.flags & MUI_LPR_ABOVE) != 0)
2958 n = MUIV_List_Move_Top;
2959 else
2960 n = MUIV_List_Move_Bottom;
2962 DoMethod(msg->obj, MUIM_List_Move, MUIV_List_Move_Active, n);
2964 return TRUE;
2968 /****i* List.mui/MUIM_CreateDragImage ****************************************
2970 * NAME
2971 * MUIM_CreateDragImage
2973 ******************************************************************************
2977 static IPTR List__MUIM_CreateDragImage(struct IClass *cl, Object *obj,
2978 struct MUIP_CreateDragImage *msg)
2980 struct MUI_ListData *data = INST_DATA(cl, obj);
2981 BOOL success = TRUE;
2982 struct MUI_List_TestPos_Result pos;
2983 WORD width, height, left, top;
2984 struct MUI_DragImage *img = NULL;
2985 const struct ZuneFrameGfx *zframe;
2986 LONG depth;
2988 /* Get info on dragged entry */
2989 DoMethod(obj, MUIM_List_TestPos, _left(data->area) - msg->touchx,
2990 _top(data->area) - msg->touchy, (IPTR) &pos);
2991 if (pos.entry == -1)
2992 success = FALSE;
2994 if (success)
2996 /* Get boundaries of entry */
2997 width = _mwidth(data->area);
2998 height = data->entry_maxheight;
2999 left = _mleft(data->area);
3000 top = _top(data->area) - msg->touchy
3001 - (pos.yoffset + data->entry_maxheight / 2);
3003 /* Allocate drag image structure */
3004 img = (struct MUI_DragImage *)
3005 AllocVec(sizeof(struct MUI_DragImage), MEMF_CLEAR);
3006 if (img == NULL)
3007 success = FALSE;
3010 if (success)
3012 /* Get drag frame */
3013 zframe = zune_zframe_get(obj,
3014 &muiGlobalInfo(obj)->mgi_Prefs->frames[MUIV_Frame_Drag]);
3016 /* Allocate drag image buffer */
3017 img->width = width + zframe->ileft + zframe->iright;
3018 img->height = height + zframe->itop + zframe->ibottom;
3019 depth = GetBitMapAttr(_screen(obj)->RastPort.BitMap, BMA_DEPTH);
3020 img->bm = AllocBitMap(img->width, img->height, depth, BMF_MINPLANES,
3021 _screen(obj)->RastPort.BitMap);
3023 if (img->bm != NULL)
3025 /* Render entry */
3026 struct RastPort temprp;
3027 InitRastPort(&temprp);
3028 temprp.BitMap = img->bm;
3029 ClipBlit(_rp(obj), left, top, &temprp,
3030 zframe->ileft, zframe->itop, width, height,
3031 0xc0);
3033 /* Render frame */
3034 struct RastPort *rp_save = muiRenderInfo(obj)->mri_RastPort;
3035 muiRenderInfo(obj)->mri_RastPort = &temprp;
3036 zframe->draw(zframe->customframe, muiRenderInfo(obj), 0, 0,
3037 img->width, img->height, 0, 0, img->width, img->height);
3038 muiRenderInfo(obj)->mri_RastPort = rp_save;
3041 /* Ensure drag point matches where user clicked */
3042 img->touchx = msg->touchx - zframe->ileft + _addleft(obj);
3043 img->touchy = -(pos.yoffset + data->entry_maxheight / 2)
3044 - zframe->itop;
3045 img->flags = 0;
3048 return (IPTR) img;
3051 static void DoWheelMove(struct IClass *cl, Object *obj, LONG wheely)
3053 LONG new, first, entries, visible;
3055 new = first = XGET(obj, MUIA_List_First);
3056 entries = XGET(obj, MUIA_List_Entries);
3057 visible = XGET(obj, MUIA_List_Visible);
3059 new += wheely;
3061 if (new > entries - visible)
3063 new = entries - visible;
3066 if (new < 0)
3068 new = 0;
3071 if (new != first)
3073 set(obj, MUIA_List_First, new);
3077 /**************************************************************************
3078 MUIM_HandleEvent
3079 **************************************************************************/
3080 IPTR List__MUIM_HandleEvent(struct IClass *cl, Object *obj, struct MUIP_HandleEvent *msg)
3082 struct MUI_ListData *data = INST_DATA(cl, obj);
3083 struct MUI_List_TestPos_Result pos;
3084 LONG seltype, old_active, new_active, visible, first, last, i;
3085 IPTR result = 0;
3086 BOOL select = FALSE, clear = FALSE, range_select = FALSE, changing;
3087 WORD delta;
3088 typeof(msg->muikey) muikey = msg->muikey;
3090 new_active = old_active = XGET(obj, MUIA_List_Active);
3091 visible = XGET(obj, MUIA_List_Visible);
3093 if (muikey != MUIKEY_NONE)
3095 result = MUI_EventHandlerRC_Eat;
3097 /* Make keys behave differently in read-only mode */
3098 if (data->read_only)
3100 switch (muikey)
3102 case MUIKEY_TOP:
3103 muikey = MUIKEY_LINESTART;
3104 break;
3106 case MUIKEY_BOTTOM:
3107 muikey = MUIKEY_LINEEND;
3108 break;
3110 case MUIKEY_UP:
3111 muikey = MUIKEY_LEFT;
3112 break;
3114 case MUIKEY_DOWN:
3115 case MUIKEY_PRESS:
3116 muikey = MUIKEY_RIGHT;
3117 break;
3121 switch (muikey)
3123 case MUIKEY_TOGGLE:
3124 if (data->multiselect != MUIV_Listview_MultiSelect_None
3125 && !data->read_only)
3127 select = TRUE;
3128 data->click_column = data->def_click_column;
3129 new_active = MUIV_List_Active_Down;
3131 else
3133 DoMethod(obj, MUIM_List_Jump, 0);
3134 muikey = MUIKEY_NONE;
3136 break;
3138 case MUIKEY_TOP:
3139 new_active = MUIV_List_Active_Top;
3140 break;
3142 case MUIKEY_BOTTOM:
3143 new_active = MUIV_List_Active_Bottom;
3144 break;
3146 case MUIKEY_LEFT:
3147 case MUIKEY_WORDLEFT:
3148 DoMethod(obj, MUIM_List_Jump, MUIV_List_Jump_Up);
3149 break;
3151 case MUIKEY_RIGHT:
3152 case MUIKEY_WORDRIGHT:
3153 DoMethod(obj, MUIM_List_Jump, MUIV_List_Jump_Down);
3154 break;
3156 case MUIKEY_LINESTART:
3157 DoMethod(obj, MUIM_List_Jump, MUIV_List_Jump_Top);
3158 break;
3160 case MUIKEY_LINEEND:
3161 DoMethod(obj, MUIM_List_Jump, MUIV_List_Jump_Bottom);
3162 break;
3164 case MUIKEY_UP:
3165 new_active = MUIV_List_Active_Up;
3166 break;
3168 case MUIKEY_DOWN:
3169 new_active = MUIV_List_Active_Down;
3170 break;
3172 case MUIKEY_PAGEUP:
3173 if (data->read_only)
3174 DoWheelMove(cl, obj, -visible);
3175 else
3176 new_active = MUIV_List_Active_PageUp;
3177 break;
3179 case MUIKEY_PAGEDOWN:
3180 if (data->read_only)
3181 DoWheelMove(cl, obj, visible);
3182 else
3183 new_active = MUIV_List_Active_PageDown;
3184 break;
3186 default:
3187 result = 0;
3190 else if (msg->imsg)
3192 DoMethod(obj, MUIM_List_TestPos, msg->imsg->MouseX, msg->imsg->MouseY, (IPTR) &pos);
3194 switch (msg->imsg->Class)
3196 case IDCMP_MOUSEBUTTONS:
3197 if (msg->imsg->Code == SELECTDOWN)
3199 if (_isinobject(data->area, msg->imsg->MouseX, msg->imsg->MouseY))
3201 data->mouse_click = MOUSE_CLICK_ENTRY;
3203 if (!data->read_only && pos.entry != -1)
3205 new_active = pos.entry;
3207 clear = (data->multiselect == MUIV_Listview_MultiSelect_Shifted
3208 && (msg->imsg->Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)) == 0);
3209 seltype = clear ? MUIV_List_Select_On: MUIV_List_Select_Toggle;
3210 select = data->multiselect != MUIV_Listview_MultiSelect_None;
3212 /* Handle MUIA_Listview_ClickColumn */
3213 data->click_column = pos.column;
3214 superset(cl, obj, MUIA_Listview_ClickColumn,
3215 data->click_column);
3217 /* Handle double clicking */
3218 if (data->last_active == pos.entry
3219 && DoubleClick(data->last_secs, data->last_mics, msg->imsg->Seconds, msg->imsg->Micros))
3221 set(obj, MUIA_Listview_DoubleClick, TRUE);
3222 data->last_active = -1;
3223 data->last_secs = data->last_mics = 0;
3225 else
3227 data->last_active = pos.entry;
3228 data->last_secs = msg->imsg->Seconds;
3229 data->last_mics = msg->imsg->Micros;
3232 /* Look out for mouse movement, timer and
3233 inactive-window events while mouse button is
3234 down */
3235 DoMethod(_win(obj), MUIM_Window_RemEventHandler, (IPTR) &data->ehn);
3236 data->ehn.ehn_Events |= (IDCMP_MOUSEMOVE | IDCMP_INTUITICKS |IDCMP_INACTIVEWINDOW);
3237 DoMethod(_win(obj), MUIM_Window_AddEventHandler, (IPTR) &data->ehn);
3241 else
3243 /* Activate object */
3244 if (msg->imsg->Code == SELECTUP && data->mouse_click)
3246 set(_win(obj), MUIA_Window_ActiveObject, (IPTR)obj);
3247 data->mouse_click = 0;
3250 /* Restore normal event mask */
3251 DoMethod(_win(obj), MUIM_Window_RemEventHandler, (IPTR) &data->ehn);
3252 data->ehn.ehn_Events &= ~(IDCMP_MOUSEMOVE | IDCMP_INTUITICKS | IDCMP_INACTIVEWINDOW);
3253 DoMethod(_win(obj), MUIM_Window_AddEventHandler, (IPTR) &data->ehn);
3255 break;
3257 case IDCMP_MOUSEMOVE:
3258 case IDCMP_INTUITICKS:
3259 if (pos.flags & MUI_LPR_ABOVE)
3260 new_active = MUIV_List_Active_Up;
3261 else if (pos.flags & MUI_LPR_BELOW)
3262 new_active = MUIV_List_Active_Down;
3263 else
3264 new_active = pos.entry;
3266 select = new_active != old_active && data->multiselect != MUIV_Listview_MultiSelect_None;
3267 if (select)
3269 DoMethod(obj, MUIM_List_Select, MUIV_List_Select_Active, MUIV_List_Select_Ask, &seltype);
3270 range_select = new_active >= 0;
3273 break;
3275 case IDCMP_INACTIVEWINDOW:
3276 /* Stop listening for events we only listen to when mouse button is
3277 down: we will not be informed of the button being released */
3278 DoMethod(_win(obj), MUIM_Window_RemEventHandler, (IPTR) &data->ehn);
3279 data->ehn.ehn_Events &= ~(IDCMP_MOUSEMOVE | IDCMP_INTUITICKS | IDCMP_INACTIVEWINDOW);
3280 DoMethod(_win(obj), MUIM_Window_AddEventHandler, (IPTR) &data->ehn);
3281 break;
3283 case IDCMP_RAWKEY:
3284 /* Scroll wheel */
3285 if (_isinobject(data->vert, msg->imsg->MouseX, msg->imsg->MouseY))
3286 delta = 1;
3287 else if (_isinobject(data->area, msg->imsg->MouseX, msg->imsg->MouseY))
3288 delta = 4;
3289 else
3290 delta = 0;
3292 if (delta != 0)
3294 switch (msg->imsg->Code)
3296 case RAWKEY_NM_WHEEL_UP:
3297 DoWheelMove(cl, obj, -delta);
3298 break;
3300 case RAWKEY_NM_WHEEL_DOWN:
3301 DoWheelMove(cl, obj, delta);
3302 break;
3304 result = MUI_EventHandlerRC_Eat;
3306 break;
3310 /* Decide in advance if any selections may change */
3311 changing = clear || muikey == MUIKEY_TOGGLE || select;
3313 /* Change selected and active entries */
3314 if (changing)
3315 set(obj, MUIA_Listview_SelectChange, TRUE);
3317 if (clear)
3319 DoMethod(obj, MUIM_List_Select, MUIV_List_Select_All, MUIV_List_Select_Off, NULL);
3322 if (muikey == MUIKEY_TOGGLE)
3324 DoMethod(obj, MUIM_List_Select, MUIV_List_Select_Active, MUIV_List_Select_Toggle, NULL);
3325 select = FALSE;
3328 if (new_active != old_active)
3329 set(obj, MUIA_List_Active, new_active);
3331 if (select)
3333 if (range_select)
3335 if (old_active < new_active)
3336 first = old_active + 1, last = new_active;
3337 else
3338 first = new_active, last = old_active - 1;
3339 for (i = first; i <= last; i++)
3340 DoMethod(obj, MUIM_List_Select, i, seltype, NULL);
3342 else
3343 DoMethod(obj, MUIM_List_Select, MUIV_List_Select_Active, seltype, NULL);
3346 if (changing)
3347 set(obj, MUIA_Listview_SelectChange, FALSE);
3349 return result;
3352 /**************************************************************************
3353 Dispatcher
3354 **************************************************************************/
3355 BOOPSI_DISPATCHER(IPTR, List_Dispatcher, cl, obj, msg)
3357 switch (msg->MethodID)
3359 case OM_NEW:
3360 return List__OM_NEW(cl, obj, (struct opSet *)msg);
3361 case OM_DISPOSE:
3362 return List__OM_DISPOSE(cl, obj, msg);
3363 case OM_SET:
3364 return List__OM_SET(cl, obj, (struct opSet *)msg);
3365 case OM_GET:
3366 return List__OM_GET(cl, obj, (struct opGet *)msg);
3368 case MUIM_Setup:
3369 return List__MUIM_Setup(cl, obj, (struct MUIP_Setup *)msg);
3370 case MUIM_Cleanup:
3371 return List__MUIM_Cleanup(cl, obj, (struct MUIP_Cleanup *)msg);
3372 case MUIM_HandleEvent:
3373 return List__MUIM_HandleEvent(cl, obj, (struct MUIP_HandleEvent *)msg);
3374 case MUIM_AskMinMax:
3375 return List__MUIM_AskMinMax(cl, obj, (struct MUIP_AskMinMax *)msg);
3376 case MUIM_Show:
3377 return List__MUIM_Show(cl, obj, (struct MUIP_Show *)msg);
3378 case MUIM_Hide:
3379 return List__MUIM_Hide(cl, obj, (struct MUIP_Hide *)msg);
3380 case MUIM_Draw:
3381 return List__MUIM_Draw(cl, obj, (struct MUIP_Draw *)msg);
3382 case MUIM_Layout:
3383 return List__MUIM_Layout(cl, obj, (struct MUIP_Layout *)msg);
3384 case MUIM_List_Clear:
3385 return List__MUIM_Clear(cl, obj, (struct MUIP_List_Clear *)msg);
3386 case MUIM_List_Sort:
3387 return List__MUIM_Sort(cl, obj, (struct MUIP_List_Sort *)msg);
3388 case MUIM_List_Exchange:
3389 return List__MUIM_Exchange(cl, obj,
3390 (struct MUIP_List_Exchange *)msg);
3391 case MUIM_List_Insert:
3392 return List__MUIM_Insert(cl, obj, (APTR) msg);
3393 case MUIM_List_InsertSingle:
3394 return List__MUIM_InsertSingle(cl, obj, (APTR) msg);
3395 case MUIM_List_GetEntry:
3396 return List__MUIM_GetEntry(cl, obj, (APTR) msg);
3397 case MUIM_List_Redraw:
3398 return List__MUIM_Redraw(cl, obj, (APTR) msg);
3399 case MUIM_List_Remove:
3400 return List__MUIM_Remove(cl, obj, (APTR) msg);
3401 case MUIM_List_Select:
3402 return List__MUIM_Select(cl, obj, (APTR) msg);
3403 case MUIM_List_Construct:
3404 return List__MUIM_Construct(cl, obj, (APTR) msg);
3405 case MUIM_List_Destruct:
3406 return List__MUIM_Destruct(cl, obj, (APTR) msg);
3407 case MUIM_List_Compare:
3408 return List__MUIM_Compare(cl, obj, (APTR) msg);
3409 case MUIM_List_Display:
3410 return List__MUIM_Display(cl, obj, (APTR) msg);
3411 case MUIM_List_SelectChange:
3412 return List__MUIM_SelectChange(cl, obj, (APTR) msg);
3413 case MUIM_List_CreateImage:
3414 return List__MUIM_CreateImage(cl, obj, (APTR) msg);
3415 case MUIM_List_DeleteImage:
3416 return List__MUIM_DeleteImage(cl, obj, (APTR) msg);
3417 case MUIM_List_Jump:
3418 return List__MUIM_Jump(cl, obj, (APTR) msg);
3419 case MUIM_List_Move:
3420 return List__MUIM_Move(cl, obj, (struct MUIP_List_Move *)msg);
3421 case MUIM_List_NextSelected:
3422 return List__MUIM_NextSelected(cl, obj,
3423 (struct MUIP_List_NextSelected *)msg);
3424 case MUIM_List_TestPos:
3425 return List__MUIM_TestPos(cl, obj, (APTR) msg);
3426 case MUIM_DragQuery:
3427 return List__MUIM_DragQuery(cl, obj, (APTR) msg);
3428 case MUIM_DragFinish:
3429 return List__MUIM_DragFinish(cl, obj, (APTR) msg);
3430 case MUIM_DragReport:
3431 return List__MUIM_DragReport(cl, obj, (APTR) msg);
3432 case MUIM_DragDrop:
3433 return List__MUIM_DragDrop(cl, obj, (APTR) msg);
3434 case MUIM_CreateDragImage:
3435 return List__MUIM_CreateDragImage(cl, obj, (APTR) msg);
3438 return DoSuperMethodA(cl, obj, msg);
3440 BOOPSI_DISPATCHER_END
3443 * Class descriptor.
3445 const struct __MUIBuiltinClass _MUI_List_desc =
3447 MUIC_List,
3448 MUIC_Group,
3449 sizeof(struct MUI_ListData),
3450 (void *) List_Dispatcher