muimaster.library: update minor version
[AROS.git] / workbench / libs / muimaster / classes / list.c
blobd0a0823299a4dffc3d952efbc307e959978f372f
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 <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 column lists this is the title,
138 * otherwise 1. NULL for no title(s) */
140 /* Cursor images */
141 struct MUI_ImageSpec_intern *list_cursor;
142 struct MUI_ImageSpec_intern *list_select;
143 struct MUI_ImageSpec_intern *list_selcur;
145 /* Render optimization */
146 int update; /* 1 - update everything, 2 - redraw entry at update_pos,
147 * 3 - scroll to current entries_first (old value is in
148 * update_pos) */
149 int update_pos;
151 LONG drop_mark_y;
153 /* list images */
154 struct MinList images;
156 /* user prefs */
157 ListviewRefresh prefs_refresh;
158 UWORD prefs_linespacing;
159 BOOL prefs_smoothed;
160 UWORD prefs_smoothval;
163 #define LIST_ADJUSTWIDTH (1<<0)
164 #define LIST_ADJUSTHEIGHT (1<<1)
165 #define LIST_AUTOVISIBLE (1<<2)
166 #define LIST_DRAGSORTABLE (1<<3)
167 #define LIST_SHOWDROPMARKS (1<<4)
168 #define LIST_QUIET (1<<5)
171 /****** List.mui/MUIA_List_CompareHook ***************************************
173 * NAME
174 * MUIA_List_CompareHook -- (V4) [IS.], struct Hook *
176 * FUNCTION
177 * The provided hook indicates the sort ordering of two list entries.
178 * The hook receives list-entry data pointers as its second and third
179 * arguments. The hook should return a negative value if the first entry
180 * should be placed before the second entry, a positive value if the
181 * first entry should be placed after the second entry, and zero if the
182 * entries are equal.
184 * In addition to being used internally for sorting operations, this hook
185 * will be called when MUIM_List_Compare is externally invoked.
187 * If this attribute is not specified or is set to NULL, all list entries
188 * must be strings.
190 ******************************************************************************
194 /****** List.mui/MUIA_List_MultiTestHook *************************************
196 * NAME
197 * MUIA_List_MultiTestHook -- (V4) [IS.], struct Hook *
199 * FUNCTION
200 * The provided hook indicates whether a particular list entry
201 * may be multiselected. The hook receives the list-entry data pointer as
202 * its third argument, and returns a Boolean value. If this attribute is
203 * not specified or is set to NULL, all list entries are considered
204 * multi-selectable.
206 * Whenever an entry is about to be selected, this hook is called if
207 * there are other entries already selected. If the hook returns TRUE,
208 * the entry may be multi-selected; if the hook returns FALSE, the entry
209 * remains unselected.
211 * Additionally, if a non-multi-selectable entry has been selected (as
212 * the only selected entry in the list), any attempt to select an
213 * additional entry will fail.
215 ******************************************************************************
219 /**************************************************************************
220 Allocate a single list entry, does not initialize it (except the pointer)
221 **************************************************************************/
222 static struct ListEntry *AllocListEntry(struct MUI_ListData *data)
224 ULONG *mem;
225 struct ListEntry *le;
226 int size = sizeof(struct ListEntry) + sizeof(LONG) * data->columns + 4;
227 /* sizeinfo */
228 LONG j;
230 mem = AllocPooled(data->pool, size);
231 if (!mem)
232 return NULL;
233 D(bug("List AllocListEntry %p, %ld bytes\n", mem, size));
235 mem[0] = size; /* Save the size */
236 le = (struct ListEntry *)(mem + 1);
237 le->widths = (LONG *) (le + 1);
239 /* Initialize fields */
240 le->height = 0;
241 le->width = 0;
242 le->flags = 0;
243 for (j = 0; j < data->columns; j++)
244 le->widths[j] = 0;
246 return le;
249 /**************************************************************************
250 Deallocate a single list entry, does not deinitialize it
251 **************************************************************************/
252 static void FreeListEntry(struct MUI_ListData *data,
253 struct ListEntry *entry)
255 ULONG *mem = ((ULONG *) entry) - 1;
256 D(bug("FreeListEntry %p size=%ld\n", mem, mem[0]));
257 FreePooled(data->pool, mem, mem[0]);
260 /**************************************************************************
261 Ensures that there can be at least the given amount of entries within
262 the list. Returns 0 if not. It also allocates the space for the title.
263 It can be accessed with data->entries[ENTRY_TITLE]
264 **************************************************************************/
265 static int SetListSize(struct MUI_ListData *data, LONG size)
267 struct ListEntry **new_entries;
268 int new_entries_allocated;
270 if (size + 1 <= data->entries_allocated)
271 return 1;
273 new_entries_allocated = data->entries_allocated * 2 + 4;
274 if (new_entries_allocated < size + 1)
275 new_entries_allocated = size + 1 + 10; /* 10 is just random */
277 D(bug("List %p : SetListSize allocating %ld bytes\n", data,
278 new_entries_allocated * sizeof(struct ListEntry *)));
279 new_entries =
280 AllocVec(new_entries_allocated * sizeof(struct ListEntry *), 0);
281 if (NULL == new_entries)
282 return 0;
283 if (data->entries)
285 CopyMem(data->entries - 1, new_entries,
286 (data->entries_num + 1) * sizeof(struct ListEntry *));
287 FreeVec(data->entries - 1);
289 data->entries = new_entries + 1;
290 data->entries_allocated = new_entries_allocated;
291 return 1;
294 /**************************************************************************
295 Prepares the insertion of count entries at pos.
296 This function doesn't care if there is enough space in the datastructure.
297 SetListSize() must be used first.
298 With current implementation, this call will never fail
299 **************************************************************************/
300 static int PrepareInsertListEntries(struct MUI_ListData *data, int pos,
301 int count)
303 memmove(&data->entries[pos + count], &data->entries[pos],
304 (data->entries_num - pos) * sizeof(struct ListEntry *));
305 return 1;
308 /**************************************************************************
309 Removes count (already deinitalized) list entries starting az pos.
310 **************************************************************************/
311 static void RemoveListEntries(struct MUI_ListData *data, int pos, int count)
313 // FIXME: segfault if entries_num = pos = count = 1
314 memmove(&data->entries[pos], &data->entries[pos + count],
315 (data->entries_num - (pos + count)) * sizeof(struct ListEntry *));
318 /**************************************************************************
319 Frees all memory allocated by ParseListFormat()
320 **************************************************************************/
321 static void FreeListFormat(struct MUI_ListData *data)
323 int i;
325 if (data->ci)
327 for (i = 0; i < data->columns; i++)
329 FreeVec(data->ci[i].preparse);
330 data->ci[i].preparse = NULL;
332 FreeVec(data->ci);
333 data->ci = NULL;
335 FreeVec(data->preparses);
336 data->preparses = NULL;
337 if (data->strings)
339 FreeVec(data->strings - 1);
340 data->strings = NULL;
342 data->columns = 0;
345 /**************************************************************************
346 Parses the given format string (also frees a previously parsed format).
347 Return 0 on failure.
348 **************************************************************************/
349 static int ParseListFormat(struct MUI_ListData *data, STRPTR format)
351 int new_columns, i;
352 STRPTR ptr;
353 STRPTR format_sep;
354 char c;
356 IPTR args[ARG_CNT];
357 struct RDArgs *rdargs;
359 if (!format)
360 format = (STRPTR) "";
362 ptr = format;
364 FreeListFormat(data);
366 new_columns = 1;
368 /* Count the number of columns first */
369 while ((c = *ptr++))
370 if (c == ',')
371 new_columns++;
373 if (!(data->preparses =
374 AllocVec((new_columns + 10) * sizeof(STRPTR), 0)))
375 return 0;
377 if (!(data->strings = AllocVec((new_columns + 1 + 10)
378 * sizeof(STRPTR), 0))) /* hold enough space also for the entry pos,
379 * used by orginal MUI and also some
380 * security space */
381 return 0;
383 if (!(data->ci = AllocVec(new_columns * sizeof(struct ColumnInfo), 0)))
384 return 0;
386 // set defaults
387 for (i = 0; i < new_columns; i++)
389 data->ci[i].colno = -1; // -1 means: use unassigned column
390 data->ci[i].weight = 100;
391 data->ci[i].delta = 4;
392 data->ci[i].min_width = -1;
393 data->ci[i].max_width = -1;
394 data->ci[i].user_width = -1;
395 data->ci[i].bar = FALSE;
396 data->ci[i].preparse = NULL;
399 if ((format_sep = StrDup(format)) != 0)
401 for (i = 0; format_sep[i] != '\0'; i++)
403 if (format_sep[i] == ',')
404 format_sep[i] = '\0';
407 if ((rdargs = AllocDosObject(DOS_RDARGS, NULL)) != 0)
409 ptr = format_sep;
410 i = 0;
413 rdargs->RDA_Source.CS_Buffer = ptr;
414 rdargs->RDA_Source.CS_Length = strlen(ptr);
415 rdargs->RDA_Source.CS_CurChr = 0;
416 rdargs->RDA_DAList = 0;
417 rdargs->RDA_Buffer = NULL;
418 rdargs->RDA_BufSiz = 0;
419 rdargs->RDA_ExtHelp = NULL;
420 rdargs->RDA_Flags = 0;
422 memset(args, 0, sizeof args);
423 if (ReadArgs(FORMAT_TEMPLATE, args, rdargs))
425 if (args[ARG_COL])
426 data->ci[i].colno = *(LONG *) args[ARG_COL];
427 if (args[ARG_WEIGHT])
428 data->ci[i].weight = *(LONG *) args[ARG_WEIGHT];
429 if (args[ARG_DELTA])
430 data->ci[i].delta = *(LONG *) args[ARG_DELTA];
431 if (args[ARG_MINWIDTH])
432 data->ci[i].min_width =
433 *(LONG *) args[ARG_MINWIDTH];
434 if (args[ARG_MAXWIDTH])
435 data->ci[i].max_width =
436 *(LONG *) args[ARG_MAXWIDTH];
437 data->ci[i].bar = args[ARG_BAR];
438 if (args[ARG_PREPARSE])
439 data->ci[i].preparse =
440 StrDup((STRPTR) args[ARG_PREPARSE]);
442 FreeArgs(rdargs);
444 ptr += strlen(ptr) + 1;
445 i++;
447 while (i < new_columns);
448 FreeDosObject(DOS_RDARGS, rdargs);
450 FreeVec(format_sep);
453 for (i = 0; i < new_columns; i++)
455 D(bug("colno %d weight %d delta %d preparse %s\n",
456 data->ci[i].colno, data->ci[i].weight, data->ci[i].delta,
457 data->ci[i].preparse));
460 data->columns = new_columns;
461 data->strings++; /* Skip entry pos */
463 return 1;
466 /**************************************************************************
467 Call the MUIM_List_Display for the given entry. It fills out
468 data->string and data->preparses
469 **************************************************************************/
470 static void DisplayEntry(struct IClass *cl, Object *obj, int entry_pos)
472 struct MUI_ListData *data = INST_DATA(cl, obj);
473 APTR entry_data;
474 int col;
476 for (col = 0; col < data->columns; col++)
477 data->preparses[col] = data->ci[col].preparse;
479 if (entry_pos == ENTRY_TITLE)
481 if ((data->columns == 1) && (data->title != (STRPTR) 1))
483 *data->strings = data->title;
484 return;
486 entry_data = NULL; /* it's a title request */
488 else
489 entry_data = data->entries[entry_pos]->data;
491 /* Get the display formation */
492 DoMethod(obj, MUIM_List_Display, (IPTR) entry_data,
493 (IPTR) data->strings, entry_pos, (IPTR) data->preparses);
496 /**************************************************************************
497 Determine the dims of a single entry and adapt the columninfo according
498 to it. pos might be ENTRY_TITLE. Returns 0 if pos entry needs to
499 be redrawn after this operation, 1 if all entries need to be redrawn.
500 **************************************************************************/
501 static int CalcDimsOfEntry(struct IClass *cl, Object *obj, int pos)
503 struct MUI_ListData *data = INST_DATA(cl, obj);
504 struct ListEntry *entry = data->entries[pos];
505 int j;
506 int ret = 0;
508 if (!entry)
509 return ret;
511 if (!(_flags(obj) & MADF_SETUP))
512 return ret;
514 DisplayEntry(cl, obj, pos);
516 /* Set height to at least minheight */
517 if (data->entries[pos]->height < data->entry_minheight)
518 data->entries[pos]->height = data->entry_minheight;
520 for (j = 0; j < data->columns; j++)
522 ZText *text =
523 zune_text_new(data->preparses[j], data->strings[j],
524 ZTEXT_ARG_NONE, 0);
525 if (text != NULL)
527 zune_text_get_bounds(text, obj);
529 if (text->height > data->entries[pos]->height)
531 data->entries[pos]->height = text->height;
532 /* entry height changed, redraw all entries later */
533 ret = 1;
535 data->entries[pos]->widths[j] = text->width;
537 if (text->width > data->ci[j].entries_width)
539 /* This columns width is bigger than the other in the same
540 * columns, so we store this value
542 data->ci[j].entries_width = text->width;
543 /* column width changed, redraw all entries later */
544 ret = 1;
547 zune_text_destroy(text);
550 if (data->entries[pos]->height > data->entry_maxheight)
552 data->entry_maxheight = data->entries[pos]->height;
553 /* maximum entry height changed, redraw all entries later */
554 ret = 1;
557 return ret;
560 /**************************************************************************
561 Determine the widths of the entries
562 **************************************************************************/
563 static void CalcWidths(struct IClass *cl, Object *obj)
565 int i, j;
566 struct MUI_ListData *data = INST_DATA(cl, obj);
568 if (!(_flags(obj) & MADF_SETUP))
569 return;
571 for (j = 0; j < data->columns; j++)
572 data->ci[j].entries_width = 0;
574 data->entry_maxheight = 0;
575 data->entries_totalheight = 0;
576 data->entries_maxwidth = 0;
578 for (i = (data->title ? ENTRY_TITLE : 0); i < data->entries_num; i++)
580 CalcDimsOfEntry(cl, obj, i);
581 data->entries_totalheight += data->entries[i]->height;
584 for (j = 0; j < data->columns; j++)
585 data->entries_maxwidth += data->ci[j].entries_width;
587 if (!data->entry_maxheight)
588 data->entry_maxheight = 1;
591 /**************************************************************************
592 Calculates the number of visible entry lines. Returns 1 if it has
593 changed
594 **************************************************************************/
595 static int CalcVertVisible(struct IClass *cl, Object *obj)
597 struct MUI_ListData *data = INST_DATA(cl, obj);
598 int old_entries_visible = data->entries_visible;
599 int old_entries_top_pixel = data->entries_top_pixel;
601 data->entries_visible = (_mheight(obj) - data->title_height)
602 / (data->entry_maxheight /* + data->prefs_linespacing */ );
604 /* Distribute extra vertical space evenly between top and bottom of
605 * list */
607 data->entries_top_pixel = _mtop(obj) + data->title_height
608 + (_mheight(obj) - data->title_height
610 data->entries_visible *
611 (data->entry_maxheight /* + data->prefs_linespacing */ )) / 2;
613 return (old_entries_visible != data->entries_visible)
614 || (old_entries_top_pixel != data->entries_top_pixel);
617 /**************************************************************************
618 Default hook to compare two list entries. Works for strings only.
619 **************************************************************************/
620 AROS_UFH3S(int, default_compare_func,
621 AROS_UFHA(struct Hook *, h, A0),
622 AROS_UFHA(char *, s2, A2),
623 AROS_UFHA(char *, s1, A1))
625 AROS_USERFUNC_INIT
627 return Stricmp(s1, s2);
629 AROS_USERFUNC_EXIT
632 /**************************************************************************
633 OM_NEW
634 **************************************************************************/
635 IPTR List__OM_NEW(struct IClass *cl, Object *obj, struct opSet *msg)
637 struct MUI_ListData *data;
638 struct TagItem *tag;
639 struct TagItem *tags;
640 APTR *array = NULL;
641 LONG new_entries_active = MUIV_List_Active_Off;
643 obj = (Object *) DoSuperNewTags(cl, obj, NULL,
644 MUIA_Font, MUIV_Font_List,
645 MUIA_ShowSelState, FALSE,
646 MUIA_InputMode, MUIV_InputMode_RelVerify,
647 MUIA_Background, MUII_ListBack, TAG_MORE, (IPTR) msg->ops_AttrList);
648 if (!obj)
649 return FALSE;
651 data = INST_DATA(cl, obj);
653 data->columns = 1;
654 data->entries_active = MUIV_List_Active_Off;
655 data->intern_puddle_size = 2008;
656 data->intern_thresh_size = 1024;
657 data->default_compare_hook.h_Entry = (HOOKFUNC) default_compare_func;
658 data->default_compare_hook.h_SubEntry = 0;
659 data->compare_hook = &(data->default_compare_hook);
660 data->flags = LIST_SHOWDROPMARKS;
662 /* parse initial taglist */
663 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
665 switch (tag->ti_Tag)
667 case MUIA_List_Active:
668 new_entries_active = tag->ti_Data;
669 break;
671 case MUIA_List_Pool:
672 data->pool = (APTR) tag->ti_Data;
673 break;
675 case MUIA_List_PoolPuddleSize:
676 data->intern_puddle_size = tag->ti_Data;
677 break;
679 case MUIA_List_PoolThreshSize:
680 data->intern_thresh_size = tag->ti_Data;
681 break;
683 case MUIA_List_CompareHook:
684 data->compare_hook = (struct Hook *)tag->ti_Data;
685 if (data->compare_hook == NULL)
686 data->compare_hook = &data->default_compare_hook;
687 break;
689 case MUIA_List_ConstructHook:
690 data->construct_hook = (struct Hook *)tag->ti_Data;
691 break;
693 case MUIA_List_DestructHook:
694 data->destruct_hook = (struct Hook *)tag->ti_Data;
695 break;
697 case MUIA_List_DisplayHook:
698 data->display_hook = (struct Hook *)tag->ti_Data;
699 break;
701 case MUIA_List_MultiTestHook:
702 data->multi_test_hook = (struct Hook *)tag->ti_Data;
703 break;
705 case MUIA_List_SourceArray:
706 array = (APTR *) tag->ti_Data;
707 break;
709 case MUIA_List_Format:
710 data->format = StrDup((STRPTR) tag->ti_Data);
711 break;
713 case MUIA_List_Title:
714 data->title = (STRPTR) tag->ti_Data;
715 break;
717 case MUIA_List_MinLineHeight:
718 data->entry_minheight = tag->ti_Data;
719 break;
721 case MUIA_List_AdjustHeight:
722 _handle_bool_tag(data->flags, tag->ti_Data, LIST_ADJUSTHEIGHT);
723 break;
725 case MUIA_List_AdjustWidth:
726 _handle_bool_tag(data->flags, tag->ti_Data, LIST_ADJUSTWIDTH);
727 break;
729 case MUIA_List_AutoVisible:
730 _handle_bool_tag(data->flags, tag->ti_Data, LIST_AUTOVISIBLE);
731 break;
733 case MUIA_List_ShowDropMarks:
734 _handle_bool_tag(data->flags, tag->ti_Data, LIST_SHOWDROPMARKS);
735 break;
737 case MUIA_List_DragSortable:
738 _handle_bool_tag(data->flags, tag->ti_Data, LIST_DRAGSORTABLE);
739 set(obj, MUIA_Draggable, tag->ti_Data);
740 break;
744 if (!data->pool)
746 /* No memory pool given, so we create our own */
747 data->pool = data->intern_pool =
748 CreatePool(0, data->intern_puddle_size,
749 data->intern_thresh_size);
750 if (!data->pool)
752 CoerceMethod(cl, obj, OM_DISPOSE);
753 return 0;
757 /* parse the list format */
758 if (!(ParseListFormat(data, data->format)))
760 CoerceMethod(cl, obj, OM_DISPOSE);
761 return 0;
764 /* This is neccessary for at least the title */
765 if (!SetListSize(data, 0))
767 CoerceMethod(cl, obj, OM_DISPOSE);
768 return 0;
771 if (data->title)
773 if (!(data->entries[ENTRY_TITLE] = AllocListEntry(data)))
775 CoerceMethod(cl, obj, OM_DISPOSE);
776 return 0;
779 else
780 data->entries[ENTRY_TITLE] = NULL;
782 if (array)
784 int i;
785 /* Count the number of elements */
786 for (i = 0; array[i] != NULL; i++)
788 /* Insert them */
789 DoMethod(obj, MUIM_List_Insert, (IPTR) array, i,
790 MUIV_List_Insert_Top);
793 if ((data->entries_num) && (new_entries_active != MUIV_List_Active_Off))
795 switch (new_entries_active)
797 case MUIV_List_Active_Top:
798 new_entries_active = 0;
799 break;
801 case MUIV_List_Active_Bottom:
802 new_entries_active = data->entries_num - 1;
803 break;
806 if (new_entries_active < 0)
807 new_entries_active = 0;
808 else if (new_entries_active >= data->entries_num)
809 new_entries_active = data->entries_num - 1;
811 data->entries_active = new_entries_active;
812 /* Selected entry will be moved into visible area */
815 NewList((struct List *)&data->images);
817 D(bug("List_New(%lx)\n", obj));
819 return (IPTR) obj;
822 /**************************************************************************
823 OM_DISPOSE
824 **************************************************************************/
825 IPTR List__OM_DISPOSE(struct IClass *cl, Object *obj, Msg msg)
827 struct MUI_ListData *data = INST_DATA(cl, obj);
829 D(bug("List Dispose\n"));
831 /* Call destruct method for every entry and free the entries manually
832 * to avoid notification */
833 while (data->confirm_entries_num)
835 struct ListEntry *lentry =
836 data->entries[--data->confirm_entries_num];
837 DoMethod(obj, MUIM_List_Destruct, (IPTR) lentry->data,
838 (IPTR) data->pool);
839 FreeListEntry(data, lentry);
842 if (data->intern_pool)
843 DeletePool(data->intern_pool);
844 if (data->entries)
845 FreeVec(data->entries - 1);
846 /* title is currently before all other elements */
848 FreeListFormat(data);
849 FreeVec(data->format);
851 return DoSuperMethodA(cl, obj, msg);
855 /**************************************************************************
856 OM_SET
857 **************************************************************************/
858 IPTR List__OM_SET(struct IClass *cl, Object *obj, struct opSet *msg)
860 struct MUI_ListData *data = INST_DATA(cl, obj);
861 struct TagItem *tag;
862 struct TagItem *tags;
864 /* parse taglist */
865 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
867 switch (tag->ti_Tag)
869 case MUIA_List_CompareHook:
870 data->compare_hook = (struct Hook *)tag->ti_Data;
871 if (data->compare_hook == NULL)
872 data->compare_hook = &data->default_compare_hook;
873 break;
875 case MUIA_List_ConstructHook:
876 data->construct_hook = (struct Hook *)tag->ti_Data;
877 break;
879 case MUIA_List_DestructHook:
880 data->destruct_hook = (struct Hook *)tag->ti_Data;
881 break;
883 case MUIA_List_DisplayHook:
884 data->display_hook = (struct Hook *)tag->ti_Data;
885 break;
887 case MUIA_List_MultiTestHook:
888 data->multi_test_hook = (struct Hook *)tag->ti_Data;
889 if (data->multi_test_hook != NULL)
891 /* Clearing current selections is the easiest way to keep
892 * selections consistent with the new hook */
893 DoMethod(obj, MUIM_List_Select, MUIV_List_Select_All,
894 MUIV_List_Select_Off, NULL);
896 break;
898 case MUIA_List_Title:
899 data->title = (STRPTR) tag->ti_Data;
900 DoMethod(obj, MUIM_List_Redraw, MUIV_List_Redraw_All);
901 break;
903 case MUIA_List_VertProp_First:
904 data->vertprop_first = tag->ti_Data;
905 if (data->entries_first != tag->ti_Data)
907 set(obj, MUIA_List_First, tag->ti_Data);
909 break;
911 case MUIA_List_Format:
912 data->format = StrDup((STRPTR) tag->ti_Data);
913 ParseListFormat(data, data->format);
914 // FIXME: should we check for errors?
915 DoMethod(obj, MUIM_List_Redraw, MUIV_List_Redraw_All);
916 break;
918 case MUIA_List_VertProp_Entries:
919 data->vertprop_entries = tag->ti_Data;
920 break;
922 case MUIA_List_VertProp_Visible:
923 data->vertprop_visible = tag->ti_Data;
924 data->entries_visible = tag->ti_Data;
925 break;
927 case MUIA_List_Active:
929 LONG new_entries_active = tag->ti_Data;
931 if ((data->entries_num)
932 && (new_entries_active != MUIV_List_Active_Off))
934 switch (new_entries_active)
936 case MUIV_List_Active_Top:
937 new_entries_active = 0;
938 break;
940 case MUIV_List_Active_Bottom:
941 new_entries_active = data->entries_num - 1;
942 break;
944 case MUIV_List_Active_Up:
945 new_entries_active = data->entries_active - 1;
946 break;
948 case MUIV_List_Active_Down:
949 new_entries_active = data->entries_active + 1;
950 break;
952 case MUIV_List_Active_PageUp:
953 new_entries_active =
954 data->entries_active - data->entries_visible;
955 break;
957 case MUIV_List_Active_PageDown:
958 new_entries_active =
959 data->entries_active + data->entries_visible;
960 break;
963 if (new_entries_active < 0)
964 new_entries_active = 0;
965 else if (new_entries_active >= data->entries_num)
966 new_entries_active = data->entries_num - 1;
968 else
969 new_entries_active = -1;
971 if (data->entries_active != new_entries_active)
973 LONG old = data->entries_active;
974 data->entries_active = new_entries_active;
976 /* Selectchange stuff */
977 if (new_entries_active != -1)
979 DoMethod(obj, MUIM_List_SelectChange,
980 new_entries_active, MUIV_List_Select_On, 0);
981 DoMethod(obj, MUIM_List_SelectChange,
982 new_entries_active, MUIV_List_Select_Active, 0);
984 else
985 DoMethod(obj, MUIM_List_SelectChange,
986 MUIV_List_Active_Off, MUIV_List_Select_Off, 0);
988 data->update = 2;
989 data->update_pos = old;
990 MUI_Redraw(obj, MADF_DRAWUPDATE);
991 data->update = 2;
992 data->update_pos = data->entries_active;
993 MUI_Redraw(obj, MADF_DRAWUPDATE);
995 /* Make new active entry visible (if there is one and
996 list is visible) */
997 if (new_entries_active != -1
998 && (_flags(obj) & MADF_SETUP))
1000 DoMethod(obj, MUIM_List_Jump,
1001 MUIV_List_Jump_Active);
1005 break;
1007 case MUIA_List_First:
1008 data->update_pos = data->entries_first;
1009 data->update = 3;
1010 data->entries_first = tag->ti_Data;
1012 MUI_Redraw(obj, MADF_DRAWUPDATE);
1013 if ((data->vertprop_first != tag->ti_Data)
1014 && (!(data->flags & LIST_QUIET)))
1016 set(obj, MUIA_List_VertProp_First, tag->ti_Data);
1018 break;
1020 case MUIA_List_Visible: /* Shouldn't be settable? */
1021 if (data->vertprop_visible != tag->ti_Data)
1022 set(obj, MUIA_List_VertProp_Visible, tag->ti_Data);
1023 break;
1025 case MUIA_List_Entries:
1026 if (data->confirm_entries_num == tag->ti_Data)
1028 data->entries_num = tag->ti_Data;
1029 if (!(data->flags & LIST_QUIET))
1031 set(obj, MUIA_List_VertProp_Entries, data->entries_num);
1034 else
1036 D(bug("Bug: confirm_entries != MUIA_List_Entries!\n"));
1038 break;
1040 case MUIA_List_Quiet:
1041 _handle_bool_tag(data->flags, tag->ti_Data, LIST_QUIET);
1042 if (!tag->ti_Data)
1044 DoMethod(obj, MUIM_List_Redraw, MUIV_List_Redraw_All);
1045 if (data->entries_num != XGET(obj, MUIA_List_VertProp_Entries))
1046 set(obj, MUIA_List_VertProp_Entries, data->entries_num);
1047 if (data->vertprop_first !=
1048 XGET(obj, MUIA_List_VertProp_First))
1049 set(obj, MUIA_List_VertProp_First, data->vertprop_first);
1051 break;
1053 case MUIA_List_AutoVisible:
1054 _handle_bool_tag(data->flags, tag->ti_Data, LIST_AUTOVISIBLE);
1055 break;
1057 case MUIA_List_ShowDropMarks:
1058 _handle_bool_tag(data->flags, tag->ti_Data, LIST_SHOWDROPMARKS);
1059 break;
1061 case MUIA_List_DragSortable:
1062 _handle_bool_tag(data->flags, tag->ti_Data, LIST_DRAGSORTABLE);
1063 set(obj, MUIA_Draggable, tag->ti_Data);
1064 break;
1066 case MUIA_Selected:
1067 /* Swallow this so the Area class doesn't redraw us */
1068 tag->ti_Tag = TAG_IGNORE;
1069 break;
1073 return DoSuperMethodA(cl, obj, (Msg) msg);
1076 /**************************************************************************
1077 OM_GET
1078 **************************************************************************/
1079 IPTR List__OM_GET(struct IClass *cl, Object *obj, struct opGet *msg)
1081 /* small macro to simplify return value storage */
1082 #define STORE *(msg->opg_Storage)
1083 struct MUI_ListData *data = INST_DATA(cl, obj);
1085 switch (msg->opg_AttrID)
1087 case MUIA_List_Entries:
1088 STORE = data->entries_num;
1089 return 1;
1090 case MUIA_List_First:
1091 STORE = data->entries_first;
1092 return 1;
1093 case MUIA_List_Active:
1094 STORE = data->entries_active;
1095 return 1;
1096 case MUIA_List_InsertPosition:
1097 STORE = data->insert_position;
1098 return 1;
1099 case MUIA_List_Title:
1100 STORE = (IPTR) data->title;
1101 return 1;
1102 case MUIA_List_VertProp_Entries:
1103 STORE = data->vertprop_entries;
1104 return 1;
1105 case MUIA_List_VertProp_Visible:
1106 case MUIA_List_Visible:
1107 STORE = data->vertprop_visible;
1108 return 1;
1109 case MUIA_List_VertProp_First:
1110 STORE = data->vertprop_first;
1111 return 1;
1112 case MUIA_List_Format:
1113 STORE = (IPTR) data->format;
1114 return 1;
1115 case MUIA_List_AutoVisible:
1116 STORE = data->flags & LIST_AUTOVISIBLE;
1117 return 1;
1118 case MUIA_List_ShowDropMarks:
1119 STORE = data->flags & LIST_SHOWDROPMARKS;
1120 return 1;
1121 case MUIA_List_DragSortable:
1122 STORE = data->flags & LIST_DRAGSORTABLE;
1123 return 1;
1124 break;
1127 if (DoSuperMethodA(cl, obj, (Msg) msg))
1128 return 1;
1129 return 0;
1130 #undef STORE
1133 /**************************************************************************
1134 MUIM_Setup
1135 **************************************************************************/
1136 IPTR List__MUIM_Setup(struct IClass *cl, Object *obj,
1137 struct MUIP_Setup *msg)
1139 struct MUI_ListData *data = INST_DATA(cl, obj);
1141 if (!DoSuperMethodA(cl, obj, (Msg) msg))
1142 return 0;
1144 data->prefs_refresh = muiGlobalInfo(obj)->mgi_Prefs->list_refresh;
1145 data->prefs_linespacing =
1146 muiGlobalInfo(obj)->mgi_Prefs->list_linespacing;
1147 data->prefs_smoothed = muiGlobalInfo(obj)->mgi_Prefs->list_smoothed;
1148 data->prefs_smoothval = muiGlobalInfo(obj)->mgi_Prefs->list_smoothval;
1150 CalcWidths(cl, obj);
1152 data->list_cursor =
1153 zune_imspec_setup(MUII_ListCursor, muiRenderInfo(obj));
1154 data->list_select =
1155 zune_imspec_setup(MUII_ListSelect, muiRenderInfo(obj));
1156 data->list_selcur =
1157 zune_imspec_setup(MUII_ListSelCur, muiRenderInfo(obj));
1159 return 1;
1162 /**************************************************************************
1163 MUIM_Cleanup
1164 **************************************************************************/
1165 IPTR List__MUIM_Cleanup(struct IClass *cl, Object *obj,
1166 struct MUIP_Cleanup *msg)
1168 struct MUI_ListData *data = INST_DATA(cl, obj);
1169 struct ListImage *li = List_First(&data->images);
1171 while (li)
1173 struct ListImage *next = Node_Next(li);
1174 DoMethod(obj, MUIM_List_DeleteImage, (IPTR) li);
1175 li = next;
1178 zune_imspec_cleanup(data->list_cursor);
1179 zune_imspec_cleanup(data->list_select);
1180 zune_imspec_cleanup(data->list_selcur);
1182 return DoSuperMethodA(cl, obj, (Msg) msg);
1185 /**************************************************************************
1186 MUIM_AskMinMax
1187 **************************************************************************/
1188 IPTR List__MUIM_AskMinMax(struct IClass *cl, Object *obj,
1189 struct MUIP_AskMinMax *msg)
1191 struct MUI_ListData *data = INST_DATA(cl, obj);
1193 DoSuperMethodA(cl, obj, (Msg) msg);
1196 if ((data->flags & LIST_ADJUSTWIDTH) && (data->entries_num > 0))
1198 msg->MinMaxInfo->MinWidth += data->entries_maxwidth;
1199 msg->MinMaxInfo->DefWidth += data->entries_maxwidth;
1200 msg->MinMaxInfo->MaxWidth += data->entries_maxwidth;
1202 else
1204 msg->MinMaxInfo->MinWidth += 40;
1205 msg->MinMaxInfo->DefWidth += 100;
1206 msg->MinMaxInfo->MaxWidth = MUI_MAXMAX;
1209 if (data->entries_num > 0)
1211 if (data->flags & LIST_ADJUSTHEIGHT)
1213 msg->MinMaxInfo->MinHeight += data->entries_totalheight;
1214 msg->MinMaxInfo->DefHeight += data->entries_totalheight;
1215 msg->MinMaxInfo->MaxHeight += data->entries_totalheight;
1217 else
1219 ULONG h = data->entry_maxheight + data->prefs_linespacing;
1220 msg->MinMaxInfo->MinHeight += 2 * h + data->prefs_linespacing;
1221 msg->MinMaxInfo->DefHeight += 8 * h + data->prefs_linespacing;
1222 msg->MinMaxInfo->MaxHeight = MUI_MAXMAX;
1225 else
1227 msg->MinMaxInfo->MinHeight += 36;
1228 msg->MinMaxInfo->DefHeight += 96;
1229 msg->MinMaxInfo->MaxHeight = MUI_MAXMAX;
1231 D(bug("List %p minheigh=%d, line maxh=%d\n",
1232 obj, msg->MinMaxInfo->MinHeight, data->entry_maxheight));
1233 return TRUE;
1236 /****i* List.mui/MUIM_Layout *************************************************
1238 * NAME
1239 * MUIM_Layout
1241 ******************************************************************************
1245 IPTR List__MUIM_Layout(struct IClass *cl, Object *obj,
1246 struct MUIP_Layout *msg)
1248 struct MUI_ListData *data = INST_DATA(cl, obj);
1249 ULONG rc = DoSuperMethodA(cl, obj, (Msg) msg);
1250 LONG new_entries_first = data->entries_first;
1252 /* Calc the numbers of entries visible */
1253 CalcVertVisible(cl, obj);
1255 /* Ensure active entry is visible if requested */
1256 if (data->entries_active + 1 >=
1257 (data->entries_first + data->entries_visible)
1258 && (data->flags & LIST_AUTOVISIBLE) != 0)
1259 new_entries_first =
1260 data->entries_active - data->entries_visible + 1;
1262 /* Ensure there are no unnecessary empty lines */
1263 if ((new_entries_first + data->entries_visible >=
1264 data->entries_num)
1265 && (data->entries_visible <= data->entries_num))
1266 new_entries_first = data->entries_num - data->entries_visible;
1268 /* Always show the start of the list if it isn't long enough to fill the
1269 view */
1270 if (data->entries_num <= data->entries_visible)
1271 new_entries_first = 0;
1273 if (new_entries_first < 0)
1274 new_entries_first = 0;
1276 set(obj, new_entries_first != data->entries_first ?
1277 MUIA_List_First : TAG_IGNORE, new_entries_first);
1279 /* So the notify happens */
1280 set(obj, MUIA_List_VertProp_Visible, data->entries_visible);
1282 return rc;
1286 /**************************************************************************
1287 MUIM_Show
1288 **************************************************************************/
1289 IPTR List__MUIM_Show(struct IClass *cl, Object *obj,
1290 struct MUIP_Show *msg)
1292 struct MUI_ListData *data = INST_DATA(cl, obj);
1293 ULONG rc = DoSuperMethodA(cl, obj, (Msg) msg);
1295 zune_imspec_show(data->list_cursor, obj);
1296 zune_imspec_show(data->list_select, obj);
1297 zune_imspec_show(data->list_selcur, obj);
1298 return rc;
1302 /**************************************************************************
1303 MUIM_Hide
1304 **************************************************************************/
1305 IPTR List__MUIM_Hide(struct IClass *cl, Object *obj,
1306 struct MUIP_Hide *msg)
1308 struct MUI_ListData *data = INST_DATA(cl, obj);
1310 zune_imspec_hide(data->list_cursor);
1311 zune_imspec_hide(data->list_select);
1312 zune_imspec_hide(data->list_selcur);
1314 return DoSuperMethodA(cl, obj, (Msg) msg);
1318 /**************************************************************************
1319 Draw an entry at entry_pos at the given row. To draw the title, set pos to
1320 ENTRY_TITLE
1321 **************************************************************************/
1322 static VOID List_DrawEntry(struct IClass *cl, Object *obj, int entry_pos,
1323 int y)
1325 struct MUI_ListData *data = INST_DATA(cl, obj);
1326 int col, x1, x2;
1328 /* To be sure we don't draw anything if there is no title */
1329 if (entry_pos == ENTRY_TITLE && !data->title)
1330 return;
1332 DisplayEntry(cl, obj, entry_pos);
1333 x1 = _mleft(obj);
1335 for (col = 0; col < data->columns; col++)
1337 ZText *text;
1338 x2 = x1 + data->ci[col].entries_width;
1340 if ((text =
1341 zune_text_new(data->preparses[col], data->strings[col],
1342 ZTEXT_ARG_NONE, 0)))
1344 /* Could be made simpler, as we don't really need the bounds */
1345 zune_text_get_bounds(text, obj);
1346 /* Note, this was MPEN_SHADOW before */
1347 SetAPen(_rp(obj), muiRenderInfo(obj)->mri_Pens[MPEN_TEXT]);
1348 zune_text_draw(text, obj, x1, x2, y); /* totally wrong! */
1349 zune_text_destroy(text);
1351 x1 = x2 + data->ci[col].delta + (data->ci[col].bar ? BAR_WIDTH : 0);
1355 /**************************************************************************
1356 MUIM_Draw
1357 **************************************************************************/
1358 IPTR List__MUIM_Draw(struct IClass *cl, Object *obj, struct MUIP_Draw *msg)
1360 struct MUI_ListData *data = INST_DATA(cl, obj);
1361 int entry_pos, y;
1362 APTR clip;
1363 int start, end;
1364 BOOL scroll_caused_damage = FALSE;
1365 struct MUI_ImageSpec_intern *highlight;
1367 if (data->flags & LIST_QUIET)
1368 return 0;
1370 DoSuperMethodA(cl, obj, (Msg) msg);
1372 /* Calculate the title height */
1373 if (data->title)
1375 data->title_height = data->entries[ENTRY_TITLE]->height + 2;
1377 else
1379 data->title_height = 0;
1382 /* Calc the numbers of entries visible */
1383 CalcVertVisible(cl, obj);
1385 if ((msg->flags & MADF_DRAWUPDATE) == 0 || data->update == 1)
1387 DoMethod(obj, MUIM_DrawBackground, _mleft(obj), _mtop(obj),
1388 _mwidth(obj), _mheight(obj),
1389 0, data->entries_first * data->entry_maxheight, 0);
1392 clip = MUI_AddClipping(muiRenderInfo(obj), _mleft(obj), _mtop(obj),
1393 _mwidth(obj), _mheight(obj));
1395 if ((msg->flags & MADF_DRAWUPDATE) == 0 || data->update == 1)
1397 y = _mtop(obj);
1398 /* Draw Title
1400 if (data->title_height && data->title)
1402 List_DrawEntry(cl, obj, ENTRY_TITLE, y);
1403 y += data->entries[ENTRY_TITLE]->height;
1404 SetAPen(_rp(obj), _pens(obj)[MPEN_SHADOW]);
1405 Move(_rp(obj), _mleft(obj), y);
1406 Draw(_rp(obj), _mright(obj), y);
1407 SetAPen(_rp(obj), _pens(obj)[MPEN_SHINE]);
1408 y++;
1409 Move(_rp(obj), _mleft(obj), y);
1410 Draw(_rp(obj), _mright(obj), y);
1414 y = data->entries_top_pixel;
1416 start = data->entries_first;
1417 end = data->entries_first + data->entries_visible;
1419 if ((msg->flags & MADF_DRAWUPDATE) && data->update == 3)
1421 int diffy = data->entries_first - data->update_pos;
1422 int top, bottom;
1423 if (abs(diffy) < data->entries_visible)
1425 scroll_caused_damage =
1426 (_rp(obj)->Layer->Flags & LAYERREFRESH) ? FALSE : TRUE;
1428 ScrollRaster(_rp(obj), 0, diffy * data->entry_maxheight,
1429 _mleft(obj), y,
1430 _mright(obj),
1431 y + data->entry_maxheight * data->entries_visible);
1433 scroll_caused_damage =
1434 scroll_caused_damage
1435 && (_rp(obj)->Layer->Flags & LAYERREFRESH);
1437 if (diffy > 0)
1439 start = end - diffy;
1440 y += data->entry_maxheight * (data->entries_visible -
1441 diffy);
1443 else
1444 end = start - diffy;
1447 top = y;
1448 bottom = y + (end - start) * data->entry_maxheight;
1450 DoMethod(obj, MUIM_DrawBackground, _mleft(obj), top,
1451 _mwidth(obj), bottom - top + 1,
1453 top - _mtop(obj) + data->entries_first * data->entry_maxheight,
1457 for (entry_pos = start;
1458 entry_pos < end && entry_pos < data->entries_num; entry_pos++)
1460 struct ListEntry *entry = data->entries[entry_pos];
1462 if (!(msg->flags & MADF_DRAWUPDATE) ||
1463 ((msg->flags & MADF_DRAWUPDATE) && data->update == 1) ||
1464 ((msg->flags & MADF_DRAWUPDATE) && data->update == 3) ||
1465 ((msg->flags & MADF_DRAWUPDATE) && data->update == 2
1466 && data->update_pos == entry_pos))
1468 /* Choose appropriate highlight image */
1470 if (entry_pos == data->entries_active
1471 && entry->flags & ENTRY_SELECTED)
1472 highlight = data->list_selcur;
1473 else if (entry_pos == data->entries_active)
1474 highlight = data->list_cursor;
1475 else if (entry->flags & ENTRY_SELECTED)
1476 highlight = data->list_select;
1477 else
1478 highlight = NULL;
1480 /* Draw highlight or background */
1482 if (highlight != NULL)
1484 zune_imspec_draw(highlight, muiRenderInfo(obj),
1485 _mleft(obj), y, _mwidth(obj), data->entry_maxheight,
1486 0, y - data->entries_top_pixel, 0);
1488 else if ((msg->flags & MADF_DRAWUPDATE) && data->update == 2
1489 && data->update_pos == entry_pos)
1491 DoMethod(obj, MUIM_DrawBackground, _mleft(obj), y,
1492 _mwidth(obj), data->entry_maxheight, 0,
1493 y - _mtop(obj) +
1494 data->entries_first * data->entry_maxheight, 0);
1497 List_DrawEntry(cl, obj, entry_pos, y);
1499 y += data->entry_maxheight;
1502 MUI_RemoveClipping(muiRenderInfo(obj), clip);
1504 data->update = 0;
1506 if (scroll_caused_damage)
1508 if (MUI_BeginRefresh(muiRenderInfo(obj), 0))
1510 /* Theoretically it might happen that more damage is caused
1511 after ScrollRaster. By something else, like window movement
1512 in front of our window. Therefore refresh root object of
1513 window, not just this object */
1515 Object *o = NULL;
1517 get(_win(obj), MUIA_Window_RootObject, &o);
1518 MUI_Redraw(o, MADF_DRAWOBJECT);
1520 MUI_EndRefresh(muiRenderInfo(obj), 0);
1524 ULONG x1 = _mleft(obj);
1525 ULONG col;
1526 y = _mtop(obj);
1528 if (data->title_height && data->title)
1530 for (col = 0; col < data->columns; col++)
1532 ULONG halfdelta = data->ci[col].delta / 2;
1533 x1 += data->ci[col].entries_width + halfdelta;
1535 if (x1 + (data->ci[col].bar ? BAR_WIDTH : 0) > _mright(obj))
1536 break;
1538 if (data->ci[col].bar)
1540 SetAPen(_rp(obj), _pens(obj)[MPEN_SHINE]);
1541 Move(_rp(obj), x1, y);
1542 Draw(_rp(obj), x1,
1543 y + data->entries[ENTRY_TITLE]->height - 1);
1544 SetAPen(_rp(obj), _pens(obj)[MPEN_SHADOW]);
1545 Move(_rp(obj), x1 + 1, y);
1546 Draw(_rp(obj), x1 + 1,
1547 y + data->entries[ENTRY_TITLE]->height - 1);
1549 x1 += BAR_WIDTH;
1551 x1 += data->ci[col].delta - halfdelta;
1553 y += data->entries[ENTRY_TITLE]->height + 1;
1556 x1 = _mleft(obj);
1558 for (col = 0; col < data->columns; col++)
1560 ULONG halfdelta = data->ci[col].delta / 2;
1561 x1 += data->ci[col].entries_width + halfdelta;
1563 if (x1 + (data->ci[col].bar ? BAR_WIDTH : 0) > _mright(obj))
1564 break;
1566 if (data->ci[col].bar)
1568 SetAPen(_rp(obj), _pens(obj)[MPEN_SHINE]);
1569 Move(_rp(obj), x1, y);
1570 Draw(_rp(obj), x1, _mbottom(obj));
1571 SetAPen(_rp(obj), _pens(obj)[MPEN_SHADOW]);
1572 Move(_rp(obj), x1 + 1, y);
1573 Draw(_rp(obj), x1 + 1, _mbottom(obj));
1575 x1 += BAR_WIDTH;
1578 x1 += data->ci[col].delta - halfdelta;
1581 return 0;
1584 /****** List.mui/MUIM_List_Clear *********************************************
1586 * NAME
1587 * MUIM_List_Clear (V4)
1589 * SYNOPSIS
1590 * DoMethod(obj, MUIM_List_Clear);
1592 * FUNCTION
1593 * Removes all entries from the list.
1595 ******************************************************************************
1599 IPTR List__MUIM_Clear(struct IClass *cl, Object *obj,
1600 struct MUIP_List_Clear *msg)
1602 struct MUI_ListData *data = INST_DATA(cl, obj);
1604 while (data->confirm_entries_num)
1606 struct ListEntry *lentry =
1607 data->entries[--data->confirm_entries_num];
1608 DoMethod(obj, MUIM_List_Destruct, (IPTR) lentry->data,
1609 (IPTR) data->pool);
1610 FreeListEntry(data, lentry);
1612 /* Should never fail when shrinking */
1613 SetListSize(data, 0);
1616 if (data->confirm_entries_num != data->entries_num)
1618 SetAttrs(obj, MUIA_List_Entries, 0, MUIA_List_First, 0,
1619 /* Notify only when no entry was active */
1620 data->entries_active !=
1621 MUIV_List_Active_Off ? MUIA_List_Active : TAG_DONE,
1622 MUIV_List_Active_Off, TAG_DONE);
1624 data->update = 1;
1625 MUI_Redraw(obj, MADF_DRAWUPDATE);
1628 return 0;
1631 /**************************************************************************
1632 MUIM_List_Exchange
1633 **************************************************************************/
1634 IPTR List__MUIM_Exchange(struct IClass *cl, Object *obj,
1635 struct MUIP_List_Exchange *msg)
1637 struct MUI_ListData *data = INST_DATA(cl, obj);
1638 LONG pos1, pos2;
1640 switch (msg->pos1)
1642 case MUIV_List_Exchange_Top:
1643 pos1 = 0;
1644 break;
1645 case MUIV_List_Exchange_Active:
1646 pos1 = data->entries_active;
1647 break;
1648 case MUIV_List_Exchange_Bottom:
1649 pos1 = data->entries_num - 1;
1650 break;
1651 default:
1652 pos1 = msg->pos1;
1655 switch (msg->pos2)
1657 case MUIV_List_Exchange_Top:
1658 pos2 = 0;
1659 break;
1660 case MUIV_List_Exchange_Active:
1661 pos2 = data->entries_active;
1662 break;
1663 case MUIV_List_Exchange_Bottom:
1664 pos2 = data->entries_num - 1;
1665 break;
1666 case MUIV_List_Exchange_Next:
1667 pos2 = pos1 + 1;
1668 break;
1669 case MUIV_List_Exchange_Previous:
1670 pos2 = pos1 - 1;
1671 break;
1672 default:
1673 pos2 = msg->pos2;
1676 if (pos1 >= 0 && pos1 < data->entries_num && pos2 >= 0
1677 && pos2 < data->entries_num && pos1 != pos2)
1679 struct ListEntry *save = data->entries[pos1];
1680 data->entries[pos1] = data->entries[pos2];
1681 data->entries[pos2] = save;
1683 data->update = 2;
1684 data->update_pos = pos1;
1685 MUI_Redraw(obj, MADF_DRAWUPDATE);
1687 data->update = 2;
1688 data->update_pos = pos2;
1689 MUI_Redraw(obj, MADF_DRAWUPDATE);
1691 return TRUE;
1693 else
1695 return FALSE;
1699 /**************************************************************************
1700 MUIM_List_Redraw
1701 **************************************************************************/
1702 IPTR List__MUIM_Redraw(struct IClass *cl, Object *obj,
1703 struct MUIP_List_Redraw *msg)
1705 struct MUI_ListData *data = INST_DATA(cl, obj);
1707 if (!(data->flags & LIST_QUIET))
1709 if (msg->pos == MUIV_List_Redraw_All)
1711 data->update = 1;
1712 CalcWidths(cl, obj);
1713 MUI_Redraw(obj, MADF_DRAWUPDATE);
1715 else
1717 LONG pos = -1;
1718 if (msg->pos == MUIV_List_Redraw_Active)
1719 pos = data->entries_active;
1720 else if (msg->pos == MUIV_List_Redraw_Entry)
1722 LONG i;
1723 for (i = 0; i < data->entries_num; i++)
1724 if (data->entries[i]->data == msg->entry)
1726 pos = i;
1727 break;
1730 else
1731 pos = msg->pos;
1733 if (pos != -1)
1735 if (CalcDimsOfEntry(cl, obj, pos))
1736 data->update = 1;
1737 else
1739 data->update = 2;
1740 data->update_pos = pos;
1742 MUI_Redraw(obj, MADF_DRAWUPDATE);
1746 return 0;
1749 /**************************************************************************
1750 MUIM_List_Remove
1751 **************************************************************************/
1752 IPTR List__MUIM_Remove(struct IClass *cl, Object *obj,
1753 struct MUIP_List_Remove *msg)
1755 struct MUI_ListData *data = INST_DATA(cl, obj);
1756 LONG pos, cur;
1757 LONG new_act;
1758 struct ListEntry *lentry;
1759 //int rem_count = 1;
1761 if (!data->entries_num)
1762 return 0;
1764 switch (msg->pos)
1766 case MUIV_List_Remove_First:
1767 pos = 0;
1768 break;
1770 case MUIV_List_Remove_Active:
1771 pos = data->entries_active;
1772 break;
1774 case MUIV_List_Remove_Last:
1775 pos = data->entries_num - 1;
1776 break;
1778 case MUIV_List_Remove_Selected:
1779 /* TODO: needs special handling */
1780 pos = data->entries_active;
1781 break;
1783 default:
1784 pos = msg->pos;
1785 break;
1788 if (pos < 0 || pos >= data->entries_num)
1789 return 0;
1791 new_act = data->entries_active;
1793 if (pos == new_act && new_act == data->entries_num - 1)
1794 new_act--; /* might become MUIV_List_Active_Off */
1796 lentry = data->entries[pos];
1797 DoMethod(obj, MUIM_List_Destruct, (IPTR) lentry->data,
1798 (IPTR) data->pool);
1800 cur = pos + 1;
1802 RemoveListEntries(data, pos, cur - pos);
1803 data->confirm_entries_num -= cur - pos;
1805 /* ensure that the active element is in a valid range */
1806 if (new_act >= data->entries_num)
1807 new_act = data->entries_num - 1;
1809 SetAttrs(obj, MUIA_List_Entries, data->confirm_entries_num,
1810 (new_act >= pos) || (new_act != data->entries_active) ?
1811 MUIA_List_Active : TAG_DONE,
1812 new_act, /* Inform only if neccessary (for notify) */
1813 TAG_DONE);
1815 data->update = 1;
1816 MUI_Redraw(obj, MADF_DRAWUPDATE);
1818 return 0;
1821 /**************************************************************************
1822 MUIM_List_Select
1823 **************************************************************************/
1824 IPTR List__MUIM_Select(struct IClass *cl, Object *obj,
1825 struct MUIP_List_Select *msg)
1827 struct MUI_ListData *data = INST_DATA(cl, obj);
1828 LONG pos, i, count, selcount=0, state=0;
1829 BOOL multi_allowed = TRUE, new_select_state = FALSE;
1831 /* Establish the range of entries affected */
1832 switch (msg->pos)
1834 case MUIV_List_Select_Active:
1835 pos = data->entries_active;
1836 if (pos == MUIV_List_Active_Off)
1837 count = 0;
1838 else
1839 count = 1;
1840 break;
1842 case MUIV_List_Select_All:
1843 pos = 0;
1844 count = data->entries_num;
1845 break;
1847 default:
1848 pos = msg->pos;
1849 count = 1;
1850 if (pos < 0 || pos >= data->entries_num)
1851 return 0;
1852 break;
1855 if (msg->seltype != MUIV_List_Select_Ask && data->multi_test_hook != NULL)
1857 /* Disallow selection of an additional entry if there is a currently
1858 selected entry that is not multi-selectable (in such case there
1859 will only be one entry currently selected, so no need to iterate) */
1860 i = MUIV_List_NextSelected_Start;
1861 DoMethod(obj, MUIM_List_NextSelected, (IPTR) &i);
1862 if (i != MUIV_List_NextSelected_End)
1863 selcount++;
1864 if (data->multi_test_hook != NULL && selcount != 0)
1865 multi_allowed = CallHookPkt(data->multi_test_hook, NULL,
1866 data->entries[i]->data);
1869 /* Change or check state of each entry in the range */
1870 for (i = pos; i < pos + count; i++)
1872 state = data->entries[i]->flags & ENTRY_SELECTED;
1873 switch (msg->seltype)
1875 case MUIV_List_Select_Off:
1876 new_select_state = FALSE;
1877 break;
1879 case MUIV_List_Select_On:
1880 new_select_state = TRUE;
1881 break;
1883 case MUIV_List_Select_Toggle:
1884 new_select_state = !state;
1885 break;
1887 default:
1888 if (data->entries[i]->flags & ENTRY_SELECTED)
1889 selcount++;
1890 break;
1893 if (msg->seltype != MUIV_List_Select_Ask)
1895 /* Disallow selection if entry is not multi-selectable and
1896 * there are already selected entries */
1897 if (data->multi_test_hook != NULL && new_select_state)
1898 new_select_state = multi_allowed && (selcount == 0 ||
1899 CallHookPkt(data->multi_test_hook, NULL,
1900 data->entries[i]->data));
1902 if (new_select_state)
1903 data->entries[i]->flags |= ENTRY_SELECTED;
1904 else
1905 data->entries[i]->flags &= ~ENTRY_SELECTED;
1909 /* Report old state or number of selected entries */
1910 if (msg->info)
1912 if (msg->pos == MUIV_List_Select_All
1913 && msg->seltype == MUIV_List_Select_Ask)
1914 *msg->info = selcount;
1915 else
1916 *msg->info = state;
1919 /* Redraw unless it was just an enquiry */
1920 if (msg->seltype != MUIV_List_Select_Ask)
1922 if (count > 1)
1923 data->update = 1;
1924 else
1926 data->update = 2;
1927 data->update_pos = pos;
1929 MUI_Redraw(obj, MADF_DRAWUPDATE);
1932 return 0;
1935 /**************************************************************************
1936 MUIM_List_Insert
1937 **************************************************************************/
1939 IPTR List__MUIM_Insert(struct IClass *cl, Object *obj,
1940 struct MUIP_List_Insert *msg)
1942 struct MUI_ListData *data = INST_DATA(cl, obj);
1943 LONG pos, count, sort;
1945 count = msg->count;
1946 sort = 0;
1948 if (count == -1)
1950 /* Count the number of entries */
1951 for (count = 0; msg->entries[count] != NULL; count++)
1955 if (count <= 0)
1956 return ~0;
1958 switch (msg->pos)
1960 case MUIV_List_Insert_Top:
1961 pos = 0;
1962 break;
1964 case MUIV_List_Insert_Active:
1965 if (data->entries_active != -1)
1966 pos = data->entries_active;
1967 else
1968 pos = 0;
1969 break;
1971 case MUIV_List_Insert_Sorted:
1972 pos = data->entries_num;
1973 sort = 1; /* we sort'em later */
1974 break;
1976 case MUIV_List_Insert_Bottom:
1977 pos = data->entries_num;
1978 break;
1980 default:
1981 if (msg->pos > data->entries_num)
1982 pos = data->entries_num;
1983 else if (msg->pos < 0)
1984 pos = 0;
1985 else
1986 pos = msg->pos;
1987 break;
1990 if (!(SetListSize(data, data->entries_num + count)))
1991 return ~0;
1993 LONG until = pos + count;
1994 APTR *toinsert = msg->entries;
1996 if (!(PrepareInsertListEntries(data, pos, count)))
1997 return ~0;
1999 while (pos < until)
2001 struct ListEntry *lentry;
2003 if (!(lentry = AllocListEntry(data)))
2005 /* Panic, but we must be in a consistent state, so remove
2006 * the space where the following list entries should have gone
2008 RemoveListEntries(data, pos, until - pos);
2009 return ~0;
2012 /* now call the construct method which returns us a pointer which
2013 we need to store */
2014 lentry->data = (APTR) DoMethod(obj, MUIM_List_Construct,
2015 (IPTR) * toinsert, (IPTR) data->pool);
2016 if (!lentry->data)
2018 FreeListEntry(data, lentry);
2019 RemoveListEntries(data, pos, until - pos);
2021 /* TODO: Also check for visible stuff like below */
2022 if (data->entries_num != data->confirm_entries_num)
2023 set(obj, MUIA_List_Entries, data->confirm_entries_num);
2024 return ~0;
2027 data->entries[pos] = lentry;
2028 data->confirm_entries_num++;
2030 if (_flags(obj) & MADF_SETUP)
2032 /* We have to calculate the width and height of the newly
2033 * inserted entry. This has to be done after inserting the
2034 * element into the list */
2035 CalcDimsOfEntry(cl, obj, pos);
2038 toinsert++;
2039 pos++;
2042 /* Recalculate the number of visible entries */
2043 if (_flags(obj) & MADF_SETUP)
2044 CalcVertVisible(cl, obj);
2046 if (data->entries_num != data->confirm_entries_num)
2048 SetAttrs(obj,
2049 MUIA_List_Entries, data->confirm_entries_num,
2050 MUIA_List_Visible, data->entries_visible, TAG_DONE);
2053 /* If the array is already sorted, we could do a simple insert
2054 * sort and would be much faster than with qsort.
2055 * If an array is not yet sorted, does a MUIV_List_Insert_Sorted
2056 * sort the whole array?
2058 * I think, we better sort the whole array:
2060 if (sort)
2062 DoMethod(obj, MUIM_List_Sort);
2063 /* TODO: which pos to return here !? */
2064 /* MUIM_List_Sort already called MUI_Redraw */
2066 else
2068 data->update = 1;
2069 MUI_Redraw(obj, MADF_DRAWUPDATE);
2071 data->insert_position = pos;
2073 return (ULONG) pos;
2076 /**************************************************************************
2077 MUIM_List_InsertSingle
2078 **************************************************************************/
2079 IPTR List__MUIM_InsertSingle(struct IClass *cl, Object *obj,
2080 struct MUIP_List_InsertSingle *msg)
2082 return DoMethod(obj, MUIM_List_Insert, (IPTR) & msg->entry, 1,
2083 msg->pos);
2086 /**************************************************************************
2087 MUIM_List_GetEntry
2088 **************************************************************************/
2089 IPTR List__MUIM_GetEntry(struct IClass *cl, Object *obj,
2090 struct MUIP_List_GetEntry *msg)
2092 struct MUI_ListData *data = INST_DATA(cl, obj);
2093 int pos = msg->pos;
2095 if (pos == MUIV_List_GetEntry_Active)
2096 pos = data->entries_active;
2098 if (pos < 0 || pos >= data->entries_num)
2100 *msg->entry = NULL;
2101 return 0;
2103 *msg->entry = data->entries[pos]->data;
2104 return (IPTR) *msg->entry;
2107 /**************************************************************************
2108 MUIM_List_Construct
2109 **************************************************************************/
2110 IPTR List__MUIM_Construct(struct IClass *cl, Object *obj,
2111 struct MUIP_List_Construct *msg)
2113 struct MUI_ListData *data = INST_DATA(cl, obj);
2115 if (NULL == data->construct_hook)
2116 return (IPTR) msg->entry;
2117 if ((IPTR) data->construct_hook == MUIV_List_ConstructHook_String)
2119 int len = msg->entry ? strlen((STRPTR) msg->entry) : 0;
2120 ULONG *mem = AllocPooled(msg->pool, len + 5);
2122 if (NULL == mem)
2123 return 0;
2124 mem[0] = len + 5;
2125 if (msg->entry != NULL)
2126 strcpy((STRPTR) (mem + 1), (STRPTR) msg->entry);
2127 else
2128 *(STRPTR) (mem + 1) = 0;
2129 return (IPTR) (mem + 1);
2131 return CallHookPkt(data->construct_hook, msg->pool, msg->entry);
2134 /**************************************************************************
2135 MUIM_List_Destruct
2136 **************************************************************************/
2137 IPTR List__MUIM_Destruct(struct IClass *cl, Object *obj,
2138 struct MUIP_List_Destruct *msg)
2140 struct MUI_ListData *data = INST_DATA(cl, obj);
2142 if (NULL == data->destruct_hook)
2143 return 0;
2145 if ((IPTR) data->destruct_hook == MUIV_List_DestructHook_String)
2147 ULONG *mem = ((ULONG *) msg->entry) - 1;
2148 FreePooled(msg->pool, mem, mem[0]);
2150 else
2152 CallHookPkt(data->destruct_hook, msg->pool, msg->entry);
2154 return 0;
2157 /****** List.mui/MUIM_List_Compare *******************************************
2159 * NAME
2160 * MUIM_List_Compare (V20)
2162 * SYNOPSIS
2163 * DoMethod(obj, MUIM_List_Compare, APTR entry1, APTR entry2,
2164 * LONG sort_type1, LONG sort_type2);
2166 * FUNCTION
2167 * Compare two list entries according to the current comparison hook
2168 * (MUIA_List_CompareHook).
2170 * INPUTS
2171 * entry1 - the first entry data.
2172 * entry2 - the second entry data.
2173 * sort_type1 - undocumented.
2174 * sort_type2 - undocumented.
2176 * SEE ALSO
2177 * MUIA_List_CompareHook, MUIM_List_Sort.
2179 ******************************************************************************
2183 IPTR List__MUIM_Compare(struct IClass *cl, Object *obj,
2184 struct MUIP_List_Compare *msg)
2186 struct MUI_ListData *data = INST_DATA(cl, obj);
2188 return CallHookPkt(data->compare_hook, msg->entry2, msg->entry1);
2191 /**************************************************************************
2192 MUIM_List_Display
2193 **************************************************************************/
2194 IPTR List__MUIM_Display(struct IClass *cl, Object *obj,
2195 struct MUIP_List_Display *msg)
2197 struct MUI_ListData *data = INST_DATA(cl, obj);
2199 if (NULL == data->display_hook)
2201 if (msg->entry)
2202 *msg->array = msg->entry;
2203 else
2204 *msg->array = 0;
2205 return 1;
2208 *((ULONG *) (msg->array - 1)) = msg->entry_pos;
2209 return CallHookPkt(data->display_hook, msg->array, msg->entry);
2212 /**************************************************************************
2213 MUIM_List_SelectChange
2214 **************************************************************************/
2215 IPTR List__MUIM_SelectChange(struct IClass *cl, Object *obj,
2216 struct MUIP_List_SelectChange *msg)
2218 return 1;
2221 /**************************************************************************
2222 MUIM_List_CreateImage
2223 Called by a List subclass in its Setup method.
2224 Connects an Area subclass object to the list, much like an object gets
2225 connected to a window. List calls Setup and AskMinMax on that object,
2226 keeps a reference to it (that reference will be returned).
2227 Text engine will dereference that pointer and draw the object with its
2228 default size.
2229 **************************************************************************/
2230 IPTR List__MUIM_CreateImage(struct IClass *cl, Object *obj,
2231 struct MUIP_List_CreateImage *msg)
2233 struct MUI_ListData *data = INST_DATA(cl, obj);
2234 struct ListImage *li;
2236 if (!msg->obj)
2237 return 0;
2239 /* List must be already setup in Setup of your subclass */
2240 if (!(_flags(obj) & MADF_SETUP))
2241 return 0;
2242 li = AllocPooled(data->pool, sizeof(struct ListImage));
2243 if (!li)
2244 return 0;
2245 li->obj = msg->obj;
2247 AddTail((struct List *)&data->images, (struct Node *)li);
2248 DoMethod(li->obj, MUIM_ConnectParent, (IPTR) obj);
2249 DoSetupMethod(li->obj, muiRenderInfo(obj));
2252 return (IPTR) li;
2255 /**************************************************************************
2256 MUIM_List_DeleteImage
2257 **************************************************************************/
2258 IPTR List__MUIM_DeleteImage(struct IClass *cl, Object *obj,
2259 struct MUIP_List_DeleteImage *msg)
2261 struct MUI_ListData *data = INST_DATA(cl, obj);
2262 struct ListImage *li = (struct ListImage *)msg->listimg;
2264 if (li)
2266 DoMethod(li->obj, MUIM_Cleanup);
2267 DoMethod(li->obj, MUIM_DisconnectParent);
2268 Remove((struct Node *)li);
2269 FreePooled(data->pool, li, sizeof(struct ListImage));
2272 return 0;
2275 /****** List.mui/MUIM_List_Jump **********************************************
2277 * NAME
2278 * MUIM_List_Jump (V4)
2280 * SYNOPSIS
2281 * DoMethod(obj, MUIM_List_Jump, LONG pos);
2283 * FUNCTION
2284 * Scrolls the list so that a particular entry is visible.
2286 * INPUTS
2287 * pos - index of entry that should become visible, or one of these
2288 * special values:
2289 * MUIV_List_Jump_Active: show the active entry.
2290 * MUIV_List_Jump_Top: show the first entry.
2291 * MUIV_List_Jump_Bottom: show the last entry.
2292 * MUIV_List_Jump_Up: show the previous hidden entry.
2293 * MUIV_List_Jump_Down: show the next hidden entry.
2295 ******************************************************************************
2299 IPTR List__MUIM_Jump(struct IClass *cl, Object *obj,
2300 struct MUIP_List_Jump *msg)
2302 struct MUI_ListData *data = INST_DATA(cl, obj);
2303 LONG pos = msg->pos;
2305 switch (pos)
2307 case MUIV_List_Jump_Top:
2308 pos = 0;
2309 break;
2311 case MUIV_List_Jump_Active:
2312 pos = data->entries_active;
2313 break;
2315 case MUIV_List_Jump_Bottom:
2316 pos = data->entries_num - 1;
2317 break;
2319 case MUIV_List_Jump_Down:
2320 pos = data->entries_first + data->entries_visible;
2321 break;
2323 case MUIV_List_Jump_Up:
2324 pos = data->entries_first - 1;
2325 break;
2328 if (pos >= data->entries_num)
2330 pos = data->entries_num - 1;
2332 if (pos < 0)
2333 pos = 0;
2335 if (pos < data->entries_first)
2337 set(obj, MUIA_List_First, pos);
2339 else if (pos >= data->entries_first + data->entries_visible)
2341 pos -= (data->entries_visible - 1);
2342 if (pos < 0)
2343 pos = 0;
2344 if (pos != data->entries_first)
2346 set(obj, MUIA_List_First, pos);
2350 return TRUE;
2353 /****** List.mui/MUIM_List_Sort **********************************************
2355 * NAME
2356 * MUIM_List_Sort (V4)
2358 * SYNOPSIS
2359 * DoMethod(obj, MUIM_List_Sort);
2361 * FUNCTION
2362 * Sort the list's entries according to the current comparison hook
2363 * (MUIA_List_CompareHook).
2365 * SEE ALSO
2366 * MUIA_List_CompareHook, MUIM_List_Compare.
2368 ******************************************************************************
2372 IPTR List__MUIM_Sort(struct IClass *cl, Object *obj,
2373 struct MUIP_List_Sort *msg)
2375 struct MUI_ListData *data = INST_DATA(cl, obj);
2377 int i, j, max;
2378 struct MUIP_List_Compare cmpmsg =
2379 { MUIM_List_Compare, NULL, NULL, 0, 0 };
2381 if (data->entries_num > 1)
2384 Simple sort algorithm. Feel free to improve it.
2386 for (i = 0; i < data->entries_num - 1; i++)
2388 max = i;
2389 for (j = i + 1; j < data->entries_num; j++)
2391 cmpmsg.entry1 = data->entries[max]->data;
2392 cmpmsg.entry2 = data->entries[j]->data;
2393 if ((LONG) DoMethodA(obj, (Msg) & cmpmsg) > 0)
2395 max = j;
2398 if (i != max)
2400 APTR tmp = data->entries[i];
2401 data->entries[i] = data->entries[max];
2402 data->entries[max] = tmp;
2407 data->update = 1;
2408 MUI_Redraw(obj, MADF_DRAWUPDATE);
2410 return 0;
2413 /****** List.mui/MUIM_List_Move **********************************************
2415 * NAME
2416 * MUIM_List_Move (V9)
2418 * SYNOPSIS
2419 * DoMethod(obj, MUIM_List_Move, LONG from, LONG to);
2421 * FUNCTION
2422 * Move a list entry to a new position.
2424 * INPUTS
2425 * from - the current index of the entry that should be moved, or one of
2426 * these special values:
2427 * MUIV_List_Move_Active: the active entry.
2428 * MUIV_List_Move_Top: the first entry.
2429 * MUIV_List_Move_Bottom: the last entry.
2430 * to - the index of the entry's new position, or one of
2431 * these special values:
2432 * MUIV_List_Move_Active: the active entry.
2433 * MUIV_List_Move_Top: the first entry.
2434 * MUIV_List_Move_Bottom: the last entry.
2436 ******************************************************************************
2440 IPTR List__MUIM_Move(struct IClass *cl, Object *obj,
2441 struct MUIP_List_Move *msg)
2443 struct MUI_ListData *data = INST_DATA(cl, obj);
2445 LONG from, to;
2446 int i;
2448 /* Normalise special 'from' values */
2449 switch (msg->from)
2451 case MUIV_List_Move_Top:
2452 from = 0;
2453 break;
2454 case MUIV_List_Move_Active:
2455 from = data->entries_active;
2456 break;
2457 case MUIV_List_Move_Bottom:
2458 from = data->entries_num - 1;
2459 break;
2460 default:
2461 from = msg->from;
2464 /* Normalise special 'to' values */
2465 switch (msg->to)
2467 case MUIV_List_Move_Top:
2468 to = 0;
2469 break;
2470 case MUIV_List_Move_Active:
2471 to = data->entries_active;
2472 break;
2473 case MUIV_List_Move_Bottom:
2474 to = data->entries_num - 1;
2475 break;
2476 case MUIV_List_Move_Next:
2477 to = from + 1;
2478 break;
2479 case MUIV_List_Move_Previous:
2480 to = from - 1;
2481 break;
2482 default:
2483 to = msg->to;
2486 /* Check that values are within valid bounds */
2487 if (from > data->entries_num - 1 || from < 0
2488 || to > data->entries_num - 1 || to < 0 || from == to)
2489 return (IPTR) FALSE;
2491 /* Shift all entries in the range between the 'from' and 'to' positions */
2492 if (from < to)
2494 struct ListEntry *backup = data->entries[from];
2495 for (i = from; i < to; i++)
2496 data->entries[i] = data->entries[i + 1];
2497 data->entries[to] = backup;
2499 else
2501 struct ListEntry *backup = data->entries[from];
2502 for (i = from; i > to; i--)
2503 data->entries[i] = data->entries[i - 1];
2504 data->entries[to] = backup;
2507 /* Update index of active entry */
2508 if (from == data->entries_active)
2509 data->entries_active = to;
2510 else if (data->entries_active > from && data->entries_active < to)
2511 data->entries_active--;
2512 else if (data->entries_active < from && data->entries_active >= to)
2513 data->entries_active++;
2515 /* Reflect list changes visually */
2516 data->update = 1;
2517 MUI_Redraw(obj, MADF_DRAWUPDATE);
2519 return TRUE;
2522 /**************************************************************************
2523 MUIM_List_NextSelected
2524 **************************************************************************/
2525 IPTR List__MUIM_NextSelected(struct IClass *cl, Object *obj,
2526 struct MUIP_List_NextSelected *msg)
2528 struct MUI_ListData *data = INST_DATA(cl, obj);
2529 LONG pos, i;
2530 BOOL found = FALSE;
2532 /* Get the first entry to check */
2533 pos = *msg->pos;
2534 if (pos == MUIV_List_NextSelected_Start)
2535 pos = 0;
2536 else
2537 pos++;
2539 /* Find the next selected entry */
2540 for (i = pos; i < data->entries_num && !found; i++)
2542 if (data->entries[i]->flags & ENTRY_SELECTED)
2544 pos = i;
2545 found = TRUE;
2549 /* Return index of selected entry, or indicate there are no more */
2550 if (!found)
2551 pos = MUIV_List_NextSelected_End;
2552 *msg->pos = pos;
2554 return TRUE;
2557 /**************************************************************************
2558 MUIM_List_TestPos
2559 **************************************************************************/
2560 IPTR List__MUIM_TestPos(struct IClass *cl, Object *obj,
2561 struct MUIP_List_TestPos *msg)
2563 struct MUI_ListData *data = INST_DATA(cl, obj);
2564 struct MUI_List_TestPos_Result *result = msg->res;
2565 LONG col = -1, row = -1;
2566 UWORD flags = 0, i;
2567 LONG mx = msg->x - _left(obj);
2568 LONG entries_visible;
2570 if (data->entries_visible <= data->entries_num)
2571 entries_visible = data->entries_visible;
2572 else
2573 entries_visible = data->entries_num;
2574 LONG ey = msg->y - data->entries_top_pixel;
2575 /* y coordinates transformed to the entries */
2577 /* Now check if it was clicked on a title or on entries */
2578 if (ey < 0)
2579 flags |= MUI_LPR_ABOVE;
2580 else if (ey >= entries_visible * data->entry_maxheight)
2581 flags |= MUI_LPR_BELOW;
2582 else
2584 /* Identify row */
2585 row = ey / data->entry_maxheight + data->entries_first;
2586 result->yoffset =
2587 ey % data->entry_maxheight - data->entry_maxheight / 2;
2590 if (mx < 0)
2591 flags |= MUI_LPR_LEFT;
2592 else if (mx >= _width(obj))
2593 flags |= MUI_LPR_RIGHT;
2594 else
2596 /* Identify column */
2597 if (data->entries_num > 0 && data->columns > 0)
2599 LONG width_sum = 0;
2600 col = data->columns - 1;
2601 for (i = 0; i < data->columns; i++)
2603 result->xoffset = mx - width_sum;
2604 width_sum +=
2605 data->ci[i].entries_width +
2606 data->ci[i].delta +
2607 (data->ci[i].bar ? BAR_WIDTH : 0);
2608 D(bug("[List/MUIM_TestPos] i %d "
2609 "width %d width_sum %d mx %d\n",
2610 i, data->ci[i].entries_width, width_sum, mx));
2611 if (mx < width_sum)
2613 col = i;
2614 D(bug("[List/MUIM_TestPos] Column hit %d\n", col));
2615 break;
2621 result->entry = row;
2622 result->column = col;
2623 result->flags = flags;
2625 return TRUE;
2628 /****i* List.mui/MUIM_DragQuery **********************************************
2630 * NAME
2631 * MUIM_DragQuery
2633 ******************************************************************************
2637 IPTR List__MUIM_DragQuery(struct IClass *cl, Object *obj,
2638 struct MUIP_DragQuery *msg)
2640 if (msg->obj == obj)
2641 return MUIV_DragQuery_Accept;
2642 else
2643 return MUIV_DragQuery_Refuse;
2647 /****i* List.mui/MUIM_DragFinish *********************************************
2649 * NAME
2650 * MUIM_DragFinish
2652 ******************************************************************************
2656 IPTR List__MUIM_DragFinish(struct IClass *cl, Object *obj,
2657 struct MUIP_DragFinish *msg)
2659 struct MUI_ListData *data = INST_DATA(cl, obj);
2661 data->drop_mark_y = -1;
2663 return DoSuperMethodA(cl, obj, (Msg) msg);
2666 /****i* List.mui/MUIM_DragReport *********************************************
2668 * NAME
2669 * MUIM_DragReport
2671 ******************************************************************************
2675 IPTR List__MUIM_DragReport(struct IClass *cl, Object *obj,
2676 struct MUIP_DragReport *msg)
2678 struct MUI_ListData *data = INST_DATA(cl, obj);
2679 struct MUI_List_TestPos_Result pos;
2680 struct RastPort *rp = _rp(obj);
2681 LONG n, y;
2682 UWORD old_pattern;
2684 /* Choose new drop mark position */
2686 DoMethod(obj, MUIM_List_TestPos, msg->x, msg->y, (IPTR) &pos);
2687 if (pos.entry != -1)
2689 n = pos.entry;
2690 if (pos.yoffset > 0)
2691 n++;
2693 else if ((pos.flags & MUI_LPR_ABOVE) != 0)
2694 n = data->entries_first;
2695 else
2697 n = MIN(data->entries_visible, data->entries_num)
2698 - data->entries_first;
2701 /* Clear old drop mark */
2703 if ((data->flags & LIST_SHOWDROPMARKS) != 0)
2705 y = data->entries_top_pixel + (n - data->entries_first)
2706 * data->entry_maxheight;
2707 if (y != data->drop_mark_y)
2709 DoMethod(obj, MUIM_DrawBackground, _mleft(obj), data->drop_mark_y,
2710 _mwidth(obj), 1, 0, 0, 0);
2712 /* Draw new drop mark and store its position */
2714 SetABPenDrMd(rp, _pens(obj)[MPEN_SHINE], _pens(obj)[MPEN_SHADOW],
2715 JAM2);
2716 old_pattern = rp->LinePtrn;
2717 SetDrPt(rp, 0xF0F0);
2718 Move(rp, _mleft(obj), y);
2719 Draw(rp, _mright(obj), y);
2720 SetDrPt(rp, old_pattern);
2721 data->drop_mark_y = y;
2725 return TRUE;
2729 /****i* List.mui/MUIM_DragDrop ***********************************************
2731 * NAME
2732 * MUIM_DragDrop
2734 ******************************************************************************
2738 IPTR List__MUIM_DragDrop(struct IClass *cl, Object *obj,
2739 struct MUIP_DragDrop *msg)
2741 struct MUI_ListData *data = INST_DATA(cl, obj);
2742 struct MUI_List_TestPos_Result pos;
2743 LONG n;
2745 /* Find drop position */
2747 DoMethod(obj, MUIM_List_TestPos, msg->x, msg->y, (IPTR) &pos);
2748 if (pos.entry != -1)
2750 /* Change drop position when coords move past centre of entry, not
2751 * entry boundary */
2753 n = pos.entry;
2754 if (pos.yoffset > 0)
2755 n++;
2757 /* Ensure that dropped entry will be positioned between the two
2758 * entries that are above and below the drop mark, rather than
2759 * strictly at the numeric index shown */
2761 if (n > data->entries_active)
2762 n--;
2764 else if ((pos.flags & MUI_LPR_ABOVE) != 0)
2765 n = MUIV_List_Move_Top;
2766 else
2767 n = MUIV_List_Move_Bottom;
2769 DoMethod(msg->obj, MUIM_List_Move, MUIV_List_Move_Active, n);
2771 return TRUE;
2775 /****i* List.mui/MUIM_CreateDragImage ****************************************
2777 * NAME
2778 * MUIM_CreateDragImage
2780 ******************************************************************************
2784 static IPTR List__MUIM_CreateDragImage(struct IClass *cl, Object *obj,
2785 struct MUIP_CreateDragImage *msg)
2787 struct MUI_ListData *data = INST_DATA(cl, obj);
2788 BOOL success = TRUE;
2789 struct MUI_List_TestPos_Result pos;
2790 WORD width, height, left, top;
2791 struct MUI_DragImage *img = NULL;
2792 const struct ZuneFrameGfx *zframe;
2793 LONG depth;
2795 /* Get info on dragged entry */
2796 DoMethod(obj, MUIM_List_TestPos, _left(obj) - msg->touchx,
2797 _top(obj) - msg->touchy, (IPTR) &pos);
2798 if (pos.entry == -1)
2799 success = FALSE;
2801 if (success)
2803 /* Get boundaries of entry */
2804 width = _mwidth(obj);
2805 height = data->entry_maxheight;
2806 left = _mleft(obj);
2807 top = _top(obj) - msg->touchy
2808 - (pos.yoffset + data->entry_maxheight / 2);
2810 /* Allocate drag image structure */
2811 img = (struct MUI_DragImage *)
2812 AllocVec(sizeof(struct MUI_DragImage), MEMF_CLEAR);
2813 if (img == NULL)
2814 success = FALSE;
2817 if (success)
2819 /* Get drag frame */
2820 zframe = zune_zframe_get(obj,
2821 &muiGlobalInfo(obj)->mgi_Prefs->frames[MUIV_Frame_Drag]);
2823 /* Allocate drag image buffer */
2824 img->width = width + zframe->ileft + zframe->iright;
2825 img->height = height + zframe->itop + zframe->ibottom;
2826 depth = GetBitMapAttr(_screen(obj)->RastPort.BitMap, BMA_DEPTH);
2827 img->bm = AllocBitMap(img->width, img->height, depth, BMF_MINPLANES,
2828 _screen(obj)->RastPort.BitMap);
2830 if (img->bm != NULL)
2832 /* Render entry */
2833 struct RastPort temprp;
2834 InitRastPort(&temprp);
2835 temprp.BitMap = img->bm;
2836 ClipBlit(_rp(obj), left, top, &temprp,
2837 zframe->ileft, zframe->itop, width, height,
2838 0xc0);
2840 /* Render frame */
2841 struct RastPort *rp_save = muiRenderInfo(obj)->mri_RastPort;
2842 muiRenderInfo(obj)->mri_RastPort = &temprp;
2843 zframe->draw(zframe->customframe, muiRenderInfo(obj), 0, 0,
2844 img->width, img->height, 0, 0, img->width, img->height);
2845 muiRenderInfo(obj)->mri_RastPort = rp_save;
2848 /* Ensure drag point matches where user clicked */
2849 img->touchx = msg->touchx - zframe->ileft + _addleft(obj);
2850 img->touchy = -(pos.yoffset + data->entry_maxheight / 2)
2851 - zframe->itop;
2852 img->flags = 0;
2855 return (IPTR) img;
2858 /**************************************************************************
2859 Dispatcher
2860 **************************************************************************/
2861 BOOPSI_DISPATCHER(IPTR, List_Dispatcher, cl, obj, msg)
2863 switch (msg->MethodID)
2865 case OM_NEW:
2866 return List__OM_NEW(cl, obj, (struct opSet *)msg);
2867 case OM_DISPOSE:
2868 return List__OM_DISPOSE(cl, obj, msg);
2869 case OM_SET:
2870 return List__OM_SET(cl, obj, (struct opSet *)msg);
2871 case OM_GET:
2872 return List__OM_GET(cl, obj, (struct opGet *)msg);
2874 case MUIM_Setup:
2875 return List__MUIM_Setup(cl, obj, (struct MUIP_Setup *)msg);
2876 case MUIM_Cleanup:
2877 return List__MUIM_Cleanup(cl, obj, (struct MUIP_Cleanup *)msg);
2878 case MUIM_AskMinMax:
2879 return List__MUIM_AskMinMax(cl, obj, (struct MUIP_AskMinMax *)msg);
2880 case MUIM_Show:
2881 return List__MUIM_Show(cl, obj, (struct MUIP_Show *)msg);
2882 case MUIM_Hide:
2883 return List__MUIM_Hide(cl, obj, (struct MUIP_Hide *)msg);
2884 case MUIM_Draw:
2885 return List__MUIM_Draw(cl, obj, (struct MUIP_Draw *)msg);
2886 case MUIM_Layout:
2887 return List__MUIM_Layout(cl, obj, (struct MUIP_Layout *)msg);
2888 case MUIM_List_Clear:
2889 return List__MUIM_Clear(cl, obj, (struct MUIP_List_Clear *)msg);
2890 case MUIM_List_Sort:
2891 return List__MUIM_Sort(cl, obj, (struct MUIP_List_Sort *)msg);
2892 case MUIM_List_Exchange:
2893 return List__MUIM_Exchange(cl, obj,
2894 (struct MUIP_List_Exchange *)msg);
2895 case MUIM_List_Insert:
2896 return List__MUIM_Insert(cl, obj, (APTR) msg);
2897 case MUIM_List_InsertSingle:
2898 return List__MUIM_InsertSingle(cl, obj, (APTR) msg);
2899 case MUIM_List_GetEntry:
2900 return List__MUIM_GetEntry(cl, obj, (APTR) msg);
2901 case MUIM_List_Redraw:
2902 return List__MUIM_Redraw(cl, obj, (APTR) msg);
2903 case MUIM_List_Remove:
2904 return List__MUIM_Remove(cl, obj, (APTR) msg);
2905 case MUIM_List_Select:
2906 return List__MUIM_Select(cl, obj, (APTR) msg);
2907 case MUIM_List_Construct:
2908 return List__MUIM_Construct(cl, obj, (APTR) msg);
2909 case MUIM_List_Destruct:
2910 return List__MUIM_Destruct(cl, obj, (APTR) msg);
2911 case MUIM_List_Compare:
2912 return List__MUIM_Compare(cl, obj, (APTR) msg);
2913 case MUIM_List_Display:
2914 return List__MUIM_Display(cl, obj, (APTR) msg);
2915 case MUIM_List_SelectChange:
2916 return List__MUIM_SelectChange(cl, obj, (APTR) msg);
2917 case MUIM_List_CreateImage:
2918 return List__MUIM_CreateImage(cl, obj, (APTR) msg);
2919 case MUIM_List_DeleteImage:
2920 return List__MUIM_DeleteImage(cl, obj, (APTR) msg);
2921 case MUIM_List_Jump:
2922 return List__MUIM_Jump(cl, obj, (APTR) msg);
2923 case MUIM_List_Move:
2924 return List__MUIM_Move(cl, obj, (struct MUIP_List_Move *)msg);
2925 case MUIM_List_NextSelected:
2926 return List__MUIM_NextSelected(cl, obj,
2927 (struct MUIP_List_NextSelected *)msg);
2928 case MUIM_List_TestPos:
2929 return List__MUIM_TestPos(cl, obj, (APTR) msg);
2930 case MUIM_DragQuery:
2931 return List__MUIM_DragQuery(cl, obj, (APTR) msg);
2932 case MUIM_DragFinish:
2933 return List__MUIM_DragFinish(cl, obj, (APTR) msg);
2934 case MUIM_DragReport:
2935 return List__MUIM_DragReport(cl, obj, (APTR) msg);
2936 case MUIM_DragDrop:
2937 return List__MUIM_DragDrop(cl, obj, (APTR) msg);
2938 case MUIM_CreateDragImage:
2939 return List__MUIM_CreateDragImage(cl, obj, (APTR) msg);
2942 return DoSuperMethodA(cl, obj, msg);
2944 BOOPSI_DISPATCHER_END
2947 * Class descriptor.
2949 const struct __MUIBuiltinClass _MUI_List_desc =
2951 MUIC_List,
2952 MUIC_Area,
2953 sizeof(struct MUI_ListData),
2954 (void *) List_Dispatcher