correction to debug output
[AROS.git] / workbench / libs / muimaster / classes / list.c
blob3e59dc50f7d79b2373ce40cda8be16d1a2c37f70
1 /*
2 Copyright © 2002-2014, 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 /* List must be already setup in Setup of your subclass */
2237 if (!(_flags(obj) & MADF_SETUP))
2238 return 0;
2239 li = AllocPooled(data->pool, sizeof(struct ListImage));
2240 if (!li)
2241 return 0;
2242 li->obj = msg->obj;
2244 AddTail((struct List *)&data->images, (struct Node *)li);
2245 DoMethod(li->obj, MUIM_ConnectParent, (IPTR) obj);
2246 DoSetupMethod(li->obj, muiRenderInfo(obj));
2249 return (IPTR) li;
2252 /**************************************************************************
2253 MUIM_List_DeleteImage
2254 **************************************************************************/
2255 IPTR List__MUIM_DeleteImage(struct IClass *cl, Object *obj,
2256 struct MUIP_List_DeleteImage *msg)
2258 struct MUI_ListData *data = INST_DATA(cl, obj);
2259 struct ListImage *li = (struct ListImage *)msg->listimg;
2261 if (li)
2263 DoMethod(li->obj, MUIM_Cleanup);
2264 DoMethod(li->obj, MUIM_DisconnectParent);
2265 Remove((struct Node *)li);
2266 FreePooled(data->pool, li, sizeof(struct ListImage));
2269 return 0;
2272 /****** List.mui/MUIM_List_Jump **********************************************
2274 * NAME
2275 * MUIM_List_Jump (V4)
2277 * SYNOPSIS
2278 * DoMethod(obj, MUIM_List_Jump, LONG pos);
2280 * FUNCTION
2281 * Scrolls the list so that a particular entry is visible.
2283 * INPUTS
2284 * pos - index of entry that should become visible, or one of these
2285 * special values:
2286 * MUIV_List_Jump_Active: show the active entry.
2287 * MUIV_List_Jump_Top: show the first entry.
2288 * MUIV_List_Jump_Bottom: show the last entry.
2289 * MUIV_List_Jump_Up: show the previous hidden entry.
2290 * MUIV_List_Jump_Down: show the next hidden entry.
2292 ******************************************************************************
2296 IPTR List__MUIM_Jump(struct IClass *cl, Object *obj,
2297 struct MUIP_List_Jump *msg)
2299 struct MUI_ListData *data = INST_DATA(cl, obj);
2300 LONG pos = msg->pos;
2302 switch (pos)
2304 case MUIV_List_Jump_Top:
2305 pos = 0;
2306 break;
2308 case MUIV_List_Jump_Active:
2309 pos = data->entries_active;
2310 break;
2312 case MUIV_List_Jump_Bottom:
2313 pos = data->entries_num - 1;
2314 break;
2316 case MUIV_List_Jump_Down:
2317 pos = data->entries_first + data->entries_visible;
2318 break;
2320 case MUIV_List_Jump_Up:
2321 pos = data->entries_first - 1;
2322 break;
2325 if (pos >= data->entries_num)
2327 pos = data->entries_num - 1;
2329 if (pos < 0)
2330 pos = 0;
2332 if (pos < data->entries_first)
2334 set(obj, MUIA_List_First, pos);
2336 else if (pos >= data->entries_first + data->entries_visible)
2338 pos -= (data->entries_visible - 1);
2339 if (pos < 0)
2340 pos = 0;
2341 if (pos != data->entries_first)
2343 set(obj, MUIA_List_First, pos);
2347 return TRUE;
2350 /****** List.mui/MUIM_List_Sort **********************************************
2352 * NAME
2353 * MUIM_List_Sort (V4)
2355 * SYNOPSIS
2356 * DoMethod(obj, MUIM_List_Sort);
2358 * FUNCTION
2359 * Sort the list's entries according to the current comparison hook
2360 * (MUIA_List_CompareHook).
2362 * SEE ALSO
2363 * MUIA_List_CompareHook, MUIM_List_Compare.
2365 ******************************************************************************
2369 IPTR List__MUIM_Sort(struct IClass *cl, Object *obj,
2370 struct MUIP_List_Sort *msg)
2372 struct MUI_ListData *data = INST_DATA(cl, obj);
2374 int i, j, max;
2375 struct MUIP_List_Compare cmpmsg =
2376 { MUIM_List_Compare, NULL, NULL, 0, 0 };
2378 if (data->entries_num > 1)
2381 Simple sort algorithm. Feel free to improve it.
2383 for (i = 0; i < data->entries_num - 1; i++)
2385 max = i;
2386 for (j = i + 1; j < data->entries_num; j++)
2388 cmpmsg.entry1 = data->entries[max]->data;
2389 cmpmsg.entry2 = data->entries[j]->data;
2390 if ((LONG) DoMethodA(obj, (Msg) & cmpmsg) > 0)
2392 max = j;
2395 if (i != max)
2397 APTR tmp = data->entries[i];
2398 data->entries[i] = data->entries[max];
2399 data->entries[max] = tmp;
2404 data->update = 1;
2405 MUI_Redraw(obj, MADF_DRAWUPDATE);
2407 return 0;
2410 /****** List.mui/MUIM_List_Move **********************************************
2412 * NAME
2413 * MUIM_List_Move (V9)
2415 * SYNOPSIS
2416 * DoMethod(obj, MUIM_List_Move, LONG from, LONG to);
2418 * FUNCTION
2419 * Move a list entry to a new position.
2421 * INPUTS
2422 * from - the current index of the entry that should be moved, or one of
2423 * these special values:
2424 * MUIV_List_Move_Active: the active entry.
2425 * MUIV_List_Move_Top: the first entry.
2426 * MUIV_List_Move_Bottom: the last entry.
2427 * to - the index of the entry's new position, or one of
2428 * these special values:
2429 * MUIV_List_Move_Active: the active entry.
2430 * MUIV_List_Move_Top: the first entry.
2431 * MUIV_List_Move_Bottom: the last entry.
2433 ******************************************************************************
2437 IPTR List__MUIM_Move(struct IClass *cl, Object *obj,
2438 struct MUIP_List_Move *msg)
2440 struct MUI_ListData *data = INST_DATA(cl, obj);
2442 LONG from, to;
2443 int i;
2445 /* Normalise special 'from' values */
2446 switch (msg->from)
2448 case MUIV_List_Move_Top:
2449 from = 0;
2450 break;
2451 case MUIV_List_Move_Active:
2452 from = data->entries_active;
2453 break;
2454 case MUIV_List_Move_Bottom:
2455 from = data->entries_num - 1;
2456 break;
2457 default:
2458 from = msg->from;
2461 /* Normalise special 'to' values */
2462 switch (msg->to)
2464 case MUIV_List_Move_Top:
2465 to = 0;
2466 break;
2467 case MUIV_List_Move_Active:
2468 to = data->entries_active;
2469 break;
2470 case MUIV_List_Move_Bottom:
2471 to = data->entries_num - 1;
2472 break;
2473 case MUIV_List_Move_Next:
2474 to = from + 1;
2475 break;
2476 case MUIV_List_Move_Previous:
2477 to = from - 1;
2478 break;
2479 default:
2480 to = msg->to;
2483 /* Check that values are within valid bounds */
2484 if (from > data->entries_num - 1 || from < 0
2485 || to > data->entries_num - 1 || to < 0 || from == to)
2486 return (IPTR) FALSE;
2488 /* Shift all entries in the range between the 'from' and 'to' positions */
2489 if (from < to)
2491 struct ListEntry *backup = data->entries[from];
2492 for (i = from; i < to; i++)
2493 data->entries[i] = data->entries[i + 1];
2494 data->entries[to] = backup;
2496 else
2498 struct ListEntry *backup = data->entries[from];
2499 for (i = from; i > to; i--)
2500 data->entries[i] = data->entries[i - 1];
2501 data->entries[to] = backup;
2504 /* Update index of active entry */
2505 if (from == data->entries_active)
2506 data->entries_active = to;
2507 else if (data->entries_active > from && data->entries_active < to)
2508 data->entries_active--;
2509 else if (data->entries_active < from && data->entries_active >= to)
2510 data->entries_active++;
2512 /* Reflect list changes visually */
2513 data->update = 1;
2514 MUI_Redraw(obj, MADF_DRAWUPDATE);
2516 return TRUE;
2519 /**************************************************************************
2520 MUIM_List_NextSelected
2521 **************************************************************************/
2522 IPTR List__MUIM_NextSelected(struct IClass *cl, Object *obj,
2523 struct MUIP_List_NextSelected *msg)
2525 struct MUI_ListData *data = INST_DATA(cl, obj);
2526 LONG pos, i;
2527 BOOL found = FALSE;
2529 /* Get the first entry to check */
2530 pos = *msg->pos;
2531 if (pos == MUIV_List_NextSelected_Start)
2532 pos = 0;
2533 else
2534 pos++;
2536 /* Find the next selected entry */
2537 for (i = pos; i < data->entries_num && !found; i++)
2539 if (data->entries[i]->flags & ENTRY_SELECTED)
2541 pos = i;
2542 found = TRUE;
2546 /* Return index of selected entry, or indicate there are no more */
2547 if (!found)
2548 pos = MUIV_List_NextSelected_End;
2549 *msg->pos = pos;
2551 return TRUE;
2554 /**************************************************************************
2555 MUIM_List_TestPos
2556 **************************************************************************/
2557 IPTR List__MUIM_TestPos(struct IClass *cl, Object *obj,
2558 struct MUIP_List_TestPos *msg)
2560 struct MUI_ListData *data = INST_DATA(cl, obj);
2561 struct MUI_List_TestPos_Result *result = msg->res;
2562 LONG col = -1, row = -1;
2563 UWORD flags = 0;
2564 LONG mx = msg->x - _left(obj);
2565 LONG entries_visible;
2567 if (data->entries_visible <= data->entries_num)
2568 entries_visible = data->entries_visible;
2569 else
2570 entries_visible = data->entries_num;
2571 LONG ey = msg->y - data->entries_top_pixel;
2572 /* y coordinates transformed to the entries */
2574 /* Now check if it was clicked on a title or on entries */
2575 if (ey < 0)
2576 flags |= MUI_LPR_ABOVE;
2577 else if (ey >= entries_visible * data->entry_maxheight)
2578 flags |= MUI_LPR_BELOW;
2579 else
2581 /* Identify row */
2582 row = ey / data->entry_maxheight + data->entries_first;
2583 result->yoffset =
2584 ey % data->entry_maxheight - data->entry_maxheight / 2;
2587 if (mx < 0)
2588 flags |= MUI_LPR_LEFT;
2589 else if (mx >= _width(obj))
2590 flags |= MUI_LPR_RIGHT;
2591 else
2593 /* Identify column */
2594 if (data->entries_num > 0 && data->columns > 0)
2596 LONG width_sum = 0;
2597 for (col = 0; col < data->columns; col++)
2599 result->xoffset = mx - width_sum;
2600 width_sum +=
2601 data->ci[col].entries_width +
2602 data->ci[col].delta +
2603 (data->ci[col].bar ? BAR_WIDTH : 0);
2604 D(bug("[List/MUIM_TestPos] col %d "
2605 "width %d width_sum %d mx %d\n",
2606 col, data->ci[col].entries_width, width_sum, mx));
2607 if (mx < width_sum)
2609 D(bug("[List/MUIM_TestPos] Column hit %d\n", col));
2610 break;
2616 result->entry = row;
2617 result->column = col;
2618 result->flags = flags;
2620 return TRUE;
2623 /****i* List.mui/MUIM_DragQuery **********************************************
2625 * NAME
2626 * MUIM_DragQuery
2628 ******************************************************************************
2632 IPTR List__MUIM_DragQuery(struct IClass *cl, Object *obj,
2633 struct MUIP_DragQuery *msg)
2635 if (msg->obj == obj)
2636 return MUIV_DragQuery_Accept;
2637 else
2638 return MUIV_DragQuery_Refuse;
2642 /****i* List.mui/MUIM_DragFinish *********************************************
2644 * NAME
2645 * MUIM_DragFinish
2647 ******************************************************************************
2651 IPTR List__MUIM_DragFinish(struct IClass *cl, Object *obj,
2652 struct MUIP_DragFinish *msg)
2654 struct MUI_ListData *data = INST_DATA(cl, obj);
2656 data->drop_mark_y = -1;
2658 return DoSuperMethodA(cl, obj, (Msg) msg);
2662 /****i* List.mui/MUIM_DragReport *********************************************
2664 * NAME
2665 * MUIM_DragReport
2667 ******************************************************************************
2671 IPTR List__MUIM_DragReport(struct IClass *cl, Object *obj,
2672 struct MUIP_DragReport *msg)
2674 struct MUI_ListData *data = INST_DATA(cl, obj);
2675 struct MUI_List_TestPos_Result pos;
2676 struct RastPort *rp = _rp(obj);
2677 LONG n, y;
2678 UWORD old_pattern;
2680 /* Choose new drop mark position */
2682 DoMethod(obj, MUIM_List_TestPos, msg->x, msg->y, (IPTR) &pos);
2683 if (pos.entry != -1)
2685 n = pos.entry;
2686 if (pos.yoffset > 0)
2687 n++;
2689 else if ((pos.flags & MUI_LPR_ABOVE) != 0)
2690 n = data->entries_first;
2691 else
2693 n = MIN(data->entries_visible, data->entries_num)
2694 - data->entries_first;
2697 /* Clear old drop mark */
2699 if ((data->flags & LIST_SHOWDROPMARKS) != 0)
2701 y = data->entries_top_pixel + (n - data->entries_first)
2702 * data->entry_maxheight;
2703 if (y != data->drop_mark_y)
2705 DoMethod(obj, MUIM_DrawBackground, _mleft(obj), data->drop_mark_y,
2706 _mwidth(obj), 1, 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;
2721 return TRUE;
2725 /****i* List.mui/MUIM_DragDrop ***********************************************
2727 * NAME
2728 * MUIM_DragDrop
2730 ******************************************************************************
2734 IPTR List__MUIM_DragDrop(struct IClass *cl, Object *obj,
2735 struct MUIP_DragDrop *msg)
2737 struct MUI_ListData *data = INST_DATA(cl, obj);
2738 struct MUI_List_TestPos_Result pos;
2739 LONG n;
2741 /* Find drop position */
2743 DoMethod(obj, MUIM_List_TestPos, msg->x, msg->y, (IPTR) &pos);
2744 if (pos.entry != -1)
2746 /* Change drop position when coords move past centre of entry, not
2747 * entry boundary */
2749 n = pos.entry;
2750 if (pos.yoffset > 0)
2751 n++;
2753 /* Ensure that dropped entry will be positioned between the two
2754 * entries that are above and below the drop mark, rather than
2755 * strictly at the numeric index shown */
2757 if (n > data->entries_active)
2758 n--;
2760 else if ((pos.flags & MUI_LPR_ABOVE) != 0)
2761 n = MUIV_List_Move_Top;
2762 else
2763 n = MUIV_List_Move_Bottom;
2765 DoMethod(msg->obj, MUIM_List_Move, MUIV_List_Move_Active, n);
2767 return TRUE;
2771 /****i* List.mui/MUIM_CreateDragImage ****************************************
2773 * NAME
2774 * MUIM_CreateDragImage
2776 ******************************************************************************
2780 static IPTR List__MUIM_CreateDragImage(struct IClass *cl, Object *obj,
2781 struct MUIP_CreateDragImage *msg)
2783 struct MUI_ListData *data = INST_DATA(cl, obj);
2784 BOOL success = TRUE;
2785 struct MUI_List_TestPos_Result pos;
2786 WORD width, height, left, top;
2787 struct MUI_DragImage *img = NULL;
2788 const struct ZuneFrameGfx *zframe;
2789 LONG depth;
2791 /* Get info on dragged entry */
2792 DoMethod(obj, MUIM_List_TestPos, _left(obj) - msg->touchx,
2793 _top(obj) - msg->touchy, (IPTR) &pos);
2794 if (pos.entry == -1)
2795 success = FALSE;
2797 if (success)
2799 /* Get boundaries of entry */
2800 width = _mwidth(obj);
2801 height = data->entry_maxheight;
2802 left = _mleft(obj);
2803 top = _top(obj) - msg->touchy
2804 - (pos.yoffset + data->entry_maxheight / 2);
2806 /* Allocate drag image structure */
2807 img = (struct MUI_DragImage *)
2808 AllocVec(sizeof(struct MUI_DragImage), MEMF_CLEAR);
2809 if (img == NULL)
2810 success = FALSE;
2813 if (success)
2815 /* Get drag frame */
2816 zframe = zune_zframe_get(obj,
2817 &muiGlobalInfo(obj)->mgi_Prefs->frames[MUIV_Frame_Drag]);
2819 /* Allocate drag image buffer */
2820 img->width = width + zframe->ileft + zframe->iright;
2821 img->height = height + zframe->itop + zframe->ibottom;
2822 depth = GetBitMapAttr(_screen(obj)->RastPort.BitMap, BMA_DEPTH);
2823 img->bm = AllocBitMap(img->width, img->height, depth, BMF_MINPLANES,
2824 _screen(obj)->RastPort.BitMap);
2826 if (img->bm != NULL)
2828 /* Render entry */
2829 struct RastPort temprp;
2830 InitRastPort(&temprp);
2831 temprp.BitMap = img->bm;
2832 ClipBlit(_rp(obj), left, top, &temprp,
2833 zframe->ileft, zframe->itop, width, height,
2834 0xc0);
2836 /* Render frame */
2837 struct RastPort *rp_save = muiRenderInfo(obj)->mri_RastPort;
2838 muiRenderInfo(obj)->mri_RastPort = &temprp;
2839 zframe->draw(zframe->customframe, muiRenderInfo(obj), 0, 0,
2840 img->width, img->height, 0, 0, img->width, img->height);
2841 muiRenderInfo(obj)->mri_RastPort = rp_save;
2844 /* Ensure drag point matches where user clicked */
2845 img->touchx = msg->touchx - zframe->ileft + _addleft(obj);
2846 img->touchy = -(pos.yoffset + data->entry_maxheight / 2)
2847 - zframe->itop;
2848 img->flags = 0;
2851 return (IPTR) img;
2854 /**************************************************************************
2855 Dispatcher
2856 **************************************************************************/
2857 BOOPSI_DISPATCHER(IPTR, List_Dispatcher, cl, obj, msg)
2859 switch (msg->MethodID)
2861 case OM_NEW:
2862 return List__OM_NEW(cl, obj, (struct opSet *)msg);
2863 case OM_DISPOSE:
2864 return List__OM_DISPOSE(cl, obj, msg);
2865 case OM_SET:
2866 return List__OM_SET(cl, obj, (struct opSet *)msg);
2867 case OM_GET:
2868 return List__OM_GET(cl, obj, (struct opGet *)msg);
2870 case MUIM_Setup:
2871 return List__MUIM_Setup(cl, obj, (struct MUIP_Setup *)msg);
2872 case MUIM_Cleanup:
2873 return List__MUIM_Cleanup(cl, obj, (struct MUIP_Cleanup *)msg);
2874 case MUIM_AskMinMax:
2875 return List__MUIM_AskMinMax(cl, obj, (struct MUIP_AskMinMax *)msg);
2876 case MUIM_Show:
2877 return List__MUIM_Show(cl, obj, (struct MUIP_Show *)msg);
2878 case MUIM_Hide:
2879 return List__MUIM_Hide(cl, obj, (struct MUIP_Hide *)msg);
2880 case MUIM_Draw:
2881 return List__MUIM_Draw(cl, obj, (struct MUIP_Draw *)msg);
2882 case MUIM_Layout:
2883 return List__MUIM_Layout(cl, obj, (struct MUIP_Layout *)msg);
2884 case MUIM_List_Clear:
2885 return List__MUIM_Clear(cl, obj, (struct MUIP_List_Clear *)msg);
2886 case MUIM_List_Sort:
2887 return List__MUIM_Sort(cl, obj, (struct MUIP_List_Sort *)msg);
2888 case MUIM_List_Exchange:
2889 return List__MUIM_Exchange(cl, obj,
2890 (struct MUIP_List_Exchange *)msg);
2891 case MUIM_List_Insert:
2892 return List__MUIM_Insert(cl, obj, (APTR) msg);
2893 case MUIM_List_InsertSingle:
2894 return List__MUIM_InsertSingle(cl, obj, (APTR) msg);
2895 case MUIM_List_GetEntry:
2896 return List__MUIM_GetEntry(cl, obj, (APTR) msg);
2897 case MUIM_List_Redraw:
2898 return List__MUIM_Redraw(cl, obj, (APTR) msg);
2899 case MUIM_List_Remove:
2900 return List__MUIM_Remove(cl, obj, (APTR) msg);
2901 case MUIM_List_Select:
2902 return List__MUIM_Select(cl, obj, (APTR) msg);
2903 case MUIM_List_Construct:
2904 return List__MUIM_Construct(cl, obj, (APTR) msg);
2905 case MUIM_List_Destruct:
2906 return List__MUIM_Destruct(cl, obj, (APTR) msg);
2907 case MUIM_List_Compare:
2908 return List__MUIM_Compare(cl, obj, (APTR) msg);
2909 case MUIM_List_Display:
2910 return List__MUIM_Display(cl, obj, (APTR) msg);
2911 case MUIM_List_SelectChange:
2912 return List__MUIM_SelectChange(cl, obj, (APTR) msg);
2913 case MUIM_List_CreateImage:
2914 return List__MUIM_CreateImage(cl, obj, (APTR) msg);
2915 case MUIM_List_DeleteImage:
2916 return List__MUIM_DeleteImage(cl, obj, (APTR) msg);
2917 case MUIM_List_Jump:
2918 return List__MUIM_Jump(cl, obj, (APTR) msg);
2919 case MUIM_List_Move:
2920 return List__MUIM_Move(cl, obj, (struct MUIP_List_Move *)msg);
2921 case MUIM_List_NextSelected:
2922 return List__MUIM_NextSelected(cl, obj,
2923 (struct MUIP_List_NextSelected *)msg);
2924 case MUIM_List_TestPos:
2925 return List__MUIM_TestPos(cl, obj, (APTR) msg);
2926 case MUIM_DragQuery:
2927 return List__MUIM_DragQuery(cl, obj, (APTR) msg);
2928 case MUIM_DragFinish:
2929 return List__MUIM_DragFinish(cl, obj, (APTR) msg);
2930 case MUIM_DragReport:
2931 return List__MUIM_DragReport(cl, obj, (APTR) msg);
2932 case MUIM_DragDrop:
2933 return List__MUIM_DragDrop(cl, obj, (APTR) msg);
2934 case MUIM_CreateDragImage:
2935 return List__MUIM_CreateDragImage(cl, obj, (APTR) msg);
2938 return DoSuperMethodA(cl, obj, msg);
2940 BOOPSI_DISPATCHER_END
2943 * Class descriptor.
2945 const struct __MUIBuiltinClass _MUI_List_desc =
2947 MUIC_List,
2948 MUIC_Area,
2949 sizeof(struct MUI_ListData),
2950 (void *) List_Dispatcher