Zune: List.class - make some guesses about uninitialized variables
[AROS.git] / workbench / libs / muimaster / classes / list.c
blobb208af3ce90ebd3e4b7363a4aa9a78a1ebaa12bf
1 /*
2 Copyright © 2002-2013, 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 <clib/alib_protos.h>
14 #include <proto/exec.h>
15 #include <proto/graphics.h>
16 #include <proto/utility.h>
17 #include <proto/dos.h>
18 #include <proto/intuition.h>
19 #include <proto/muimaster.h>
21 /* #define MYDEBUG 1 */
22 #include "debug.h"
23 #include "mui.h"
24 #include "muimaster_intern.h"
25 #include "support.h"
26 #include "imspec.h"
27 #include "textengine.h"
28 #include "listimage.h"
29 #include "prefs.h"
31 extern struct Library *MUIMasterBase;
33 #define ENTRY_TITLE (-1)
35 #define FORMAT_TEMPLATE "DELTA=D/N,PREPARSE=P/K,WEIGHT=W/N,MINWIDTH=MIW/N," \
36 "MAXWIDTH=MAW/N,COL=C/N,BAR/S"
38 #define BAR_WIDTH 2
40 enum
42 ARG_DELTA,
43 ARG_PREPARSE,
44 ARG_WEIGHT,
45 ARG_MINWIDTH,
46 ARG_MAXWIDTH,
47 ARG_COL,
48 ARG_BAR,
49 ARG_CNT
53 struct ListEntry
55 APTR data;
56 LONG *widths; /* Widths of the columns */
57 LONG width; /* Line width */
58 LONG height; /* Line height */
59 WORD flags; /* see below */
62 #define ENTRY_SELECTED (1<<0)
65 struct ColumnInfo
67 int colno; /* Column number */
68 int user_width; /* user set width; -1 if entry width */
69 int min_width; /* min width percentage */
70 int max_width; /* min width percentage */
71 int weight;
72 int delta; /* ignored for the first and last column, defaults to 4 */
73 int bar;
74 STRPTR preparse;
75 int entries_width; /* width of the entries (maximum of all widths) */
78 struct MUI_ImageSpec_intern;
80 struct MUI_ListData
82 /* bool attrs */
83 ULONG flags;
85 APTR intern_pool; /* The internal pool which the class has allocated */
86 LONG intern_puddle_size;
87 LONG intern_thresh_size;
88 APTR pool; /* the pool which is used to allocate list entries */
90 struct Hook *construct_hook;
91 struct Hook *compare_hook;
92 struct Hook *destruct_hook;
93 struct Hook *display_hook;
94 struct Hook *multi_test_hook;
96 struct Hook default_compare_hook;
98 /* List management, currently we use a simple flat array, which is not
99 * good if many entries are inserted/deleted */
100 LONG entries_num; /* Number of Entries in the list */
101 LONG entries_allocated;
102 struct ListEntry **entries;
104 LONG entries_first; /* first visible entry */
105 LONG entries_visible; /* number of visible entries,
106 * determined at MUIM_Layout */
107 LONG entries_active;
108 LONG insert_position; /* pos of the last insertion */
110 LONG entry_maxheight; /* Maximum height of an entry */
111 ULONG entry_minheight; /* from MUIA_List_MinLineHeight */
113 LONG entries_totalheight;
114 LONG entries_maxwidth;
116 LONG vertprop_entries;
117 LONG vertprop_visible;
118 LONG vertprop_first;
120 LONG confirm_entries_num; /* These are the correct entries num, used
121 * so you cannot set MUIA_List_Entries to
122 * wrong values */
124 LONG entries_top_pixel; /* Where the entries start */
126 /* Column managment, is allocated by ParseListFormat() and freed
127 * by CleanListFormat() */
128 STRPTR format;
129 LONG columns; /* Number of columns the list has */
130 struct ColumnInfo *ci;
131 STRPTR *preparses;
132 STRPTR *strings; /* the strings for the display function, one
133 * more than needed (for the entry position) */
135 /* Titlestuff */
136 int title_height; /* The complete height of the title */
137 STRPTR title; /* On single comlums this is the title, otherwise 1 */
139 /* Cursor images */
140 struct MUI_ImageSpec_intern *list_cursor;
141 struct MUI_ImageSpec_intern *list_select;
142 struct MUI_ImageSpec_intern *list_selcur;
144 /* Render optimization */
145 int update; /* 1 - update everything, 2 - redraw entry at update_pos,
146 * 3 - scroll to current entries_first (old value is in
147 * update_pos) */
148 int update_pos;
150 LONG drop_mark_y;
152 /* list images */
153 struct MinList images;
155 /* user prefs */
156 ListviewRefresh prefs_refresh;
157 UWORD prefs_linespacing;
158 BOOL prefs_smoothed;
159 UWORD prefs_smoothval;
162 #define LIST_ADJUSTWIDTH (1<<0)
163 #define LIST_ADJUSTHEIGHT (1<<1)
164 #define LIST_AUTOVISIBLE (1<<2)
165 #define LIST_DRAGSORTABLE (1<<3)
166 #define LIST_SHOWDROPMARKS (1<<4)
167 #define LIST_QUIET (1<<5)
170 /****** List.mui/MUIA_List_CompareHook ***************************************
172 * NAME
173 * MUIA_List_CompareHook -- (V4) [IS.], struct Hook *
175 * FUNCTION
176 * The provided hook indicates the sort ordering of two list entries.
177 * The hook receives list-entry data pointers as its second and third
178 * arguments. The hook should return a negative value if the first entry
179 * should be placed before the second entry, a positive value if the
180 * first entry should be placed after the second entry, and zero if the
181 * entries are equal.
183 * In addition to being used internally for sorting operations, this hook
184 * will be called when MUIM_List_Compare is externally invoked.
186 * If this attribute is not specified or is set to NULL, all list entries
187 * must be strings.
189 ******************************************************************************
193 /****** List.mui/MUIA_List_MultiTestHook *************************************
195 * NAME
196 * MUIA_List_MultiTestHook -- (V4) [IS.], struct Hook *
198 * FUNCTION
199 * The provided hook indicates whether a particular list entry
200 * may be multiselected. The hook receives the list-entry data pointer as
201 * its third argument, and returns a Boolean value. If this attribute is
202 * not specified or is set to NULL, all list entries are considered
203 * multi-selectable.
205 * Whenever an entry is about to be selected, this hook is called if
206 * there are other entries already selected. If the hook returns TRUE,
207 * the entry may be multi-selected; if the hook returns FALSE, the entry
208 * remains unselected.
210 * Additionally, if a non-multi-selectable entry has been selected (as
211 * the only selected entry in the list), any attempt to select an
212 * additional entry will fail.
214 ******************************************************************************
218 /**************************************************************************
219 Allocate a single list entry, does not initialize it (except the pointer)
220 **************************************************************************/
221 static struct ListEntry *AllocListEntry(struct MUI_ListData *data)
223 ULONG *mem;
224 struct ListEntry *le;
225 int size = sizeof(struct ListEntry) + sizeof(LONG) * data->columns + 4;
226 /* sizeinfo */
227 LONG j;
229 mem = AllocPooled(data->pool, size);
230 if (!mem)
231 return NULL;
232 D(bug("List AllocListEntry %p, %ld bytes\n", mem, size));
234 mem[0] = size; /* Save the size */
235 le = (struct ListEntry *)(mem + 1);
236 le->widths = (LONG *) (le + 1);
238 /* Initialize fields */
239 le->height = 0;
240 le->width = 0;
241 le->flags = 0;
242 for (j = 0; j < data->columns; j++)
243 le->widths[j] = 0;
245 return le;
248 /**************************************************************************
249 Deallocate a single list entry, does not deinitialize it
250 **************************************************************************/
251 static void FreeListEntry(struct MUI_ListData *data,
252 struct ListEntry *entry)
254 ULONG *mem = ((ULONG *) entry) - 1;
255 D(bug("FreeListEntry %p size=%ld\n", mem, mem[0]));
256 FreePooled(data->pool, mem, mem[0]);
259 /**************************************************************************
260 Ensures that we there can be at least the given amount of entries within
261 the list. Returns 0 if not. It also allocates the space for the title.
262 It can be accesses with data->entries[ENTRY_TITLE]
263 **************************************************************************/
264 static int SetListSize(struct MUI_ListData *data, LONG size)
266 struct ListEntry **new_entries;
267 int new_entries_allocated;
269 if (size + 1 <= data->entries_allocated)
270 return 1;
272 new_entries_allocated = data->entries_allocated * 2 + 4;
273 if (new_entries_allocated < size + 1)
274 new_entries_allocated = size + 1 + 10; /* 10 is just random */
276 D(bug("List %p : SetListSize allocating %ld bytes\n", data,
277 new_entries_allocated * sizeof(struct ListEntry *)));
278 new_entries =
279 AllocVec(new_entries_allocated * sizeof(struct ListEntry *), 0);
280 if (NULL == new_entries)
281 return 0;
282 if (data->entries)
284 CopyMem(data->entries - 1, new_entries,
285 (data->entries_num + 1) * sizeof(struct ListEntry *));
286 FreeVec(data->entries - 1);
288 data->entries = new_entries + 1;
289 data->entries_allocated = new_entries_allocated;
290 return 1;
293 /**************************************************************************
294 Prepares the insertion of count entries at pos.
295 This function doesn't care if there is enough space in the datastructure.
296 SetListSize() must be used first.
297 With current implementation, this call will never fail
298 **************************************************************************/
299 static int PrepareInsertListEntries(struct MUI_ListData *data, int pos,
300 int count)
302 memmove(&data->entries[pos + count], &data->entries[pos],
303 (data->entries_num - pos) * sizeof(struct ListEntry *));
304 return 1;
307 /**************************************************************************
308 Removes count (already deinitalized) list entries starting az pos.
309 **************************************************************************/
310 static void RemoveListEntries(struct MUI_ListData *data, int pos, int count)
312 // FIXME: segfault if entries_num = pos = count = 1
313 memmove(&data->entries[pos], &data->entries[pos + count],
314 (data->entries_num - (pos + count)) * sizeof(struct ListEntry *));
317 /**************************************************************************
318 Frees all memory allocated by ParseListFormat()
319 **************************************************************************/
320 static void FreeListFormat(struct MUI_ListData *data)
322 int i;
324 if (data->ci)
326 for (i = 0; i < data->columns; i++)
328 FreeVec(data->ci[i].preparse);
329 data->ci[i].preparse = NULL;
331 FreeVec(data->ci);
332 data->ci = NULL;
334 FreeVec(data->preparses);
335 data->preparses = NULL;
336 if (data->strings)
338 FreeVec(data->strings - 1);
339 data->strings = NULL;
341 data->columns = 0;
344 /**************************************************************************
345 Parses the given format string (also frees a previouly parsed format).
346 Return 0 on failure.
347 **************************************************************************/
348 static int ParseListFormat(struct MUI_ListData *data, STRPTR format)
350 int new_columns, i;
351 STRPTR ptr;
352 STRPTR format_sep;
353 char c;
355 IPTR args[ARG_CNT];
356 struct RDArgs *rdargs;
358 if (!format)
359 format = (STRPTR) "";
361 ptr = format;
363 FreeListFormat(data);
365 new_columns = 1;
367 /* Count the number of columns first */
368 while ((c = *ptr++))
369 if (c == ',')
370 new_columns++;
372 if (!(data->preparses =
373 AllocVec((new_columns + 10) * sizeof(STRPTR), 0)))
374 return 0;
376 if (!(data->strings = AllocVec((new_columns + 1 + 10)
377 * sizeof(STRPTR), 0))) /* hold enough space also for the entry pos,
378 * used by orginal MUI and also some
379 * security space */
380 return 0;
382 if (!(data->ci = AllocVec(new_columns * sizeof(struct ColumnInfo), 0)))
383 return 0;
385 // set defaults
386 for (i = 0; i < new_columns; i++)
388 data->ci[i].colno = -1; // -1 means: use unassigned column
389 data->ci[i].weight = 100;
390 data->ci[i].delta = 4;
391 data->ci[i].min_width = -1;
392 data->ci[i].max_width = -1;
393 data->ci[i].user_width = -1;
394 data->ci[i].bar = FALSE;
395 data->ci[i].preparse = NULL;
398 if ((format_sep = StrDup(format)) != 0)
400 for (i = 0; format_sep[i] != '\0'; i++)
402 if (format_sep[i] == ',')
403 format_sep[i] = '\0';
406 if ((rdargs = AllocDosObject(DOS_RDARGS, NULL)) != 0)
408 ptr = format_sep;
409 i = 0;
412 rdargs->RDA_Source.CS_Buffer = ptr;
413 rdargs->RDA_Source.CS_Length = strlen(ptr);
414 rdargs->RDA_Source.CS_CurChr = 0;
415 rdargs->RDA_DAList = 0;
416 rdargs->RDA_Buffer = NULL;
417 rdargs->RDA_BufSiz = 0;
418 rdargs->RDA_ExtHelp = NULL;
419 rdargs->RDA_Flags = 0;
421 memset(args, 0, sizeof args);
422 if (ReadArgs(FORMAT_TEMPLATE, args, rdargs))
424 if (args[ARG_COL])
425 data->ci[i].colno = *(LONG *) args[ARG_COL];
426 if (args[ARG_WEIGHT])
427 data->ci[i].weight = *(LONG *) args[ARG_WEIGHT];
428 if (args[ARG_DELTA])
429 data->ci[i].delta = *(LONG *) args[ARG_DELTA];
430 if (args[ARG_MINWIDTH])
431 data->ci[i].min_width =
432 *(LONG *) args[ARG_MINWIDTH];
433 if (args[ARG_MAXWIDTH])
434 data->ci[i].max_width =
435 *(LONG *) args[ARG_MAXWIDTH];
436 data->ci[i].bar = args[ARG_BAR];
437 if (args[ARG_PREPARSE])
438 data->ci[i].preparse =
439 StrDup((STRPTR) args[ARG_PREPARSE]);
441 FreeArgs(rdargs);
443 ptr += strlen(ptr) + 1;
444 i++;
446 while (i < new_columns);
447 FreeDosObject(DOS_RDARGS, rdargs);
449 FreeVec(format_sep);
452 for (i = 0; i < new_columns; i++)
454 D(bug("colno %d weight %d delta %d preparse %s\n",
455 data->ci[i].colno, data->ci[i].weight, data->ci[i].delta,
456 data->ci[i].preparse));
459 data->columns = new_columns;
460 data->strings++; /* Skip entry pos */
462 return 1;
465 /**************************************************************************
466 Call the MUIM_List_Display for the given entry. It fills out
467 data->string and data->preparses
468 **************************************************************************/
469 static void DisplayEntry(struct IClass *cl, Object *obj, int entry_pos)
471 struct MUI_ListData *data = INST_DATA(cl, obj);
472 APTR entry_data;
473 int col;
475 for (col = 0; col < data->columns; col++)
476 data->preparses[col] = data->ci[col].preparse;
478 if (entry_pos == ENTRY_TITLE)
480 if ((data->columns == 1) && (data->title != (STRPTR) 1))
482 *data->strings = data->title;
483 return;
485 entry_data = NULL; /* it's a title request */
487 else
488 entry_data = data->entries[entry_pos]->data;
490 /* Get the display formation */
491 DoMethod(obj, MUIM_List_Display, (IPTR) entry_data,
492 (IPTR) data->strings, entry_pos, (IPTR) data->preparses);
495 /**************************************************************************
496 Determine the dims of a single entry and adapt the columninfo according
497 to it. pos might be ENTRY_TITLE. Returns 0 if pos entry needs to
498 be redrawn after this operation, 1 if all entries need to be redrawn.
499 **************************************************************************/
500 static int CalcDimsOfEntry(struct IClass *cl, Object *obj, int pos)
502 struct MUI_ListData *data = INST_DATA(cl, obj);
503 struct ListEntry *entry = data->entries[pos];
504 int j;
505 int ret = 0;
507 if (!entry)
508 return ret;
510 if (!(_flags(obj) & MADF_SETUP))
511 return ret;
513 DisplayEntry(cl, obj, pos);
515 /* Set height to at least minheight */
516 if (data->entries[pos]->height < data->entry_minheight)
517 data->entries[pos]->height = data->entry_minheight;
519 for (j = 0; j < data->columns; j++)
521 ZText *text =
522 zune_text_new(data->preparses[j], data->strings[j],
523 ZTEXT_ARG_NONE, 0);
524 if (text != NULL)
526 zune_text_get_bounds(text, obj);
528 if (text->height > data->entries[pos]->height)
530 data->entries[pos]->height = text->height;
531 /* entry height changed, redraw all entries later */
532 ret = 1;
534 data->entries[pos]->widths[j] = text->width;
536 if (text->width > data->ci[j].entries_width)
538 /* This columns width is bigger than the other in the same
539 * columns, so we store this value
541 data->ci[j].entries_width = text->width;
542 /* column width changed, redraw all entries later */
543 ret = 1;
546 zune_text_destroy(text);
549 if (data->entries[pos]->height > data->entry_maxheight)
551 data->entry_maxheight = data->entries[pos]->height;
552 /* maximum entry height changed, redraw all entries later */
553 ret = 1;
556 return ret;
559 /**************************************************************************
560 Determine the widths of the entries
561 **************************************************************************/
562 static void CalcWidths(struct IClass *cl, Object *obj)
564 int i, j;
565 struct MUI_ListData *data = INST_DATA(cl, obj);
567 if (!(_flags(obj) & MADF_SETUP))
568 return;
570 for (j = 0; j < data->columns; j++)
571 data->ci[j].entries_width = 0;
573 data->entry_maxheight = 0;
574 data->entries_totalheight = 0;
575 data->entries_maxwidth = 0;
577 for (i = (data->title ? ENTRY_TITLE : 0); i < data->entries_num; i++)
579 CalcDimsOfEntry(cl, obj, i);
580 data->entries_totalheight += data->entries[i]->height;
583 for (j = 0; j < data->columns; j++)
584 data->entries_maxwidth += data->ci[j].entries_width;
586 if (!data->entry_maxheight)
587 data->entry_maxheight = 1;
590 /**************************************************************************
591 Calculates the number of visible entry lines. Returns 1 if it has
592 changed
593 **************************************************************************/
594 static int CalcVertVisible(struct IClass *cl, Object *obj)
596 struct MUI_ListData *data = INST_DATA(cl, obj);
597 int old_entries_visible = data->entries_visible;
598 int old_entries_top_pixel = data->entries_top_pixel;
600 data->entries_visible = (_mheight(obj) - data->title_height)
601 / (data->entry_maxheight /* + data->prefs_linespacing */ );
603 /* Distribute extra vertical space evenly between top and bottom of
604 * list */
606 data->entries_top_pixel = _mtop(obj) + data->title_height
607 + (_mheight(obj) - data->title_height
609 data->entries_visible *
610 (data->entry_maxheight /* + data->prefs_linespacing */ )) / 2;
612 return (old_entries_visible != data->entries_visible)
613 || (old_entries_top_pixel != data->entries_top_pixel);
616 /**************************************************************************
617 Default hook to compare two list entries. Works for strings only.
618 **************************************************************************/
619 AROS_UFH3S(int, default_compare_func,
620 AROS_UFHA(struct Hook *, h, A0),
621 AROS_UFHA(char *, s2, A2),
622 AROS_UFHA(char *, s1, A1))
624 AROS_USERFUNC_INIT
626 return Stricmp(s1, s2);
628 AROS_USERFUNC_EXIT
631 /**************************************************************************
632 OM_NEW
633 **************************************************************************/
634 IPTR List__OM_NEW(struct IClass *cl, Object *obj, struct opSet *msg)
636 struct MUI_ListData *data;
637 struct TagItem *tag;
638 struct TagItem *tags;
639 APTR *array = NULL;
640 LONG new_entries_active = MUIV_List_Active_Off;
642 obj = (Object *) DoSuperNewTags(cl, obj, NULL,
643 MUIA_Font, MUIV_Font_List,
644 MUIA_Background, MUII_ListBack, TAG_MORE, (IPTR) msg->ops_AttrList);
645 if (!obj)
646 return FALSE;
648 data = INST_DATA(cl, obj);
650 data->columns = 1;
651 data->entries_active = MUIV_List_Active_Off;
652 data->intern_puddle_size = 2008;
653 data->intern_thresh_size = 1024;
654 data->default_compare_hook.h_Entry = (HOOKFUNC) default_compare_func;
655 data->default_compare_hook.h_SubEntry = 0;
656 data->compare_hook = &(data->default_compare_hook);
658 /* parse initial taglist */
659 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
661 switch (tag->ti_Tag)
663 case MUIA_List_Active:
664 new_entries_active = tag->ti_Data;
665 break;
667 case MUIA_List_Pool:
668 data->pool = (APTR) tag->ti_Data;
669 break;
671 case MUIA_List_PoolPuddleSize:
672 data->intern_puddle_size = tag->ti_Data;
673 break;
675 case MUIA_List_PoolThreshSize:
676 data->intern_thresh_size = tag->ti_Data;
677 break;
679 case MUIA_List_CompareHook:
680 data->compare_hook = (struct Hook *)tag->ti_Data;
681 if (data->compare_hook == NULL)
682 data->compare_hook = &data->default_compare_hook;
683 break;
685 case MUIA_List_ConstructHook:
686 data->construct_hook = (struct Hook *)tag->ti_Data;
687 break;
689 case MUIA_List_DestructHook:
690 data->destruct_hook = (struct Hook *)tag->ti_Data;
691 break;
693 case MUIA_List_DisplayHook:
694 data->display_hook = (struct Hook *)tag->ti_Data;
695 break;
697 case MUIA_List_MultiTestHook:
698 data->multi_test_hook = (struct Hook *)tag->ti_Data;
699 break;
701 case MUIA_List_SourceArray:
702 array = (APTR *) tag->ti_Data;
703 break;
705 case MUIA_List_Format:
706 data->format = StrDup((STRPTR) tag->ti_Data);
707 break;
709 case MUIA_List_Title:
710 data->title = (STRPTR) tag->ti_Data;
711 break;
713 case MUIA_List_MinLineHeight:
714 data->entry_minheight = tag->ti_Data;
715 break;
717 case MUIA_List_AdjustHeight:
718 _handle_bool_tag(data->flags, tag->ti_Data, LIST_ADJUSTHEIGHT);
719 break;
721 case MUIA_List_AdjustWidth:
722 _handle_bool_tag(data->flags, tag->ti_Data, LIST_ADJUSTWIDTH);
723 break;
725 case MUIA_List_AutoVisible:
726 _handle_bool_tag(data->flags, tag->ti_Data, LIST_AUTOVISIBLE);
727 break;
729 case MUIA_List_ShowDropMarks:
730 _handle_bool_tag(data->flags, tag->ti_Data, LIST_SHOWDROPMARKS);
731 break;
733 case MUIA_List_DragSortable:
734 _handle_bool_tag(data->flags, tag->ti_Data, LIST_DRAGSORTABLE);
735 set(obj, MUIA_Draggable, tag->ti_Data);
736 break;
740 if (!data->pool)
742 /* No memory pool given, so we create our own */
743 data->pool = data->intern_pool =
744 CreatePool(0, data->intern_puddle_size,
745 data->intern_thresh_size);
746 if (!data->pool)
748 CoerceMethod(cl, obj, OM_DISPOSE);
749 return 0;
753 /* parse the list format */
754 if (!(ParseListFormat(data, data->format)))
756 CoerceMethod(cl, obj, OM_DISPOSE);
757 return 0;
760 /* This is neccessary for at least the title */
761 if (!SetListSize(data, 0))
763 CoerceMethod(cl, obj, OM_DISPOSE);
764 return 0;
767 if (data->title)
769 if (!(data->entries[ENTRY_TITLE] = AllocListEntry(data)))
771 CoerceMethod(cl, obj, OM_DISPOSE);
772 return 0;
775 else
776 data->entries[ENTRY_TITLE] = NULL;
779 if (array)
781 int i;
782 /* Count the number of elements */
783 for (i = 0; array[i] != NULL; i++)
785 /* Insert them */
786 DoMethod(obj, MUIM_List_Insert, (IPTR) array, i,
787 MUIV_List_Insert_Top);
791 if ((data->entries_num) && (new_entries_active != MUIV_List_Active_Off))
793 switch (new_entries_active)
795 case MUIV_List_Active_Top:
796 new_entries_active = 0;
797 break;
799 case MUIV_List_Active_Bottom:
800 new_entries_active = data->entries_num - 1;
801 break;
804 if (new_entries_active < 0)
805 new_entries_active = 0;
806 else if (new_entries_active >= data->entries_num)
807 new_entries_active = data->entries_num - 1;
809 data->entries_active = new_entries_active;
810 /* Selected entry will be moved into visible area */
813 NewList((struct List *)&data->images);
815 D(bug("List_New(%lx)\n", obj));
817 return (IPTR) obj;
820 /**************************************************************************
821 OM_DISPOSE
822 **************************************************************************/
823 IPTR List__OM_DISPOSE(struct IClass *cl, Object *obj, Msg msg)
825 struct MUI_ListData *data = INST_DATA(cl, obj);
827 D(bug("List Dispose\n"));
829 /* Call destruct method for every entry and free the entries manually
830 * to avoid notification */
831 while (data->confirm_entries_num)
833 struct ListEntry *lentry =
834 data->entries[--data->confirm_entries_num];
835 DoMethod(obj, MUIM_List_Destruct, (IPTR) lentry->data,
836 (IPTR) data->pool);
837 FreeListEntry(data, lentry);
840 if (data->intern_pool)
841 DeletePool(data->intern_pool);
842 if (data->entries)
843 FreeVec(data->entries - 1);
844 /* title is currently before all other elements */
846 FreeListFormat(data);
847 FreeVec(data->format);
849 return DoSuperMethodA(cl, obj, msg);
853 /**************************************************************************
854 OM_SET
855 **************************************************************************/
856 IPTR List__OM_SET(struct IClass *cl, Object *obj, struct opSet *msg)
858 struct MUI_ListData *data = INST_DATA(cl, obj);
859 struct TagItem *tag;
860 struct TagItem *tags;
862 /* parse taglist */
863 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
865 switch (tag->ti_Tag)
867 case MUIA_List_CompareHook:
868 data->compare_hook = (struct Hook *)tag->ti_Data;
869 if (data->compare_hook == NULL)
870 data->compare_hook = &data->default_compare_hook;
871 break;
873 case MUIA_List_ConstructHook:
874 data->construct_hook = (struct Hook *)tag->ti_Data;
875 break;
877 case MUIA_List_DestructHook:
878 data->destruct_hook = (struct Hook *)tag->ti_Data;
879 break;
881 case MUIA_List_DisplayHook:
882 data->display_hook = (struct Hook *)tag->ti_Data;
883 break;
885 case MUIA_List_MultiTestHook:
886 data->multi_test_hook = (struct Hook *)tag->ti_Data;
887 if (data->multi_test_hook != NULL)
889 /* Clearing current selections is the easiest way to keep
890 * selections consistent with the new hook */
891 DoMethod(obj, MUIM_List_Select, MUIV_List_Select_All,
892 MUIV_List_Select_Off, NULL);
894 break;
896 case MUIA_List_VertProp_First:
897 data->vertprop_first = tag->ti_Data;
898 if (data->entries_first != tag->ti_Data)
900 set(obj, MUIA_List_First, tag->ti_Data);
902 break;
904 case MUIA_List_Format:
905 data->format = StrDup((STRPTR) tag->ti_Data);
906 ParseListFormat(data, data->format);
907 // FIXME: should we check for errors?
908 DoMethod(obj, MUIM_List_Redraw, MUIV_List_Redraw_All);
909 break;
911 case MUIA_List_VertProp_Entries:
912 data->vertprop_entries = tag->ti_Data;
913 break;
915 case MUIA_List_VertProp_Visible:
916 data->vertprop_visible = tag->ti_Data;
917 data->entries_visible = tag->ti_Data;
918 break;
920 case MUIA_List_Active:
922 LONG new_entries_active = tag->ti_Data;
924 if ((data->entries_num)
925 && (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;
937 case MUIV_List_Active_Up:
938 new_entries_active = data->entries_active - 1;
939 break;
941 case MUIV_List_Active_Down:
942 new_entries_active = data->entries_active + 1;
943 break;
945 case MUIV_List_Active_PageUp:
946 new_entries_active =
947 data->entries_active - data->entries_visible;
948 break;
950 case MUIV_List_Active_PageDown:
951 new_entries_active =
952 data->entries_active + data->entries_visible;
953 break;
956 if (new_entries_active < 0)
957 new_entries_active = 0;
958 else if (new_entries_active >= data->entries_num)
959 new_entries_active = data->entries_num - 1;
961 else
962 new_entries_active = -1;
964 if (data->entries_active != new_entries_active)
966 LONG old = data->entries_active;
967 data->entries_active = new_entries_active;
969 /* Selectchange stuff */
970 if (new_entries_active != -1)
972 DoMethod(obj, MUIM_List_SelectChange,
973 new_entries_active, MUIV_List_Select_On, 0);
974 DoMethod(obj, MUIM_List_SelectChange,
975 new_entries_active, MUIV_List_Select_Active, 0);
977 else
978 DoMethod(obj, MUIM_List_SelectChange,
979 MUIV_List_Active_Off, MUIV_List_Select_Off, 0);
981 set(obj, MUIA_Listview_SelectChange, TRUE);
983 data->update = 2;
984 data->update_pos = old;
985 MUI_Redraw(obj, MADF_DRAWUPDATE);
986 data->update = 2;
987 data->update_pos = data->entries_active;
988 MUI_Redraw(obj, MADF_DRAWUPDATE);
990 /* Make new active entry visible (if there is one and
991 list is visible) */
992 if (new_entries_active != -1
993 && (_flags(obj) & MADF_SETUP))
995 DoMethod(obj, MUIM_List_Jump,
996 MUIV_List_Jump_Active);
1000 break;
1002 case MUIA_List_First:
1003 data->update_pos = data->entries_first;
1004 data->update = 3;
1005 data->entries_first = tag->ti_Data;
1007 MUI_Redraw(obj, MADF_DRAWUPDATE);
1008 if ((data->vertprop_first != tag->ti_Data)
1009 && (!(data->flags & LIST_QUIET)))
1011 set(obj, MUIA_List_VertProp_First, tag->ti_Data);
1013 break;
1015 case MUIA_List_Visible: /* Shouldn't be settable? */
1016 if (data->vertprop_visible != tag->ti_Data)
1017 set(obj, MUIA_List_VertProp_Visible, tag->ti_Data);
1018 break;
1020 case MUIA_List_Entries:
1021 if (data->confirm_entries_num == tag->ti_Data)
1023 data->entries_num = tag->ti_Data;
1024 if (!(data->flags & LIST_QUIET))
1026 set(obj, MUIA_List_VertProp_Entries, data->entries_num);
1029 else
1031 D(bug("Bug: confirm_entries != MUIA_List_Entries!\n"));
1033 break;
1035 case MUIA_List_Quiet:
1036 _handle_bool_tag(data->flags, tag->ti_Data, LIST_QUIET);
1037 if (!tag->ti_Data)
1039 DoMethod(obj, MUIM_List_Redraw, MUIV_List_Redraw_All);
1040 if (data->entries_num != XGET(obj, MUIA_List_VertProp_Entries))
1041 set(obj, MUIA_List_VertProp_Entries, data->entries_num);
1042 if (data->vertprop_first !=
1043 XGET(obj, MUIA_List_VertProp_First))
1044 set(obj, MUIA_List_VertProp_First, data->vertprop_first);
1046 break;
1048 case MUIA_List_AutoVisible:
1049 _handle_bool_tag(data->flags, tag->ti_Data, LIST_AUTOVISIBLE);
1050 break;
1052 case MUIA_List_ShowDropMarks:
1053 _handle_bool_tag(data->flags, tag->ti_Data, LIST_SHOWDROPMARKS);
1054 break;
1056 case MUIA_List_DragSortable:
1057 _handle_bool_tag(data->flags, tag->ti_Data, LIST_DRAGSORTABLE);
1058 set(obj, MUIA_Draggable, tag->ti_Data);
1059 break;
1063 return DoSuperMethodA(cl, obj, (Msg) msg);
1066 /**************************************************************************
1067 OM_GET
1068 **************************************************************************/
1069 IPTR List__OM_GET(struct IClass *cl, Object *obj, struct opGet *msg)
1071 /* small macro to simplify return value storage */
1072 #define STORE *(msg->opg_Storage)
1073 struct MUI_ListData *data = INST_DATA(cl, obj);
1075 switch (msg->opg_AttrID)
1077 case MUIA_List_Entries:
1078 STORE = data->entries_num;
1079 return 1;
1080 case MUIA_List_First:
1081 STORE = data->entries_first;
1082 return 1;
1083 case MUIA_List_Active:
1084 STORE = data->entries_active;
1085 return 1;
1086 case MUIA_List_InsertPosition:
1087 STORE = data->insert_position;
1088 return 1;
1089 case MUIA_List_Title:
1090 STORE = (unsigned long)data->title;
1091 return 1;
1092 case MUIA_List_VertProp_Entries:
1093 STORE = data->vertprop_entries;
1094 return 1;
1095 case MUIA_List_VertProp_Visible:
1096 case MUIA_List_Visible:
1097 STORE = data->vertprop_visible;
1098 return 1;
1099 case MUIA_List_VertProp_First:
1100 STORE = data->vertprop_first;
1101 return 1;
1102 case MUIA_List_Format:
1103 STORE = (IPTR) data->format;
1104 return 1;
1105 case MUIA_List_AutoVisible:
1106 STORE = data->flags & LIST_AUTOVISIBLE;
1107 return 1;
1108 case MUIA_List_ShowDropMarks:
1109 STORE = data->flags & LIST_SHOWDROPMARKS;
1110 return 1;
1111 case MUIA_List_DragSortable:
1112 STORE = data->flags & LIST_DRAGSORTABLE;
1113 return 1;
1114 break;
1117 if (DoSuperMethodA(cl, obj, (Msg) msg))
1118 return 1;
1119 return 0;
1120 #undef STORE
1123 /**************************************************************************
1124 MUIM_Setup
1125 **************************************************************************/
1126 IPTR List__MUIM_Setup(struct IClass *cl, Object *obj,
1127 struct MUIP_Setup *msg)
1129 struct MUI_ListData *data = INST_DATA(cl, obj);
1131 if (!DoSuperMethodA(cl, obj, (Msg) msg))
1132 return 0;
1134 data->prefs_refresh = muiGlobalInfo(obj)->mgi_Prefs->list_refresh;
1135 data->prefs_linespacing =
1136 muiGlobalInfo(obj)->mgi_Prefs->list_linespacing;
1137 data->prefs_smoothed = muiGlobalInfo(obj)->mgi_Prefs->list_smoothed;
1138 data->prefs_smoothval = muiGlobalInfo(obj)->mgi_Prefs->list_smoothval;
1140 CalcWidths(cl, obj);
1142 if (data->title)
1144 data->title_height = data->entries[ENTRY_TITLE]->height + 2;
1146 else
1148 data->title_height = 0;
1151 data->list_cursor =
1152 zune_imspec_setup(MUII_ListCursor, muiRenderInfo(obj));
1153 data->list_select =
1154 zune_imspec_setup(MUII_ListSelect, muiRenderInfo(obj));
1155 data->list_selcur =
1156 zune_imspec_setup(MUII_ListSelCur, muiRenderInfo(obj));
1158 return 1;
1161 /**************************************************************************
1162 MUIM_Cleanup
1163 **************************************************************************/
1164 IPTR List__MUIM_Cleanup(struct IClass *cl, Object *obj,
1165 struct MUIP_Cleanup *msg)
1167 struct MUI_ListData *data = INST_DATA(cl, obj);
1168 struct ListImage *li = List_First(&data->images);
1170 while (li)
1172 struct ListImage *next = Node_Next(li);
1173 DoMethod(obj, MUIM_List_DeleteImage, (IPTR) li);
1174 li = next;
1177 zune_imspec_cleanup(data->list_cursor);
1178 zune_imspec_cleanup(data->list_select);
1179 zune_imspec_cleanup(data->list_selcur);
1181 return DoSuperMethodA(cl, obj, (Msg) msg);
1184 /**************************************************************************
1185 MUIM_AskMinMax
1186 **************************************************************************/
1187 IPTR List__MUIM_AskMinMax(struct IClass *cl, Object *obj,
1188 struct MUIP_AskMinMax *msg)
1190 struct MUI_ListData *data = INST_DATA(cl, obj);
1192 DoSuperMethodA(cl, obj, (Msg) msg);
1195 if ((data->flags & LIST_ADJUSTWIDTH) && (data->entries_num > 0))
1197 msg->MinMaxInfo->MinWidth += data->entries_maxwidth;
1198 msg->MinMaxInfo->DefWidth += data->entries_maxwidth;
1199 msg->MinMaxInfo->MaxWidth += data->entries_maxwidth;
1201 else
1203 msg->MinMaxInfo->MinWidth += 40;
1204 msg->MinMaxInfo->DefWidth += 100;
1205 msg->MinMaxInfo->MaxWidth = MUI_MAXMAX;
1208 if (data->entries_num > 0)
1210 if (data->flags & LIST_ADJUSTHEIGHT)
1212 msg->MinMaxInfo->MinHeight += data->entries_totalheight;
1213 msg->MinMaxInfo->DefHeight += data->entries_totalheight;
1214 msg->MinMaxInfo->MaxHeight += data->entries_totalheight;
1216 else
1218 ULONG h = data->entry_maxheight + data->prefs_linespacing;
1219 msg->MinMaxInfo->MinHeight += 2 * h + data->prefs_linespacing;
1220 msg->MinMaxInfo->DefHeight += 8 * h + data->prefs_linespacing;
1221 msg->MinMaxInfo->MaxHeight = MUI_MAXMAX;
1224 else
1226 msg->MinMaxInfo->MinHeight += 36;
1227 msg->MinMaxInfo->DefHeight += 96;
1228 msg->MinMaxInfo->MaxHeight = MUI_MAXMAX;
1230 D(bug("List %p minheigh=%d, line maxh=%d\n",
1231 obj, msg->MinMaxInfo->MinHeight, data->entry_maxheight));
1232 return TRUE;
1235 /****i* List.mui/MUIM_Layout *************************************************
1237 * NAME
1238 * MUIM_Layout
1240 ******************************************************************************
1244 IPTR List__MUIM_Layout(struct IClass *cl, Object *obj,
1245 struct MUIP_Layout *msg)
1247 struct MUI_ListData *data = INST_DATA(cl, obj);
1248 ULONG rc = DoSuperMethodA(cl, obj, (Msg) msg);
1249 LONG new_entries_first = data->entries_first;
1251 /* Calc the numbers of entries visible */
1252 CalcVertVisible(cl, obj);
1254 /* Ensure active entry is visible if requested */
1255 if (data->entries_active + 1 >=
1256 (data->entries_first + data->entries_visible)
1257 && (data->flags & LIST_AUTOVISIBLE) != 0)
1258 new_entries_first =
1259 data->entries_active - data->entries_visible + 1;
1261 /* Ensure there are no unnecessary empty lines */
1262 if ((new_entries_first + data->entries_visible >=
1263 data->entries_num)
1264 && (data->entries_visible <= data->entries_num))
1265 new_entries_first = data->entries_num - data->entries_visible;
1267 /* Always show the start of the list if it isn't long enough to fill the
1268 view */
1269 if (data->entries_num <= data->entries_visible)
1270 new_entries_first = 0;
1272 if (new_entries_first < 0)
1273 new_entries_first = 0;
1275 set(obj, new_entries_first != data->entries_first ?
1276 MUIA_List_First : TAG_IGNORE, new_entries_first);
1278 /* So the notify happens */
1279 set(obj, MUIA_List_VertProp_Visible, data->entries_visible);
1281 return rc;
1285 /**************************************************************************
1286 MUIM_Show
1287 **************************************************************************/
1288 IPTR List__MUIM_Show(struct IClass *cl, Object *obj,
1289 struct MUIP_Show *msg)
1291 struct MUI_ListData *data = INST_DATA(cl, obj);
1292 ULONG rc = DoSuperMethodA(cl, obj, (Msg) msg);
1294 zune_imspec_show(data->list_cursor, obj);
1295 zune_imspec_show(data->list_select, obj);
1296 zune_imspec_show(data->list_selcur, obj);
1297 return rc;
1301 /**************************************************************************
1302 MUIM_Hide
1303 **************************************************************************/
1304 IPTR List__MUIM_Hide(struct IClass *cl, Object *obj,
1305 struct MUIP_Hide *msg)
1307 struct MUI_ListData *data = INST_DATA(cl, obj);
1309 zune_imspec_hide(data->list_cursor);
1310 zune_imspec_hide(data->list_select);
1311 zune_imspec_hide(data->list_selcur);
1313 return DoSuperMethodA(cl, obj, (Msg) msg);
1317 /**************************************************************************
1318 Draw an entry at entry_pos at the given row. To draw the title, set pos to
1319 ENTRY_TITLE
1320 **************************************************************************/
1321 static VOID List_DrawEntry(struct IClass *cl, Object *obj, int entry_pos,
1322 int y)
1324 struct MUI_ListData *data = INST_DATA(cl, obj);
1325 int col, x1, x2;
1327 /* To be sure we don't draw anything if there is no title */
1328 if (entry_pos == ENTRY_TITLE && !data->title)
1329 return;
1331 DisplayEntry(cl, obj, entry_pos);
1332 x1 = _mleft(obj);
1334 for (col = 0; col < data->columns; col++)
1336 ZText *text;
1337 x2 = x1 + data->ci[col].entries_width;
1339 if ((text =
1340 zune_text_new(data->preparses[col], data->strings[col],
1341 ZTEXT_ARG_NONE, 0)))
1343 /* Could be made simpler, as we don't really need the bounds */
1344 zune_text_get_bounds(text, obj);
1345 /* Note, this was MPEN_SHADOW before */
1346 SetAPen(_rp(obj), muiRenderInfo(obj)->mri_Pens[MPEN_TEXT]);
1347 zune_text_draw(text, obj, x1, x2, y); /* totally wrong! */
1348 zune_text_destroy(text);
1350 x1 = x2 + data->ci[col].delta + (data->ci[col].bar ? BAR_WIDTH : 0);
1354 /**************************************************************************
1355 MUIM_Draw
1356 **************************************************************************/
1357 IPTR List__MUIM_Draw(struct IClass *cl, Object *obj, struct MUIP_Draw *msg)
1359 struct MUI_ListData *data = INST_DATA(cl, obj);
1360 int entry_pos, y;
1361 APTR clip;
1362 int start, end;
1363 BOOL scroll_caused_damage = FALSE;
1364 struct MUI_ImageSpec_intern *highlight;
1366 if (data->flags & LIST_QUIET)
1367 return 0;
1369 DoSuperMethodA(cl, obj, (Msg) msg);
1371 if (msg->flags & MADF_DRAWUPDATE)
1373 if (data->update == 1)
1374 DoMethod(obj, MUIM_DrawBackground, _mleft(obj), _mtop(obj),
1375 _mwidth(obj), _mheight(obj),
1376 0, data->entries_first * data->entry_maxheight, 0);
1378 else
1380 DoMethod(obj, MUIM_DrawBackground, _mleft(obj), _mtop(obj),
1381 _mwidth(obj), _mheight(obj),
1382 0, data->entries_first * data->entry_maxheight, 0);
1385 clip = MUI_AddClipping(muiRenderInfo(obj), _mleft(obj), _mtop(obj),
1386 _mwidth(obj), _mheight(obj));
1388 if (!(msg->flags & MADF_DRAWUPDATE)
1389 || ((msg->flags & MADF_DRAWUPDATE) && data->update == 1))
1391 y = _mtop(obj);
1392 /* Draw Title
1394 if (data->title_height && data->title)
1396 List_DrawEntry(cl, obj, ENTRY_TITLE, y);
1397 y += data->entries[ENTRY_TITLE]->height;
1398 SetAPen(_rp(obj), _pens(obj)[MPEN_SHADOW]);
1399 Move(_rp(obj), _mleft(obj), y);
1400 Draw(_rp(obj), _mright(obj), y);
1401 SetAPen(_rp(obj), _pens(obj)[MPEN_SHINE]);
1402 y++;
1403 Move(_rp(obj), _mleft(obj), y);
1404 Draw(_rp(obj), _mright(obj), y);
1408 y = data->entries_top_pixel;
1410 start = data->entries_first;
1411 end = data->entries_first + data->entries_visible;
1413 if ((msg->flags & MADF_DRAWUPDATE) && data->update == 3)
1415 int diffy = data->entries_first - data->update_pos;
1416 int top, bottom;
1417 if (abs(diffy) < data->entries_visible)
1419 scroll_caused_damage =
1420 (_rp(obj)->Layer->Flags & LAYERREFRESH) ? FALSE : TRUE;
1422 ScrollRaster(_rp(obj), 0, diffy * data->entry_maxheight,
1423 _mleft(obj), y,
1424 _mright(obj),
1425 y + data->entry_maxheight * data->entries_visible);
1427 scroll_caused_damage =
1428 scroll_caused_damage
1429 && (_rp(obj)->Layer->Flags & LAYERREFRESH);
1431 if (diffy > 0)
1433 start = end - diffy;
1434 y += data->entry_maxheight * (data->entries_visible -
1435 diffy);
1437 else
1438 end = start - diffy;
1441 top = y;
1442 bottom = y + (end - start) * data->entry_maxheight;
1444 DoMethod(obj, MUIM_DrawBackground, _mleft(obj), top,
1445 _mwidth(obj), bottom - top + 1,
1447 top - _mtop(obj) + data->entries_first * data->entry_maxheight,
1451 for (entry_pos = start;
1452 entry_pos < end && entry_pos < data->entries_num; entry_pos++)
1454 struct ListEntry *entry = data->entries[entry_pos];
1456 if (!(msg->flags & MADF_DRAWUPDATE) ||
1457 ((msg->flags & MADF_DRAWUPDATE) && data->update == 1) ||
1458 ((msg->flags & MADF_DRAWUPDATE) && data->update == 3) ||
1459 ((msg->flags & MADF_DRAWUPDATE) && data->update == 2
1460 && data->update_pos == entry_pos))
1462 /* Choose appropriate highlight image */
1464 if (entry_pos == data->entries_active
1465 && entry->flags & ENTRY_SELECTED)
1466 highlight = data->list_selcur;
1467 else if (entry_pos == data->entries_active)
1468 highlight = data->list_cursor;
1469 else if (entry->flags & ENTRY_SELECTED)
1470 highlight = data->list_select;
1471 else
1472 highlight = NULL;
1474 /* Draw highlight or background */
1476 if (highlight != NULL)
1478 zune_imspec_draw(highlight, muiRenderInfo(obj),
1479 _mleft(obj), y, _mwidth(obj), data->entry_maxheight,
1480 0, y - data->entries_top_pixel, 0);
1482 else if ((msg->flags & MADF_DRAWUPDATE) && data->update == 2
1483 && data->update_pos == entry_pos)
1485 DoMethod(obj, MUIM_DrawBackground, _mleft(obj), y,
1486 _mwidth(obj), data->entry_maxheight, 0,
1487 y - _mtop(obj) +
1488 data->entries_first * data->entry_maxheight, 0);
1491 List_DrawEntry(cl, obj, entry_pos, y);
1493 y += data->entry_maxheight;
1496 MUI_RemoveClipping(muiRenderInfo(obj), clip);
1498 data->update = 0;
1500 if (scroll_caused_damage)
1502 if (MUI_BeginRefresh(muiRenderInfo(obj), 0))
1504 /* Theoretically it might happen that more damage is caused
1505 after ScrollRaster. By something else, like window movement
1506 in front of our window. Therefore refresh root object of
1507 window, not just this object */
1509 Object *o = NULL;
1511 get(_win(obj), MUIA_Window_RootObject, &o);
1512 MUI_Redraw(o, MADF_DRAWOBJECT);
1514 MUI_EndRefresh(muiRenderInfo(obj), 0);
1518 ULONG x1 = _mleft(obj);
1519 ULONG col;
1520 y = _mtop(obj);
1522 if (data->title_height && data->title)
1524 for (col = 0; col < data->columns; col++)
1526 ULONG halfdelta = data->ci[col].delta / 2;
1527 x1 += data->ci[col].entries_width + halfdelta;
1529 if (x1 + (data->ci[col].bar ? BAR_WIDTH : 0) > _mright(obj))
1530 break;
1532 if (data->ci[col].bar)
1534 SetAPen(_rp(obj), _pens(obj)[MPEN_SHINE]);
1535 Move(_rp(obj), x1, y);
1536 Draw(_rp(obj), x1,
1537 y + data->entries[ENTRY_TITLE]->height - 1);
1538 SetAPen(_rp(obj), _pens(obj)[MPEN_SHADOW]);
1539 Move(_rp(obj), x1 + 1, y);
1540 Draw(_rp(obj), x1 + 1,
1541 y + data->entries[ENTRY_TITLE]->height - 1);
1543 x1 += BAR_WIDTH;
1545 x1 += data->ci[col].delta - halfdelta;
1547 y += data->entries[ENTRY_TITLE]->height + 1;
1550 x1 = _mleft(obj);
1552 for (col = 0; col < data->columns; col++)
1554 ULONG halfdelta = data->ci[col].delta / 2;
1555 x1 += data->ci[col].entries_width + halfdelta;
1557 if (x1 + (data->ci[col].bar ? BAR_WIDTH : 0) > _mright(obj))
1558 break;
1560 if (data->ci[col].bar)
1562 SetAPen(_rp(obj), _pens(obj)[MPEN_SHINE]);
1563 Move(_rp(obj), x1, y);
1564 Draw(_rp(obj), x1, _mbottom(obj));
1565 SetAPen(_rp(obj), _pens(obj)[MPEN_SHADOW]);
1566 Move(_rp(obj), x1 + 1, y);
1567 Draw(_rp(obj), x1 + 1, _mbottom(obj));
1569 x1 += BAR_WIDTH;
1572 x1 += data->ci[col].delta - halfdelta;
1575 return 0;
1578 /****** List.mui/MUIM_List_Clear *********************************************
1580 * NAME
1581 * MUIM_List_Clear (V4)
1583 * SYNOPSIS
1584 * DoMethod(obj, MUIM_List_Clear);
1586 * FUNCTION
1587 * Removes all entries from the list.
1589 ******************************************************************************
1593 IPTR List__MUIM_Clear(struct IClass *cl, Object *obj,
1594 struct MUIP_List_Clear *msg)
1596 struct MUI_ListData *data = INST_DATA(cl, obj);
1598 while (data->confirm_entries_num)
1600 struct ListEntry *lentry =
1601 data->entries[--data->confirm_entries_num];
1602 DoMethod(obj, MUIM_List_Destruct, (IPTR) lentry->data,
1603 (IPTR) data->pool);
1604 FreeListEntry(data, lentry);
1606 /* Should never fail when shrinking */
1607 SetListSize(data, 0);
1610 if (data->confirm_entries_num != data->entries_num)
1612 SetAttrs(obj, MUIA_List_Entries, 0, MUIA_List_First, 0,
1613 /* Notify only when no entry was active */
1614 data->entries_active !=
1615 MUIV_List_Active_Off ? MUIA_List_Active : TAG_DONE,
1616 MUIV_List_Active_Off, TAG_DONE);
1618 data->update = 1;
1619 MUI_Redraw(obj, MADF_DRAWUPDATE);
1622 return 0;
1625 /**************************************************************************
1626 MUIM_List_Exchange
1627 **************************************************************************/
1628 IPTR List__MUIM_Exchange(struct IClass *cl, Object *obj,
1629 struct MUIP_List_Exchange *msg)
1631 struct MUI_ListData *data = INST_DATA(cl, obj);
1632 LONG pos1, pos2;
1634 switch (msg->pos1)
1636 case MUIV_List_Exchange_Top:
1637 pos1 = 0;
1638 break;
1639 case MUIV_List_Exchange_Active:
1640 pos1 = data->entries_active;
1641 break;
1642 case MUIV_List_Exchange_Bottom:
1643 pos1 = data->entries_num - 1;
1644 break;
1645 default:
1646 pos1 = msg->pos1;
1649 switch (msg->pos2)
1651 case MUIV_List_Exchange_Top:
1652 pos2 = 0;
1653 break;
1654 case MUIV_List_Exchange_Active:
1655 pos2 = data->entries_active;
1656 break;
1657 case MUIV_List_Exchange_Bottom:
1658 pos2 = data->entries_num - 1;
1659 break;
1660 case MUIV_List_Exchange_Next:
1661 pos2 = pos1 + 1;
1662 break;
1663 case MUIV_List_Exchange_Previous:
1664 pos2 = pos1 - 1;
1665 break;
1666 default:
1667 pos2 = msg->pos2;
1670 if (pos1 >= 0 && pos1 < data->entries_num && pos2 >= 0
1671 && pos2 < data->entries_num && pos1 != pos2)
1673 struct ListEntry *save = data->entries[pos1];
1674 data->entries[pos1] = data->entries[pos2];
1675 data->entries[pos2] = save;
1677 data->update = 2;
1678 data->update_pos = pos1;
1679 MUI_Redraw(obj, MADF_DRAWUPDATE);
1681 data->update = 2;
1682 data->update_pos = pos2;
1683 MUI_Redraw(obj, MADF_DRAWUPDATE);
1685 return TRUE;
1687 else
1689 return FALSE;
1693 /**************************************************************************
1694 MUIM_List_Redraw
1695 **************************************************************************/
1696 IPTR List__MUIM_Redraw(struct IClass *cl, Object *obj,
1697 struct MUIP_List_Redraw *msg)
1699 struct MUI_ListData *data = INST_DATA(cl, obj);
1701 if (!(data->flags & LIST_QUIET))
1703 if (msg->pos == MUIV_List_Redraw_All)
1705 data->update = 1;
1706 CalcWidths(cl, obj);
1707 MUI_Redraw(obj, MADF_DRAWUPDATE);
1709 else
1711 LONG pos = -1;
1712 if (msg->pos == MUIV_List_Redraw_Active)
1713 pos = data->entries_active;
1714 else if (msg->pos == MUIV_List_Redraw_Entry)
1716 LONG i;
1717 for (i = 0; i < data->entries_num; i++)
1718 if (data->entries[i]->data == msg->entry)
1720 pos = i;
1721 break;
1724 else
1725 pos = msg->pos;
1727 if (pos != -1)
1729 if (CalcDimsOfEntry(cl, obj, pos))
1730 data->update = 1;
1731 else
1733 data->update = 2;
1734 data->update_pos = pos;
1736 MUI_Redraw(obj, MADF_DRAWUPDATE);
1740 return 0;
1743 /**************************************************************************
1744 MUIM_List_Remove
1745 **************************************************************************/
1746 IPTR List__MUIM_Remove(struct IClass *cl, Object *obj,
1747 struct MUIP_List_Remove *msg)
1749 struct MUI_ListData *data = INST_DATA(cl, obj);
1750 LONG pos, cur;
1751 LONG new_act;
1752 struct ListEntry *lentry;
1753 //int rem_count = 1;
1755 if (!data->entries_num)
1756 return 0;
1758 switch (msg->pos)
1760 case MUIV_List_Remove_First:
1761 pos = 0;
1762 break;
1764 case MUIV_List_Remove_Active:
1765 pos = data->entries_active;
1766 break;
1768 case MUIV_List_Remove_Last:
1769 pos = data->entries_num - 1;
1770 break;
1772 case MUIV_List_Remove_Selected:
1773 /* TODO: needs special handling */
1774 pos = data->entries_active;
1775 break;
1777 default:
1778 pos = msg->pos;
1779 break;
1782 if (pos < 0 || pos >= data->entries_num)
1783 return 0;
1785 new_act = data->entries_active;
1787 if (pos == new_act && new_act == data->entries_num - 1)
1788 new_act--; /* might become MUIV_List_Active_Off */
1790 lentry = data->entries[pos];
1791 DoMethod(obj, MUIM_List_Destruct, (IPTR) lentry->data,
1792 (IPTR) data->pool);
1794 cur = pos + 1;
1796 RemoveListEntries(data, pos, cur - pos);
1797 data->confirm_entries_num -= cur - pos;
1799 /* ensure that the active element is in a valid range */
1800 if (new_act >= data->entries_num)
1801 new_act = data->entries_num - 1;
1803 SetAttrs(obj, MUIA_List_Entries, data->confirm_entries_num,
1804 (new_act >= pos) || (new_act != data->entries_active) ?
1805 MUIA_List_Active : TAG_DONE,
1806 new_act, /* Inform only if neccessary (for notify) */
1807 TAG_DONE);
1809 data->update = 1;
1810 MUI_Redraw(obj, MADF_DRAWUPDATE);
1812 return 0;
1815 /**************************************************************************
1816 MUIM_List_Select
1817 **************************************************************************/
1818 IPTR List__MUIM_Select(struct IClass *cl, Object *obj,
1819 struct MUIP_List_Select *msg)
1821 struct MUI_ListData *data = INST_DATA(cl, obj);
1822 LONG pos, i, count, selcount=0, state;
1823 BOOL multi_allowed = TRUE;
1825 /* Establish the range of entries affected */
1826 switch (msg->pos)
1828 case MUIV_List_Select_Active:
1829 pos = data->entries_active;
1830 if (pos == MUIV_List_Active_Off)
1831 count = 0;
1832 else
1833 count = 1;
1834 break;
1836 case MUIV_List_Select_All:
1837 pos = 0;
1838 count = data->entries_num;
1839 break;
1841 default:
1842 pos = msg->pos;
1843 count = 1;
1844 if (pos < 0 || pos >= data->entries_num)
1845 return 0;
1846 break;
1849 if (msg->seltype != MUIV_List_Select_Ask && data->multi_test_hook != NULL)
1851 /* Disallow selection of an additional entry if there is a currently
1852 selected entry that is not multi-selectable (in such case there
1853 will only be one entry currently selected, so no need to iterate) */
1854 i = MUIV_List_NextSelected_Start;
1855 DoMethod(obj, MUIM_List_NextSelected, (IPTR) &i);
1856 if (i != MUIV_List_NextSelected_End)
1857 selcount++;
1858 if (data->multi_test_hook != NULL && selcount != 0)
1859 multi_allowed = CallHookPkt(data->multi_test_hook, NULL,
1860 data->entries[i]->data);
1863 state = data->entries[i]->flags & ENTRY_SELECTED;
1865 /* Change or check state of each entry in the range */
1866 for (i = pos; i < pos + count; i++)
1868 BOOL new_select_state;
1870 new_select_state = state ? TRUE : FALSE;
1872 switch (msg->seltype)
1874 case MUIV_List_Select_Off:
1875 new_select_state = FALSE;
1876 break;
1878 case MUIV_List_Select_On:
1879 new_select_state = TRUE;
1880 break;
1882 case MUIV_List_Select_Toggle:
1883 new_select_state = !state;
1884 break;
1886 default:
1887 if (data->entries[i]->flags & ENTRY_SELECTED)
1888 selcount++;
1889 break;
1892 if (msg->seltype != MUIV_List_Select_Ask)
1894 /* Disallow selection if entry is not multi-selectable and
1895 * there are already selected entries */
1896 if (data->multi_test_hook != NULL && new_select_state)
1897 new_select_state = multi_allowed && (selcount == 0 ||
1898 CallHookPkt(data->multi_test_hook, NULL,
1899 data->entries[i]->data));
1901 if (new_select_state)
1902 data->entries[i]->flags |= ENTRY_SELECTED;
1903 else
1904 data->entries[i]->flags &= ~ENTRY_SELECTED;
1908 /* Report old state or number of selected entries */
1909 if (msg->info)
1911 if (msg->pos == MUIV_List_Select_All
1912 && msg->seltype == MUIV_List_Select_Ask)
1913 *msg->info = selcount;
1914 else
1915 *msg->info = state;
1918 /* Redraw unless it was just an enquiry */
1919 if (msg->seltype != MUIV_List_Select_Ask)
1921 if (count > 1)
1922 data->update = 1;
1923 else
1925 data->update = 2;
1926 data->update_pos = pos;
1928 MUI_Redraw(obj, MADF_DRAWUPDATE);
1931 return 0;
1934 /**************************************************************************
1935 MUIM_List_Insert
1936 **************************************************************************/
1938 IPTR List__MUIM_Insert(struct IClass *cl, Object *obj,
1939 struct MUIP_List_Insert *msg)
1941 struct MUI_ListData *data = INST_DATA(cl, obj);
1942 LONG pos, count, sort;
1944 count = msg->count;
1945 sort = 0;
1947 if (count == -1)
1949 /* Count the number of entries */
1950 for (count = 0; msg->entries[count] != NULL; count++)
1954 if (count <= 0)
1955 return ~0;
1957 switch (msg->pos)
1959 case MUIV_List_Insert_Top:
1960 pos = 0;
1961 break;
1963 case MUIV_List_Insert_Active:
1964 if (data->entries_active != -1)
1965 pos = data->entries_active;
1966 else
1967 pos = 0;
1968 break;
1970 case MUIV_List_Insert_Sorted:
1971 pos = data->entries_num;
1972 sort = 1; /* we sort'em later */
1973 break;
1975 case MUIV_List_Insert_Bottom:
1976 pos = data->entries_num;
1977 break;
1979 default:
1980 if (msg->pos > data->entries_num)
1981 pos = data->entries_num;
1982 else if (msg->pos < 0)
1983 pos = 0;
1984 else
1985 pos = msg->pos;
1986 break;
1989 if (!(SetListSize(data, data->entries_num + count)))
1990 return ~0;
1992 LONG until = pos + count;
1993 APTR *toinsert = msg->entries;
1995 if (!(PrepareInsertListEntries(data, pos, count)))
1996 return ~0;
1998 while (pos < until)
2000 struct ListEntry *lentry;
2002 if (!(lentry = AllocListEntry(data)))
2004 /* Panic, but we must be in a consistent state, so remove
2005 * the space where the following list entries should have gone
2007 RemoveListEntries(data, pos, until - pos);
2008 return ~0;
2011 /* now call the construct method which returns us a pointer which
2012 we need to store */
2013 lentry->data = (APTR) DoMethod(obj, MUIM_List_Construct,
2014 (IPTR) * toinsert, (IPTR) data->pool);
2015 if (!lentry->data)
2017 FreeListEntry(data, lentry);
2018 RemoveListEntries(data, pos, until - pos);
2020 /* TODO: Also check for visible stuff like below */
2021 if (data->entries_num != data->confirm_entries_num)
2022 set(obj, MUIA_List_Entries, data->confirm_entries_num);
2023 return ~0;
2026 data->entries[pos] = lentry;
2027 data->confirm_entries_num++;
2029 if (_flags(obj) & MADF_SETUP)
2031 /* We have to calculate the width and height of the newly
2032 * inserted entry. This has to be done after inserting the
2033 * element into the list */
2034 CalcDimsOfEntry(cl, obj, pos);
2037 toinsert++;
2038 pos++;
2041 /* Recalculate the number of visible entries */
2042 if (_flags(obj) & MADF_SETUP)
2043 CalcVertVisible(cl, obj);
2045 if (data->entries_num != data->confirm_entries_num)
2047 SetAttrs(obj,
2048 MUIA_List_Entries, data->confirm_entries_num,
2049 MUIA_List_Visible, data->entries_visible, TAG_DONE);
2052 /* If the array is already sorted, we could do a simple insert
2053 * sort and would be much faster than with qsort.
2054 * If an array is not yet sorted, does a MUIV_List_Insert_Sorted
2055 * sort the whole array?
2057 * I think, we better sort the whole array:
2059 if (sort)
2061 DoMethod(obj, MUIM_List_Sort);
2062 /* TODO: which pos to return here !? */
2063 /* MUIM_List_Sort already called MUI_Redraw */
2065 else
2067 data->update = 1;
2068 MUI_Redraw(obj, MADF_DRAWUPDATE);
2070 data->insert_position = pos;
2072 return (ULONG) pos;
2075 /**************************************************************************
2076 MUIM_List_InsertSingle
2077 **************************************************************************/
2078 IPTR List__MUIM_InsertSingle(struct IClass *cl, Object *obj,
2079 struct MUIP_List_InsertSingle *msg)
2081 return DoMethod(obj, MUIM_List_Insert, (IPTR) & msg->entry, 1,
2082 msg->pos);
2085 /**************************************************************************
2086 MUIM_List_GetEntry
2087 **************************************************************************/
2088 IPTR List__MUIM_GetEntry(struct IClass *cl, Object *obj,
2089 struct MUIP_List_GetEntry *msg)
2091 struct MUI_ListData *data = INST_DATA(cl, obj);
2092 int pos = msg->pos;
2094 if (pos == MUIV_List_GetEntry_Active)
2095 pos = data->entries_active;
2097 if (pos < 0 || pos >= data->entries_num)
2099 *msg->entry = NULL;
2100 return 0;
2102 *msg->entry = data->entries[pos]->data;
2103 return (IPTR) *msg->entry;
2106 /**************************************************************************
2107 MUIM_List_Construct
2108 **************************************************************************/
2109 IPTR List__MUIM_Construct(struct IClass *cl, Object *obj,
2110 struct MUIP_List_Construct *msg)
2112 struct MUI_ListData *data = INST_DATA(cl, obj);
2114 if (NULL == data->construct_hook)
2115 return (IPTR) msg->entry;
2116 if ((IPTR) data->construct_hook == MUIV_List_ConstructHook_String)
2118 int len = msg->entry ? strlen((STRPTR) msg->entry) : 0;
2119 ULONG *mem = AllocPooled(msg->pool, len + 5);
2121 if (NULL == mem)
2122 return 0;
2123 mem[0] = len + 5;
2124 if (msg->entry != NULL)
2125 strcpy((STRPTR) (mem + 1), (STRPTR) msg->entry);
2126 else
2127 *(STRPTR) (mem + 1) = 0;
2128 return (IPTR) (mem + 1);
2130 return CallHookPkt(data->construct_hook, msg->pool, msg->entry);
2133 /**************************************************************************
2134 MUIM_List_Destruct
2135 **************************************************************************/
2136 IPTR List__MUIM_Destruct(struct IClass *cl, Object *obj,
2137 struct MUIP_List_Destruct *msg)
2139 struct MUI_ListData *data = INST_DATA(cl, obj);
2141 if (NULL == data->destruct_hook)
2142 return 0;
2144 if ((IPTR) data->destruct_hook == MUIV_List_DestructHook_String)
2146 ULONG *mem = ((ULONG *) msg->entry) - 1;
2147 FreePooled(msg->pool, mem, mem[0]);
2149 else
2151 CallHookPkt(data->destruct_hook, msg->pool, msg->entry);
2153 return 0;
2156 /****** List.mui/MUIM_List_Compare *******************************************
2158 * NAME
2159 * MUIM_List_Compare (V20)
2161 * SYNOPSIS
2162 * DoMethod(obj, MUIM_List_Compare, APTR entry1, APTR entry2,
2163 * LONG sort_type1, LONG sort_type2);
2165 * FUNCTION
2166 * Compare two list entries according to the current comparison hook
2167 * (MUIA_List_CompareHook).
2169 * INPUTS
2170 * entry1 - the first entry data.
2171 * entry2 - the second entry data.
2172 * sort_type1 - undocumented.
2173 * sort_type2 - undocumented.
2175 * SEE ALSO
2176 * MUIA_List_CompareHook, MUIM_List_Sort.
2178 ******************************************************************************
2182 IPTR List__MUIM_Compare(struct IClass *cl, Object *obj,
2183 struct MUIP_List_Compare *msg)
2185 struct MUI_ListData *data = INST_DATA(cl, obj);
2187 return CallHookPkt(data->compare_hook, msg->entry2, msg->entry1);
2190 /**************************************************************************
2191 MUIM_List_Display
2192 **************************************************************************/
2193 IPTR List__MUIM_Display(struct IClass *cl, Object *obj,
2194 struct MUIP_List_Display *msg)
2196 struct MUI_ListData *data = INST_DATA(cl, obj);
2198 if (NULL == data->display_hook)
2200 if (msg->entry)
2201 *msg->array = msg->entry;
2202 else
2203 *msg->array = 0;
2204 return 1;
2207 *((ULONG *) (msg->array - 1)) = msg->entry_pos;
2208 return CallHookPkt(data->display_hook, msg->array, msg->entry);
2211 /**************************************************************************
2212 MUIM_List_SelectChange
2213 **************************************************************************/
2214 IPTR List__MUIM_SelectChange(struct IClass *cl, Object *obj,
2215 struct MUIP_List_SelectChange *msg)
2217 return 1;
2220 /**************************************************************************
2221 MUIM_List_CreateImage
2222 Called by a List subclass in its Setup method.
2223 Connects an Area subclass object to the list, much like an object gets
2224 connected to a window. List calls Setup and AskMinMax on that object,
2225 keeps a reference to it (that reference will be returned).
2226 Text engine will dereference that pointer and draw the object with its
2227 default size.
2228 **************************************************************************/
2229 IPTR List__MUIM_CreateImage(struct IClass *cl, Object *obj,
2230 struct MUIP_List_CreateImage *msg)
2232 struct MUI_ListData *data = INST_DATA(cl, obj);
2233 struct ListImage *li;
2235 /* List must be already setup in Setup of your subclass */
2236 if (!(_flags(obj) & MADF_SETUP))
2237 return 0;
2238 li = AllocPooled(data->pool, sizeof(struct ListImage));
2239 if (!li)
2240 return 0;
2241 li->obj = msg->obj;
2243 AddTail((struct List *)&data->images, (struct Node *)li);
2244 DoMethod(li->obj, MUIM_ConnectParent, (IPTR) obj);
2245 DoSetupMethod(li->obj, muiRenderInfo(obj));
2248 return (IPTR) li;
2251 /**************************************************************************
2252 MUIM_List_DeleteImage
2253 **************************************************************************/
2254 IPTR List__MUIM_DeleteImage(struct IClass *cl, Object *obj,
2255 struct MUIP_List_DeleteImage *msg)
2257 struct MUI_ListData *data = INST_DATA(cl, obj);
2258 struct ListImage *li = (struct ListImage *)msg->listimg;
2260 if (li)
2262 DoMethod(li->obj, MUIM_Cleanup);
2263 DoMethod(li->obj, MUIM_DisconnectParent);
2264 Remove((struct Node *)li);
2265 FreePooled(data->pool, li, sizeof(struct ListImage));
2268 return 0;
2271 /****** List.mui/MUIM_List_Jump **********************************************
2273 * NAME
2274 * MUIM_List_Jump (V4)
2276 * SYNOPSIS
2277 * DoMethod(obj, MUIM_List_Jump, LONG pos);
2279 * FUNCTION
2280 * Scrolls the list so that a particular entry is visible.
2282 * INPUTS
2283 * pos - index of entry that should become visible, or one of these
2284 * special values:
2285 * MUIV_List_Jump_Active: show the active entry.
2286 * MUIV_List_Jump_Top: show the first entry.
2287 * MUIV_List_Jump_Bottom: show the last entry.
2288 * MUIV_List_Jump_Up: show the previous hidden entry.
2289 * MUIV_List_Jump_Down: show the next hidden entry.
2291 ******************************************************************************
2295 IPTR List__MUIM_Jump(struct IClass *cl, Object *obj,
2296 struct MUIP_List_Jump *msg)
2298 struct MUI_ListData *data = INST_DATA(cl, obj);
2299 LONG pos = msg->pos;
2301 switch (pos)
2303 case MUIV_List_Jump_Top:
2304 pos = 0;
2305 break;
2307 case MUIV_List_Jump_Active:
2308 pos = data->entries_active;
2309 break;
2311 case MUIV_List_Jump_Bottom:
2312 pos = data->entries_num - 1;
2313 break;
2315 case MUIV_List_Jump_Down:
2316 pos = data->entries_first + data->entries_visible;
2317 break;
2319 case MUIV_List_Jump_Up:
2320 pos = data->entries_first - 1;
2321 break;
2324 if (pos > data->entries_num)
2326 pos = data->entries_num - 1;
2328 if (pos < 0)
2329 pos = 0;
2331 if (pos < data->entries_first)
2333 set(obj, MUIA_List_First, pos);
2335 else if (pos >= data->entries_first + data->entries_visible)
2337 pos -= (data->entries_visible - 1);
2338 if (pos < 0)
2339 pos = 0;
2340 if (pos != data->entries_first)
2342 set(obj, MUIA_List_First, pos);
2346 return TRUE;
2349 /****** List.mui/MUIM_List_Sort **********************************************
2351 * NAME
2352 * MUIM_List_Sort (V4)
2354 * SYNOPSIS
2355 * DoMethod(obj, MUIM_List_Sort);
2357 * FUNCTION
2358 * Sort the list's entries according to the current comparison hook
2359 * (MUIA_List_CompareHook).
2361 * SEE ALSO
2362 * MUIA_List_CompareHook, MUIM_List_Compare.
2364 ******************************************************************************
2368 IPTR List__MUIM_Sort(struct IClass *cl, Object *obj,
2369 struct MUIP_List_Sort *msg)
2371 struct MUI_ListData *data = INST_DATA(cl, obj);
2373 int i, j, max;
2374 struct MUIP_List_Compare cmpmsg =
2375 { MUIM_List_Compare, NULL, NULL, 0, 0 };
2377 if (data->entries_num > 1)
2380 Simple sort algorithm. Feel free to improve it.
2382 for (i = 0; i < data->entries_num - 1; i++)
2384 max = i;
2385 for (j = i + 1; j < data->entries_num; j++)
2387 cmpmsg.entry1 = data->entries[max]->data;
2388 cmpmsg.entry2 = data->entries[j]->data;
2389 if ((LONG) DoMethodA(obj, (Msg) & cmpmsg) > 0)
2391 max = j;
2394 if (i != max)
2396 APTR tmp = data->entries[i];
2397 data->entries[i] = data->entries[max];
2398 data->entries[max] = tmp;
2403 data->update = 1;
2404 MUI_Redraw(obj, MADF_DRAWUPDATE);
2406 return 0;
2409 /****** List.mui/MUIM_List_Move **********************************************
2411 * NAME
2412 * MUIM_List_Move (V9)
2414 * SYNOPSIS
2415 * DoMethod(obj, MUIM_List_Move, LONG from, LONG to);
2417 * FUNCTION
2418 * Move a list entry to a new position.
2420 * INPUTS
2421 * from - the current index of the entry that should be moved, or one of
2422 * these special values:
2423 * MUIV_List_Move_Active: the active entry.
2424 * MUIV_List_Move_Top: the first entry.
2425 * MUIV_List_Move_Bottom: the last entry.
2426 * to - the index of the entry's new position, or one of
2427 * these special values:
2428 * MUIV_List_Move_Active: the active entry.
2429 * MUIV_List_Move_Top: the first entry.
2430 * MUIV_List_Move_Bottom: the last entry.
2432 ******************************************************************************
2436 IPTR List__MUIM_Move(struct IClass *cl, Object *obj,
2437 struct MUIP_List_Move *msg)
2439 struct MUI_ListData *data = INST_DATA(cl, obj);
2441 LONG from, to;
2442 int i;
2444 /* Normalise special 'from' values */
2445 switch (msg->from)
2447 case MUIV_List_Move_Top:
2448 from = 0;
2449 break;
2450 case MUIV_List_Move_Active:
2451 from = data->entries_active;
2452 break;
2453 case MUIV_List_Move_Bottom:
2454 from = data->entries_num - 1;
2455 break;
2456 default:
2457 from = msg->from;
2460 /* Normalise special 'to' values */
2461 switch (msg->to)
2463 case MUIV_List_Move_Top:
2464 to = 0;
2465 break;
2466 case MUIV_List_Move_Active:
2467 to = data->entries_active;
2468 break;
2469 case MUIV_List_Move_Bottom:
2470 to = data->entries_num - 1;
2471 break;
2472 case MUIV_List_Move_Next:
2473 to = from + 1;
2474 break;
2475 case MUIV_List_Move_Previous:
2476 to = from - 1;
2477 break;
2478 default:
2479 to = msg->to;
2482 /* Check that values are within valid bounds */
2483 if (from > data->entries_num - 1 || from < 0
2484 || to > data->entries_num - 1 || to < 0 || from == to)
2485 return (IPTR) FALSE;
2487 /* Shift all entries in the range between the 'from' and 'to' positions */
2488 if (from < to)
2490 struct ListEntry *backup = data->entries[from];
2491 for (i = from; i < to; i++)
2492 data->entries[i] = data->entries[i + 1];
2493 data->entries[to] = backup;
2495 else
2497 struct ListEntry *backup = data->entries[from];
2498 for (i = from; i > to; i--)
2499 data->entries[i] = data->entries[i - 1];
2500 data->entries[to] = backup;
2503 /* Update index of active entry */
2504 if (from == data->entries_active)
2505 data->entries_active = to;
2506 else if (data->entries_active > from && data->entries_active < to)
2507 data->entries_active--;
2508 else if (data->entries_active < from && data->entries_active >= to)
2509 data->entries_active++;
2511 /* Reflect list changes visually */
2512 data->update = 1;
2513 MUI_Redraw(obj, MADF_DRAWUPDATE);
2515 return TRUE;
2518 /**************************************************************************
2519 MUIM_List_NextSelected
2520 **************************************************************************/
2521 IPTR List__MUIM_NextSelected(struct IClass *cl, Object *obj,
2522 struct MUIP_List_NextSelected *msg)
2524 struct MUI_ListData *data = INST_DATA(cl, obj);
2525 LONG pos, i;
2526 BOOL found = FALSE;
2528 /* Get the first entry to check */
2529 pos = *msg->pos;
2530 if (pos == MUIV_List_NextSelected_Start)
2531 pos = 0;
2532 else
2533 pos++;
2535 /* Find the next selected entry */
2536 for (i = pos; i < data->entries_num && !found; i++)
2538 if (data->entries[i]->flags & ENTRY_SELECTED)
2540 pos = i;
2541 found = TRUE;
2545 /* Return index of selected entry, or indicate there are no more */
2546 if (!found)
2547 pos = MUIV_List_NextSelected_End;
2548 *msg->pos = pos;
2550 return TRUE;
2553 /**************************************************************************
2554 MUIM_List_TestPos
2555 **************************************************************************/
2556 IPTR List__MUIM_TestPos(struct IClass *cl, Object *obj,
2557 struct MUIP_List_TestPos *msg)
2559 struct MUI_ListData *data = INST_DATA(cl, obj);
2560 struct MUI_List_TestPos_Result *result = msg->res;
2561 LONG col = -1, row = -1;
2562 UWORD flags = 0;
2563 LONG mx = msg->x - _left(obj);
2564 LONG entries_visible;
2566 if (data->entries_visible <= data->entries_num)
2567 entries_visible = data->entries_visible;
2568 else
2569 entries_visible = data->entries_num;
2570 LONG ey = msg->y - data->entries_top_pixel;
2571 /* y coordinates transformed to the entries */
2573 /* Now check if it was clicked on a title or on entries */
2574 if (ey < 0)
2575 flags |= MUI_LPR_ABOVE;
2576 else if (ey >= entries_visible * data->entry_maxheight)
2577 flags |= MUI_LPR_BELOW;
2578 else
2580 /* Identify row */
2581 row = ey / data->entry_maxheight + data->entries_first;
2582 result->yoffset =
2583 ey % data->entry_maxheight - data->entry_maxheight / 2;
2586 if (mx < 0)
2587 flags |= MUI_LPR_LEFT;
2588 if (mx >= _width(obj))
2589 flags |= MUI_LPR_RIGHT;
2590 else
2592 /* Identify column */
2593 if (data->entries_num > 0 && data->columns > 0)
2595 LONG width_sum = 0;
2596 for (col = 0; col < data->columns; col++)
2598 result->xoffset = mx - width_sum;
2599 width_sum +=
2600 data->ci[col].entries_width +
2601 data->ci[col].delta +
2602 (data->ci[col].bar ? BAR_WIDTH : 0);
2603 D(bug("[List/MUIM_HandleEvent] col %d "
2604 "width %d width_sum %d mx %d\n",
2605 col, data->ci[col].entries_width, width_sum, mx));
2606 if (mx < width_sum)
2608 D(bug("[List/MUIM_HandleEvent] Column hit %d\n", col));
2609 break;
2615 result->entry = row;
2616 result->column = col;
2617 result->flags = flags;
2619 return TRUE;
2622 /****i* List.mui/MUIM_DragQuery **********************************************
2624 * NAME
2625 * MUIM_DragQuery
2627 ******************************************************************************
2631 IPTR List__MUIM_DragQuery(struct IClass *cl, Object *obj,
2632 struct MUIP_DragQuery *msg)
2634 if (msg->obj == obj)
2635 return MUIV_DragQuery_Accept;
2636 else
2637 return MUIV_DragQuery_Refuse;
2641 /****i* List.mui/MUIM_DragFinish *********************************************
2643 * NAME
2644 * MUIM_DragFinish
2646 ******************************************************************************
2650 IPTR List__MUIM_DragFinish(struct IClass *cl, Object *obj,
2651 struct MUIP_DragFinish *msg)
2653 struct MUI_ListData *data = INST_DATA(cl, obj);
2655 data->drop_mark_y = -1;
2657 return 0;
2661 /****i* List.mui/MUIM_DragReport *********************************************
2663 * NAME
2664 * MUIM_DragReport
2666 ******************************************************************************
2670 IPTR List__MUIM_DragReport(struct IClass *cl, Object *obj,
2671 struct MUIP_DragReport *msg)
2673 struct MUI_ListData *data = INST_DATA(cl, obj);
2674 struct MUI_List_TestPos_Result pos;
2675 struct RastPort *rp = _rp(obj);
2676 LONG n, y;
2677 UWORD old_pattern;
2679 /* Choose new drop mark position */
2681 DoMethod(obj, MUIM_List_TestPos, msg->x, msg->y, (IPTR) &pos);
2682 if (pos.entry != -1)
2684 n = pos.entry;
2685 if (pos.yoffset > 0)
2686 n++;
2688 else if ((pos.flags & MUI_LPR_ABOVE) != 0)
2689 n = data->entries_first;
2690 else if ((pos.flags & MUI_LPR_BELOW) != 0)
2692 n = MIN(data->entries_visible, data->entries_num)
2693 - data->entries_first;
2694 } else {
2695 n = data->entries_first;
2698 /* Clear old drop mark */
2700 y = data->entries_top_pixel + (n - data->entries_first)
2701 * data->entry_maxheight;
2702 if (y != data->drop_mark_y)
2704 DoMethod(obj, MUIM_DrawBackground, _mleft(obj), data->drop_mark_y,
2705 _mwidth(obj), 1,
2706 0, 0, 0);
2708 /* Draw new drop mark and store its position */
2710 SetABPenDrMd(rp, _pens(obj)[MPEN_SHINE], _pens(obj)[MPEN_SHADOW],
2711 JAM2);
2712 old_pattern = rp->LinePtrn;
2713 SetDrPt(rp, 0xF0F0);
2714 Move(rp, _mleft(obj), y);
2715 Draw(rp, _mright(obj), y);
2716 SetDrPt(rp, old_pattern);
2717 data->drop_mark_y = y;
2720 return TRUE;
2724 /****i* List.mui/MUIM_DragDrop ***********************************************
2726 * NAME
2727 * MUIM_DragDrop
2729 ******************************************************************************
2733 IPTR List__MUIM_DragDrop(struct IClass *cl, Object *obj,
2734 struct MUIP_DragDrop *msg)
2736 struct MUI_ListData *data = INST_DATA(cl, obj);
2737 struct MUI_List_TestPos_Result pos;
2738 LONG n;
2740 /* Find drop position */
2742 DoMethod(obj, MUIM_List_TestPos, msg->x, msg->y, (IPTR) &pos);
2743 if (pos.entry != -1)
2745 /* Change drop position when coords move past centre of entry, not
2746 * entry boundary */
2748 n = pos.entry;
2749 if (pos.yoffset > 0)
2750 n++;
2752 /* Ensure that dropped entry will be positioned between the two
2753 * entries that are above and below the drop mark, rather than
2754 * strictly at the numeric index shown */
2756 if (n > data->entries_active)
2757 n--;
2759 else if ((pos.flags & MUI_LPR_ABOVE) != 0)
2760 n = MUIV_List_Move_Top;
2761 else if ((pos.flags & MUI_LPR_BELOW) != 0)
2762 n = MUIV_List_Move_Bottom;
2763 else
2764 n = 0;
2766 DoMethod(msg->obj, MUIM_List_Move, MUIV_List_Move_Active,
2769 return TRUE;
2773 /****i* List.mui/MUIM_CreateDragImage ****************************************
2775 * NAME
2776 * MUIM_CreateDragImage
2778 ******************************************************************************
2782 static IPTR List__MUIM_CreateDragImage(struct IClass *cl, Object *obj,
2783 struct MUIP_CreateDragImage *msg)
2785 struct MUI_ListData *data = INST_DATA(cl, obj);
2786 BOOL success = TRUE;
2787 struct MUI_List_TestPos_Result pos;
2788 WORD width, height, left, top;
2789 struct MUI_DragImage *img = NULL;
2790 const struct ZuneFrameGfx *zframe;
2791 LONG depth;
2793 /* Get info on dragged entry */
2794 DoMethod(obj, MUIM_List_TestPos, _left(obj) - msg->touchx,
2795 _top(obj) - msg->touchy, (IPTR) &pos);
2796 if (pos.entry == -1)
2797 success = FALSE;
2799 if (success)
2801 /* Get boundaries of entry */
2802 width = _mwidth(obj);
2803 height = data->entry_maxheight;
2804 left = _mleft(obj);
2805 top = _top(obj) - msg->touchy
2806 - (pos.yoffset + data->entry_maxheight / 2);
2808 /* Allocate drag image structure */
2809 img = (struct MUI_DragImage *)
2810 AllocVec(sizeof(struct MUI_DragImage), MEMF_CLEAR);
2811 if (img == NULL)
2812 success = FALSE;
2815 if (success)
2817 /* Get drag frame */
2818 zframe = zune_zframe_get(obj,
2819 &muiGlobalInfo(obj)->mgi_Prefs->frames[MUIV_Frame_Drag]);
2821 /* Allocate drag image buffer */
2822 img->width = width + zframe->ileft + zframe->iright;
2823 img->height = height + zframe->itop + zframe->ibottom;
2824 depth = GetBitMapAttr(_screen(obj)->RastPort.BitMap, BMA_DEPTH);
2825 img->bm = AllocBitMap(img->width, img->height, depth, BMF_MINPLANES,
2826 _screen(obj)->RastPort.BitMap);
2828 if (img->bm != NULL)
2830 /* Render entry */
2831 struct RastPort temprp;
2832 InitRastPort(&temprp);
2833 temprp.BitMap = img->bm;
2834 ClipBlit(_rp(obj), left, top, &temprp,
2835 zframe->ileft, zframe->itop, width, height,
2836 0xc0);
2838 /* Render frame */
2839 struct RastPort *rp_save = muiRenderInfo(obj)->mri_RastPort;
2840 muiRenderInfo(obj)->mri_RastPort = &temprp;
2841 zframe->draw(zframe->customframe, muiRenderInfo(obj), 0, 0,
2842 img->width, img->height, 0, 0, img->width, img->height);
2843 muiRenderInfo(obj)->mri_RastPort = rp_save;
2846 /* Ensure drag point matches where user clicked */
2847 img->touchx = msg->touchx - zframe->ileft + _addleft(obj);
2848 img->touchy = -(pos.yoffset + data->entry_maxheight / 2)
2849 - zframe->itop;
2850 img->flags = 0;
2853 return (IPTR) img;
2856 /**************************************************************************
2857 Dispatcher
2858 **************************************************************************/
2859 BOOPSI_DISPATCHER(IPTR, List_Dispatcher, cl, obj, msg)
2861 switch (msg->MethodID)
2863 case OM_NEW:
2864 return List__OM_NEW(cl, obj, (struct opSet *)msg);
2865 case OM_DISPOSE:
2866 return List__OM_DISPOSE(cl, obj, msg);
2867 case OM_SET:
2868 return List__OM_SET(cl, obj, (struct opSet *)msg);
2869 case OM_GET:
2870 return List__OM_GET(cl, obj, (struct opGet *)msg);
2872 case MUIM_Setup:
2873 return List__MUIM_Setup(cl, obj, (struct MUIP_Setup *)msg);
2874 case MUIM_Cleanup:
2875 return List__MUIM_Cleanup(cl, obj, (struct MUIP_Cleanup *)msg);
2876 case MUIM_AskMinMax:
2877 return List__MUIM_AskMinMax(cl, obj, (struct MUIP_AskMinMax *)msg);
2878 case MUIM_Show:
2879 return List__MUIM_Show(cl, obj, (struct MUIP_Show *)msg);
2880 case MUIM_Hide:
2881 return List__MUIM_Hide(cl, obj, (struct MUIP_Hide *)msg);
2882 case MUIM_Draw:
2883 return List__MUIM_Draw(cl, obj, (struct MUIP_Draw *)msg);
2884 case MUIM_Layout:
2885 return List__MUIM_Layout(cl, obj, (struct MUIP_Layout *)msg);
2886 case MUIM_List_Clear:
2887 return List__MUIM_Clear(cl, obj, (struct MUIP_List_Clear *)msg);
2888 case MUIM_List_Sort:
2889 return List__MUIM_Sort(cl, obj, (struct MUIP_List_Sort *)msg);
2890 case MUIM_List_Exchange:
2891 return List__MUIM_Exchange(cl, obj,
2892 (struct MUIP_List_Exchange *)msg);
2893 case MUIM_List_Insert:
2894 return List__MUIM_Insert(cl, obj, (APTR) msg);
2895 case MUIM_List_InsertSingle:
2896 return List__MUIM_InsertSingle(cl, obj, (APTR) msg);
2897 case MUIM_List_GetEntry:
2898 return List__MUIM_GetEntry(cl, obj, (APTR) msg);
2899 case MUIM_List_Redraw:
2900 return List__MUIM_Redraw(cl, obj, (APTR) msg);
2901 case MUIM_List_Remove:
2902 return List__MUIM_Remove(cl, obj, (APTR) msg);
2903 case MUIM_List_Select:
2904 return List__MUIM_Select(cl, obj, (APTR) msg);
2905 case MUIM_List_Construct:
2906 return List__MUIM_Construct(cl, obj, (APTR) msg);
2907 case MUIM_List_Destruct:
2908 return List__MUIM_Destruct(cl, obj, (APTR) msg);
2909 case MUIM_List_Compare:
2910 return List__MUIM_Compare(cl, obj, (APTR) msg);
2911 case MUIM_List_Display:
2912 return List__MUIM_Display(cl, obj, (APTR) msg);
2913 case MUIM_List_SelectChange:
2914 return List__MUIM_SelectChange(cl, obj, (APTR) msg);
2915 case MUIM_List_CreateImage:
2916 return List__MUIM_CreateImage(cl, obj, (APTR) msg);
2917 case MUIM_List_DeleteImage:
2918 return List__MUIM_DeleteImage(cl, obj, (APTR) msg);
2919 case MUIM_List_Jump:
2920 return List__MUIM_Jump(cl, obj, (APTR) msg);
2921 case MUIM_List_Move:
2922 return List__MUIM_Move(cl, obj, (struct MUIP_List_Move *)msg);
2923 case MUIM_List_NextSelected:
2924 return List__MUIM_NextSelected(cl, obj,
2925 (struct MUIP_List_NextSelected *)msg);
2926 case MUIM_List_TestPos:
2927 return List__MUIM_TestPos(cl, obj, (APTR) msg);
2928 case MUIM_DragQuery:
2929 return List__MUIM_DragQuery(cl, obj, (APTR) msg);
2930 case MUIM_DragFinish:
2931 return List__MUIM_DragFinish(cl, obj, (APTR) msg);
2932 case MUIM_DragReport:
2933 return List__MUIM_DragReport(cl, obj, (APTR) msg);
2934 case MUIM_DragDrop:
2935 return List__MUIM_DragDrop(cl, obj, (APTR) msg);
2936 case MUIM_CreateDragImage:
2937 return List__MUIM_CreateDragImage(cl, obj, (APTR) msg);
2940 return DoSuperMethodA(cl, obj, msg);
2942 BOOPSI_DISPATCHER_END
2945 * Class descriptor.
2947 const struct __MUIBuiltinClass _MUI_List_desc =
2949 MUIC_List,
2950 MUIC_Area,
2951 sizeof(struct MUI_ListData),
2952 (void *) List_Dispatcher