MUIM_List_TestPos now treats the last column as extending to the
[AROS.git] / workbench / libs / muimaster / classes / list.c
blob76ea0e05505194b0cec9e5dd9638156d6fb521f3
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)
170 /*****************************************************************************************
172 NAME
173 MUIA_List_CompareHook
175 SYNOPSIS
176 [IS.], struct Hook *
178 LOCATION
179 List.mui
181 FUNCTION
182 The provided hook indicates the sort ordering of two list entries.
183 The hook receives list-entry data pointers as its second and third
184 arguments. The hook should return a negative value if the first entry
185 should be placed before the second entry, a positive value if the
186 first entry should be placed after the second entry, and zero if the
187 entries are equal.
189 In addition to being used internally for sorting operations, this hook
190 will be called when MUIM_List_Compare is externally invoked.
192 If this attribute is not specified or is set to NULL, all list entries
193 must be strings.
195 NOTES
196 MUI V4
198 EXAMPLE
200 BUGS
202 SEE ALSO
204 INTERNALS
206 *****************************************************************************************/
208 /*****************************************************************************************
210 NAME
211 MUIA_List_MultiTestHook
213 SYNOPSIS
214 [IS.], struct Hook *
216 LOCATION
217 List.mui
219 FUNCTION
220 The provided hook indicates whether a particular list entry
221 may be multiselected. The hook receives the list-entry data pointer as
222 its third argument, and returns a Boolean value. If this attribute is
223 not specified or is set to NULL, all list entries are considered
224 multi-selectable.
226 Whenever an entry is about to be selected, this hook is called if
227 there are other entries already selected. If the hook returns TRUE,
228 the entry may be multi-selected; if the hook returns FALSE, the entry
229 remains unselected.
231 Additionally, if a non-multi-selectable entry has been selected (as
232 the only selected entry in the list), any attempt to select an
233 additional entry will fail.
235 NOTES
236 MUI V4
238 EXAMPLE
240 BUGS
242 SEE ALSO
244 INTERNALS
246 *****************************************************************************************/
249 /**************************************************************************
250 Allocate a single list entry, does not initialize it (except the pointer)
251 **************************************************************************/
252 static struct ListEntry *AllocListEntry(struct MUI_ListData *data)
254 ULONG *mem;
255 struct ListEntry *le;
256 int size = sizeof(struct ListEntry) + sizeof(LONG) * data->columns + 4;
257 /* sizeinfo */
258 LONG j;
260 mem = AllocPooled(data->pool, size);
261 if (!mem)
262 return NULL;
263 D(bug("List AllocListEntry %p, %ld bytes\n", mem, size));
265 mem[0] = size; /* Save the size */
266 le = (struct ListEntry *)(mem + 1);
267 le->widths = (LONG *) (le + 1);
269 /* Initialize fields */
270 le->height = 0;
271 le->width = 0;
272 le->flags = 0;
273 for (j = 0; j < data->columns; j++)
274 le->widths[j] = 0;
276 return le;
279 /**************************************************************************
280 Deallocate a single list entry, does not deinitialize it
281 **************************************************************************/
282 static void FreeListEntry(struct MUI_ListData *data,
283 struct ListEntry *entry)
285 ULONG *mem = ((ULONG *) entry) - 1;
286 D(bug("FreeListEntry %p size=%ld\n", mem, mem[0]));
287 FreePooled(data->pool, mem, mem[0]);
290 /**************************************************************************
291 Ensures that there can be at least the given amount of entries within
292 the list. Returns 0 if not. It also allocates the space for the title.
293 It can be accessed with data->entries[ENTRY_TITLE]
294 **************************************************************************/
295 static int SetListSize(struct MUI_ListData *data, LONG size)
297 struct ListEntry **new_entries;
298 int new_entries_allocated;
300 if (size + 1 <= data->entries_allocated)
301 return 1;
303 new_entries_allocated = data->entries_allocated * 2 + 4;
304 if (new_entries_allocated < size + 1)
305 new_entries_allocated = size + 1 + 10; /* 10 is just random */
307 D(bug("List %p : SetListSize allocating %ld bytes\n", data,
308 new_entries_allocated * sizeof(struct ListEntry *)));
309 new_entries =
310 AllocVec(new_entries_allocated * sizeof(struct ListEntry *), 0);
311 if (NULL == new_entries)
312 return 0;
313 if (data->entries)
315 CopyMem(data->entries - 1, new_entries,
316 (data->entries_num + 1) * sizeof(struct ListEntry *));
317 FreeVec(data->entries - 1);
319 data->entries = new_entries + 1;
320 data->entries_allocated = new_entries_allocated;
321 return 1;
324 /**************************************************************************
325 Prepares the insertion of count entries at pos.
326 This function doesn't care if there is enough space in the datastructure.
327 SetListSize() must be used first.
328 With current implementation, this call will never fail
329 **************************************************************************/
330 static int PrepareInsertListEntries(struct MUI_ListData *data, int pos,
331 int count)
333 memmove(&data->entries[pos + count], &data->entries[pos],
334 (data->entries_num - pos) * sizeof(struct ListEntry *));
335 return 1;
338 /**************************************************************************
339 Removes count (already deinitalized) list entries starting az pos.
340 **************************************************************************/
341 static void RemoveListEntries(struct MUI_ListData *data, int pos, int count)
343 // FIXME: segfault if entries_num = pos = count = 1
344 memmove(&data->entries[pos], &data->entries[pos + count],
345 (data->entries_num - (pos + count)) * sizeof(struct ListEntry *));
348 /**************************************************************************
349 Frees all memory allocated by ParseListFormat()
350 **************************************************************************/
351 static void FreeListFormat(struct MUI_ListData *data)
353 int i;
355 if (data->ci)
357 for (i = 0; i < data->columns; i++)
359 FreeVec(data->ci[i].preparse);
360 data->ci[i].preparse = NULL;
362 FreeVec(data->ci);
363 data->ci = NULL;
365 FreeVec(data->preparses);
366 data->preparses = NULL;
367 if (data->strings)
369 FreeVec(data->strings - 1);
370 data->strings = NULL;
372 data->columns = 0;
375 /**************************************************************************
376 Parses the given format string (also frees a previously parsed format).
377 Return 0 on failure.
378 **************************************************************************/
379 static int ParseListFormat(struct MUI_ListData *data, STRPTR format)
381 int new_columns, i;
382 STRPTR ptr;
383 STRPTR format_sep;
384 char c;
386 IPTR args[ARG_CNT];
387 struct RDArgs *rdargs;
389 if (!format)
390 format = (STRPTR) "";
392 ptr = format;
394 FreeListFormat(data);
396 new_columns = 1;
398 /* Count the number of columns first */
399 while ((c = *ptr++))
400 if (c == ',')
401 new_columns++;
403 if (!(data->preparses =
404 AllocVec((new_columns + 10) * sizeof(STRPTR), 0)))
405 return 0;
407 if (!(data->strings = AllocVec((new_columns + 1 + 10)
408 * sizeof(STRPTR), 0))) /* hold enough space also for the entry pos,
409 * used by orginal MUI and also some
410 * security space */
411 return 0;
413 if (!(data->ci = AllocVec(new_columns * sizeof(struct ColumnInfo), 0)))
414 return 0;
416 // set defaults
417 for (i = 0; i < new_columns; i++)
419 data->ci[i].colno = -1; // -1 means: use unassigned column
420 data->ci[i].weight = 100;
421 data->ci[i].delta = 4;
422 data->ci[i].min_width = -1;
423 data->ci[i].max_width = -1;
424 data->ci[i].user_width = -1;
425 data->ci[i].bar = FALSE;
426 data->ci[i].preparse = NULL;
429 if ((format_sep = StrDup(format)) != 0)
431 for (i = 0; format_sep[i] != '\0'; i++)
433 if (format_sep[i] == ',')
434 format_sep[i] = '\0';
437 if ((rdargs = AllocDosObject(DOS_RDARGS, NULL)) != 0)
439 ptr = format_sep;
440 i = 0;
443 rdargs->RDA_Source.CS_Buffer = ptr;
444 rdargs->RDA_Source.CS_Length = strlen(ptr);
445 rdargs->RDA_Source.CS_CurChr = 0;
446 rdargs->RDA_DAList = 0;
447 rdargs->RDA_Buffer = NULL;
448 rdargs->RDA_BufSiz = 0;
449 rdargs->RDA_ExtHelp = NULL;
450 rdargs->RDA_Flags = 0;
452 memset(args, 0, sizeof args);
453 if (ReadArgs(FORMAT_TEMPLATE, args, rdargs))
455 if (args[ARG_COL])
456 data->ci[i].colno = *(LONG *) args[ARG_COL];
457 if (args[ARG_WEIGHT])
458 data->ci[i].weight = *(LONG *) args[ARG_WEIGHT];
459 if (args[ARG_DELTA])
460 data->ci[i].delta = *(LONG *) args[ARG_DELTA];
461 if (args[ARG_MINWIDTH])
462 data->ci[i].min_width =
463 *(LONG *) args[ARG_MINWIDTH];
464 if (args[ARG_MAXWIDTH])
465 data->ci[i].max_width =
466 *(LONG *) args[ARG_MAXWIDTH];
467 data->ci[i].bar = args[ARG_BAR];
468 if (args[ARG_PREPARSE])
469 data->ci[i].preparse =
470 StrDup((STRPTR) args[ARG_PREPARSE]);
472 FreeArgs(rdargs);
474 ptr += strlen(ptr) + 1;
475 i++;
477 while (i < new_columns);
478 FreeDosObject(DOS_RDARGS, rdargs);
480 FreeVec(format_sep);
483 for (i = 0; i < new_columns; i++)
485 D(bug("colno %d weight %d delta %d preparse %s\n",
486 data->ci[i].colno, data->ci[i].weight, data->ci[i].delta,
487 data->ci[i].preparse));
490 data->columns = new_columns;
491 data->strings++; /* Skip entry pos */
493 return 1;
496 /**************************************************************************
497 Call the MUIM_List_Display for the given entry. It fills out
498 data->string and data->preparses
499 **************************************************************************/
500 static void DisplayEntry(struct IClass *cl, Object *obj, int entry_pos)
502 struct MUI_ListData *data = INST_DATA(cl, obj);
503 APTR entry_data;
504 int col;
506 for (col = 0; col < data->columns; col++)
507 data->preparses[col] = data->ci[col].preparse;
509 if (entry_pos == ENTRY_TITLE)
511 if ((data->columns == 1) && (data->title != (STRPTR) 1))
513 *data->strings = data->title;
514 return;
516 entry_data = NULL; /* it's a title request */
518 else
519 entry_data = data->entries[entry_pos]->data;
521 /* Get the display formation */
522 DoMethod(obj, MUIM_List_Display, (IPTR) entry_data,
523 (IPTR) data->strings, entry_pos, (IPTR) data->preparses);
526 /**************************************************************************
527 Determine the dims of a single entry and adapt the columninfo according
528 to it. pos might be ENTRY_TITLE. Returns 0 if pos entry needs to
529 be redrawn after this operation, 1 if all entries need to be redrawn.
530 **************************************************************************/
531 static int CalcDimsOfEntry(struct IClass *cl, Object *obj, int pos)
533 struct MUI_ListData *data = INST_DATA(cl, obj);
534 struct ListEntry *entry = data->entries[pos];
535 int j;
536 int ret = 0;
538 if (!entry)
539 return ret;
541 if (!(_flags(obj) & MADF_SETUP))
542 return ret;
544 DisplayEntry(cl, obj, pos);
546 /* Set height to at least minheight */
547 if (data->entries[pos]->height < data->entry_minheight)
548 data->entries[pos]->height = data->entry_minheight;
550 for (j = 0; j < data->columns; j++)
552 ZText *text =
553 zune_text_new(data->preparses[j], data->strings[j],
554 ZTEXT_ARG_NONE, 0);
555 if (text != NULL)
557 zune_text_get_bounds(text, obj);
559 if (text->height > data->entries[pos]->height)
561 data->entries[pos]->height = text->height;
562 /* entry height changed, redraw all entries later */
563 ret = 1;
565 data->entries[pos]->widths[j] = text->width;
567 if (text->width > data->ci[j].entries_width)
569 /* This columns width is bigger than the other in the same
570 * columns, so we store this value
572 data->ci[j].entries_width = text->width;
573 /* column width changed, redraw all entries later */
574 ret = 1;
577 zune_text_destroy(text);
580 if (data->entries[pos]->height > data->entry_maxheight)
582 data->entry_maxheight = data->entries[pos]->height;
583 /* maximum entry height changed, redraw all entries later */
584 ret = 1;
587 return ret;
590 /**************************************************************************
591 Determine the widths of the entries
592 **************************************************************************/
593 static void CalcWidths(struct IClass *cl, Object *obj)
595 int i, j;
596 struct MUI_ListData *data = INST_DATA(cl, obj);
598 if (!(_flags(obj) & MADF_SETUP))
599 return;
601 for (j = 0; j < data->columns; j++)
602 data->ci[j].entries_width = 0;
604 data->entry_maxheight = 0;
605 data->entries_totalheight = 0;
606 data->entries_maxwidth = 0;
608 for (i = (data->title ? ENTRY_TITLE : 0); i < data->entries_num; i++)
610 CalcDimsOfEntry(cl, obj, i);
611 data->entries_totalheight += data->entries[i]->height;
614 for (j = 0; j < data->columns; j++)
615 data->entries_maxwidth += data->ci[j].entries_width;
617 if (!data->entry_maxheight)
618 data->entry_maxheight = 1;
621 /**************************************************************************
622 Calculates the number of visible entry lines. Returns 1 if it has
623 changed
624 **************************************************************************/
625 static int CalcVertVisible(struct IClass *cl, Object *obj)
627 struct MUI_ListData *data = INST_DATA(cl, obj);
628 int old_entries_visible = data->entries_visible;
629 int old_entries_top_pixel = data->entries_top_pixel;
631 data->entries_visible = (_mheight(obj) - data->title_height)
632 / (data->entry_maxheight /* + data->prefs_linespacing */ );
634 /* Distribute extra vertical space evenly between top and bottom of
635 * list */
637 data->entries_top_pixel = _mtop(obj) + data->title_height
638 + (_mheight(obj) - data->title_height
640 data->entries_visible *
641 (data->entry_maxheight /* + data->prefs_linespacing */ )) / 2;
643 return (old_entries_visible != data->entries_visible)
644 || (old_entries_top_pixel != data->entries_top_pixel);
647 /**************************************************************************
648 Default hook to compare two list entries. Works for strings only.
649 **************************************************************************/
650 AROS_UFH3S(int, default_compare_func,
651 AROS_UFHA(struct Hook *, h, A0),
652 AROS_UFHA(char *, s2, A2),
653 AROS_UFHA(char *, s1, A1))
655 AROS_USERFUNC_INIT
657 return Stricmp(s1, s2);
659 AROS_USERFUNC_EXIT
662 /**************************************************************************
663 OM_NEW
664 **************************************************************************/
665 IPTR List__OM_NEW(struct IClass *cl, Object *obj, struct opSet *msg)
667 struct MUI_ListData *data;
668 struct TagItem *tag;
669 struct TagItem *tags;
670 APTR *array = NULL;
671 LONG new_entries_active = MUIV_List_Active_Off;
673 obj = (Object *) DoSuperNewTags(cl, obj, NULL,
674 MUIA_Font, MUIV_Font_List,
675 MUIA_ShowSelState, FALSE,
676 MUIA_InputMode, MUIV_InputMode_RelVerify,
677 MUIA_Background, MUII_ListBack, TAG_MORE, (IPTR) msg->ops_AttrList);
678 if (!obj)
679 return FALSE;
681 data = INST_DATA(cl, obj);
683 data->columns = 1;
684 data->entries_active = MUIV_List_Active_Off;
685 data->intern_puddle_size = 2008;
686 data->intern_thresh_size = 1024;
687 data->default_compare_hook.h_Entry = (HOOKFUNC) default_compare_func;
688 data->default_compare_hook.h_SubEntry = 0;
689 data->compare_hook = &(data->default_compare_hook);
690 data->flags = LIST_SHOWDROPMARKS;
692 /* parse initial taglist */
693 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
695 switch (tag->ti_Tag)
697 case MUIA_List_Active:
698 new_entries_active = tag->ti_Data;
699 break;
701 case MUIA_List_Pool:
702 data->pool = (APTR) tag->ti_Data;
703 break;
705 case MUIA_List_PoolPuddleSize:
706 data->intern_puddle_size = tag->ti_Data;
707 break;
709 case MUIA_List_PoolThreshSize:
710 data->intern_thresh_size = tag->ti_Data;
711 break;
713 case MUIA_List_CompareHook:
714 data->compare_hook = (struct Hook *)tag->ti_Data;
715 if (data->compare_hook == NULL)
716 data->compare_hook = &data->default_compare_hook;
717 break;
719 case MUIA_List_ConstructHook:
720 data->construct_hook = (struct Hook *)tag->ti_Data;
721 break;
723 case MUIA_List_DestructHook:
724 data->destruct_hook = (struct Hook *)tag->ti_Data;
725 break;
727 case MUIA_List_DisplayHook:
728 data->display_hook = (struct Hook *)tag->ti_Data;
729 break;
731 case MUIA_List_MultiTestHook:
732 data->multi_test_hook = (struct Hook *)tag->ti_Data;
733 break;
735 case MUIA_List_SourceArray:
736 array = (APTR *) tag->ti_Data;
737 break;
739 case MUIA_List_Format:
740 data->format = StrDup((STRPTR) tag->ti_Data);
741 break;
743 case MUIA_List_Title:
744 data->title = (STRPTR) tag->ti_Data;
745 break;
747 case MUIA_List_MinLineHeight:
748 data->entry_minheight = tag->ti_Data;
749 break;
751 case MUIA_List_AdjustHeight:
752 _handle_bool_tag(data->flags, tag->ti_Data, LIST_ADJUSTHEIGHT);
753 break;
755 case MUIA_List_AdjustWidth:
756 _handle_bool_tag(data->flags, tag->ti_Data, LIST_ADJUSTWIDTH);
757 break;
759 case MUIA_List_AutoVisible:
760 _handle_bool_tag(data->flags, tag->ti_Data, LIST_AUTOVISIBLE);
761 break;
763 case MUIA_List_ShowDropMarks:
764 _handle_bool_tag(data->flags, tag->ti_Data, LIST_SHOWDROPMARKS);
765 break;
767 case MUIA_List_DragSortable:
768 _handle_bool_tag(data->flags, tag->ti_Data, LIST_DRAGSORTABLE);
769 set(obj, MUIA_Draggable, tag->ti_Data);
770 break;
774 if (!data->pool)
776 /* No memory pool given, so we create our own */
777 data->pool = data->intern_pool =
778 CreatePool(0, data->intern_puddle_size,
779 data->intern_thresh_size);
780 if (!data->pool)
782 CoerceMethod(cl, obj, OM_DISPOSE);
783 return 0;
787 /* parse the list format */
788 if (!(ParseListFormat(data, data->format)))
790 CoerceMethod(cl, obj, OM_DISPOSE);
791 return 0;
794 /* This is neccessary for at least the title */
795 if (!SetListSize(data, 0))
797 CoerceMethod(cl, obj, OM_DISPOSE);
798 return 0;
801 if (data->title)
803 if (!(data->entries[ENTRY_TITLE] = AllocListEntry(data)))
805 CoerceMethod(cl, obj, OM_DISPOSE);
806 return 0;
809 else
810 data->entries[ENTRY_TITLE] = NULL;
812 if (array)
814 int i;
815 /* Count the number of elements */
816 for (i = 0; array[i] != NULL; i++)
818 /* Insert them */
819 DoMethod(obj, MUIM_List_Insert, (IPTR) array, i,
820 MUIV_List_Insert_Top);
823 if ((data->entries_num) && (new_entries_active != MUIV_List_Active_Off))
825 switch (new_entries_active)
827 case MUIV_List_Active_Top:
828 new_entries_active = 0;
829 break;
831 case MUIV_List_Active_Bottom:
832 new_entries_active = data->entries_num - 1;
833 break;
836 if (new_entries_active < 0)
837 new_entries_active = 0;
838 else if (new_entries_active >= data->entries_num)
839 new_entries_active = data->entries_num - 1;
841 data->entries_active = new_entries_active;
842 /* Selected entry will be moved into visible area */
845 NewList((struct List *)&data->images);
847 D(bug("List_New(%lx)\n", obj));
849 return (IPTR) obj;
852 /**************************************************************************
853 OM_DISPOSE
854 **************************************************************************/
855 IPTR List__OM_DISPOSE(struct IClass *cl, Object *obj, Msg msg)
857 struct MUI_ListData *data = INST_DATA(cl, obj);
859 D(bug("List Dispose\n"));
861 /* Call destruct method for every entry and free the entries manually
862 * to avoid notification */
863 while (data->confirm_entries_num)
865 struct ListEntry *lentry =
866 data->entries[--data->confirm_entries_num];
867 DoMethod(obj, MUIM_List_Destruct, (IPTR) lentry->data,
868 (IPTR) data->pool);
869 FreeListEntry(data, lentry);
872 if (data->intern_pool)
873 DeletePool(data->intern_pool);
874 if (data->entries)
875 FreeVec(data->entries - 1);
876 /* title is currently before all other elements */
878 FreeListFormat(data);
879 FreeVec(data->format);
881 return DoSuperMethodA(cl, obj, msg);
885 /**************************************************************************
886 OM_SET
887 **************************************************************************/
888 IPTR List__OM_SET(struct IClass *cl, Object *obj, struct opSet *msg)
890 struct MUI_ListData *data = INST_DATA(cl, obj);
891 struct TagItem *tag;
892 struct TagItem *tags;
894 /* parse taglist */
895 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
897 switch (tag->ti_Tag)
899 case MUIA_List_CompareHook:
900 data->compare_hook = (struct Hook *)tag->ti_Data;
901 if (data->compare_hook == NULL)
902 data->compare_hook = &data->default_compare_hook;
903 break;
905 case MUIA_List_ConstructHook:
906 data->construct_hook = (struct Hook *)tag->ti_Data;
907 break;
909 case MUIA_List_DestructHook:
910 data->destruct_hook = (struct Hook *)tag->ti_Data;
911 break;
913 case MUIA_List_DisplayHook:
914 data->display_hook = (struct Hook *)tag->ti_Data;
915 break;
917 case MUIA_List_MultiTestHook:
918 data->multi_test_hook = (struct Hook *)tag->ti_Data;
919 if (data->multi_test_hook != NULL)
921 /* Clearing current selections is the easiest way to keep
922 * selections consistent with the new hook */
923 DoMethod(obj, MUIM_List_Select, MUIV_List_Select_All,
924 MUIV_List_Select_Off, NULL);
926 break;
928 case MUIA_List_Title:
929 data->title = (STRPTR) tag->ti_Data;
930 DoMethod(obj, MUIM_List_Redraw, MUIV_List_Redraw_All);
931 break;
933 case MUIA_List_VertProp_First:
934 data->vertprop_first = tag->ti_Data;
935 if (data->entries_first != tag->ti_Data)
937 set(obj, MUIA_List_First, tag->ti_Data);
939 break;
941 case MUIA_List_Format:
942 data->format = StrDup((STRPTR) tag->ti_Data);
943 ParseListFormat(data, data->format);
944 // FIXME: should we check for errors?
945 DoMethod(obj, MUIM_List_Redraw, MUIV_List_Redraw_All);
946 break;
948 case MUIA_List_VertProp_Entries:
949 data->vertprop_entries = tag->ti_Data;
950 break;
952 case MUIA_List_VertProp_Visible:
953 data->vertprop_visible = tag->ti_Data;
954 data->entries_visible = tag->ti_Data;
955 break;
957 case MUIA_List_Active:
959 LONG new_entries_active = tag->ti_Data;
961 if ((data->entries_num)
962 && (new_entries_active != MUIV_List_Active_Off))
964 switch (new_entries_active)
966 case MUIV_List_Active_Top:
967 new_entries_active = 0;
968 break;
970 case MUIV_List_Active_Bottom:
971 new_entries_active = data->entries_num - 1;
972 break;
974 case MUIV_List_Active_Up:
975 new_entries_active = data->entries_active - 1;
976 break;
978 case MUIV_List_Active_Down:
979 new_entries_active = data->entries_active + 1;
980 break;
982 case MUIV_List_Active_PageUp:
983 new_entries_active =
984 data->entries_active - data->entries_visible;
985 break;
987 case MUIV_List_Active_PageDown:
988 new_entries_active =
989 data->entries_active + data->entries_visible;
990 break;
993 if (new_entries_active < 0)
994 new_entries_active = 0;
995 else if (new_entries_active >= data->entries_num)
996 new_entries_active = data->entries_num - 1;
998 else
999 new_entries_active = -1;
1001 if (data->entries_active != new_entries_active)
1003 LONG old = data->entries_active;
1004 data->entries_active = new_entries_active;
1006 /* Selectchange stuff */
1007 if (new_entries_active != -1)
1009 DoMethod(obj, MUIM_List_SelectChange,
1010 new_entries_active, MUIV_List_Select_On, 0);
1011 DoMethod(obj, MUIM_List_SelectChange,
1012 new_entries_active, MUIV_List_Select_Active, 0);
1014 else
1015 DoMethod(obj, MUIM_List_SelectChange,
1016 MUIV_List_Active_Off, MUIV_List_Select_Off, 0);
1018 data->update = 2;
1019 data->update_pos = old;
1020 MUI_Redraw(obj, MADF_DRAWUPDATE);
1021 data->update = 2;
1022 data->update_pos = data->entries_active;
1023 MUI_Redraw(obj, MADF_DRAWUPDATE);
1025 /* Make new active entry visible (if there is one and
1026 list is visible) */
1027 if (new_entries_active != -1
1028 && (_flags(obj) & MADF_SETUP))
1030 DoMethod(obj, MUIM_List_Jump,
1031 MUIV_List_Jump_Active);
1035 break;
1037 case MUIA_List_First:
1038 data->update_pos = data->entries_first;
1039 data->update = 3;
1040 data->entries_first = tag->ti_Data;
1042 MUI_Redraw(obj, MADF_DRAWUPDATE);
1043 if ((data->vertprop_first != tag->ti_Data)
1044 && (!(data->flags & LIST_QUIET)))
1046 set(obj, MUIA_List_VertProp_First, tag->ti_Data);
1048 break;
1050 case MUIA_List_Visible: /* Shouldn't be settable? */
1051 if (data->vertprop_visible != tag->ti_Data)
1052 set(obj, MUIA_List_VertProp_Visible, tag->ti_Data);
1053 break;
1055 case MUIA_List_Entries:
1056 if (data->confirm_entries_num == tag->ti_Data)
1058 data->entries_num = tag->ti_Data;
1059 if (!(data->flags & LIST_QUIET))
1061 set(obj, MUIA_List_VertProp_Entries, data->entries_num);
1064 else
1066 D(bug("Bug: confirm_entries != MUIA_List_Entries!\n"));
1068 break;
1070 case MUIA_List_Quiet:
1071 _handle_bool_tag(data->flags, tag->ti_Data, LIST_QUIET);
1072 if (!tag->ti_Data)
1074 DoMethod(obj, MUIM_List_Redraw, MUIV_List_Redraw_All);
1075 if (data->entries_num != XGET(obj, MUIA_List_VertProp_Entries))
1076 set(obj, MUIA_List_VertProp_Entries, data->entries_num);
1077 if (data->vertprop_first !=
1078 XGET(obj, MUIA_List_VertProp_First))
1079 set(obj, MUIA_List_VertProp_First, data->vertprop_first);
1081 break;
1083 case MUIA_List_AutoVisible:
1084 _handle_bool_tag(data->flags, tag->ti_Data, LIST_AUTOVISIBLE);
1085 break;
1087 case MUIA_List_ShowDropMarks:
1088 _handle_bool_tag(data->flags, tag->ti_Data, LIST_SHOWDROPMARKS);
1089 break;
1091 case MUIA_List_DragSortable:
1092 _handle_bool_tag(data->flags, tag->ti_Data, LIST_DRAGSORTABLE);
1093 set(obj, MUIA_Draggable, tag->ti_Data);
1094 break;
1096 case MUIA_Selected:
1097 /* Swallow this so the Area class doesn't redraw us */
1098 tag->ti_Tag = TAG_IGNORE;
1099 break;
1103 return DoSuperMethodA(cl, obj, (Msg) msg);
1106 /**************************************************************************
1107 OM_GET
1108 **************************************************************************/
1109 IPTR List__OM_GET(struct IClass *cl, Object *obj, struct opGet *msg)
1111 /* small macro to simplify return value storage */
1112 #define STORE *(msg->opg_Storage)
1113 struct MUI_ListData *data = INST_DATA(cl, obj);
1115 switch (msg->opg_AttrID)
1117 case MUIA_List_Entries:
1118 STORE = data->entries_num;
1119 return 1;
1120 case MUIA_List_First:
1121 STORE = data->entries_first;
1122 return 1;
1123 case MUIA_List_Active:
1124 STORE = data->entries_active;
1125 return 1;
1126 case MUIA_List_InsertPosition:
1127 STORE = data->insert_position;
1128 return 1;
1129 case MUIA_List_Title:
1130 STORE = (IPTR) data->title;
1131 return 1;
1132 case MUIA_List_VertProp_Entries:
1133 STORE = data->vertprop_entries;
1134 return 1;
1135 case MUIA_List_VertProp_Visible:
1136 case MUIA_List_Visible:
1137 STORE = data->vertprop_visible;
1138 return 1;
1139 case MUIA_List_VertProp_First:
1140 STORE = data->vertprop_first;
1141 return 1;
1142 case MUIA_List_Format:
1143 STORE = (IPTR) data->format;
1144 return 1;
1145 case MUIA_List_AutoVisible:
1146 STORE = data->flags & LIST_AUTOVISIBLE;
1147 return 1;
1148 case MUIA_List_ShowDropMarks:
1149 STORE = data->flags & LIST_SHOWDROPMARKS;
1150 return 1;
1151 case MUIA_List_DragSortable:
1152 STORE = data->flags & LIST_DRAGSORTABLE;
1153 return 1;
1154 break;
1157 if (DoSuperMethodA(cl, obj, (Msg) msg))
1158 return 1;
1159 return 0;
1160 #undef STORE
1163 /**************************************************************************
1164 MUIM_Setup
1165 **************************************************************************/
1166 IPTR List__MUIM_Setup(struct IClass *cl, Object *obj,
1167 struct MUIP_Setup *msg)
1169 struct MUI_ListData *data = INST_DATA(cl, obj);
1171 if (!DoSuperMethodA(cl, obj, (Msg) msg))
1172 return 0;
1174 data->prefs_refresh = muiGlobalInfo(obj)->mgi_Prefs->list_refresh;
1175 data->prefs_linespacing =
1176 muiGlobalInfo(obj)->mgi_Prefs->list_linespacing;
1177 data->prefs_smoothed = muiGlobalInfo(obj)->mgi_Prefs->list_smoothed;
1178 data->prefs_smoothval = muiGlobalInfo(obj)->mgi_Prefs->list_smoothval;
1180 CalcWidths(cl, obj);
1182 data->list_cursor =
1183 zune_imspec_setup(MUII_ListCursor, muiRenderInfo(obj));
1184 data->list_select =
1185 zune_imspec_setup(MUII_ListSelect, muiRenderInfo(obj));
1186 data->list_selcur =
1187 zune_imspec_setup(MUII_ListSelCur, muiRenderInfo(obj));
1189 return 1;
1192 /**************************************************************************
1193 MUIM_Cleanup
1194 **************************************************************************/
1195 IPTR List__MUIM_Cleanup(struct IClass *cl, Object *obj,
1196 struct MUIP_Cleanup *msg)
1198 struct MUI_ListData *data = INST_DATA(cl, obj);
1199 struct ListImage *li = List_First(&data->images);
1201 while (li)
1203 struct ListImage *next = Node_Next(li);
1204 DoMethod(obj, MUIM_List_DeleteImage, (IPTR) li);
1205 li = next;
1208 zune_imspec_cleanup(data->list_cursor);
1209 zune_imspec_cleanup(data->list_select);
1210 zune_imspec_cleanup(data->list_selcur);
1212 return DoSuperMethodA(cl, obj, (Msg) msg);
1215 /**************************************************************************
1216 MUIM_AskMinMax
1217 **************************************************************************/
1218 IPTR List__MUIM_AskMinMax(struct IClass *cl, Object *obj,
1219 struct MUIP_AskMinMax *msg)
1221 struct MUI_ListData *data = INST_DATA(cl, obj);
1223 DoSuperMethodA(cl, obj, (Msg) msg);
1226 if ((data->flags & LIST_ADJUSTWIDTH) && (data->entries_num > 0))
1228 msg->MinMaxInfo->MinWidth += data->entries_maxwidth;
1229 msg->MinMaxInfo->DefWidth += data->entries_maxwidth;
1230 msg->MinMaxInfo->MaxWidth += data->entries_maxwidth;
1232 else
1234 msg->MinMaxInfo->MinWidth += 40;
1235 msg->MinMaxInfo->DefWidth += 100;
1236 msg->MinMaxInfo->MaxWidth = MUI_MAXMAX;
1239 if (data->entries_num > 0)
1241 if (data->flags & LIST_ADJUSTHEIGHT)
1243 msg->MinMaxInfo->MinHeight += data->entries_totalheight;
1244 msg->MinMaxInfo->DefHeight += data->entries_totalheight;
1245 msg->MinMaxInfo->MaxHeight += data->entries_totalheight;
1247 else
1249 ULONG h = data->entry_maxheight + data->prefs_linespacing;
1250 msg->MinMaxInfo->MinHeight += 2 * h + data->prefs_linespacing;
1251 msg->MinMaxInfo->DefHeight += 8 * h + data->prefs_linespacing;
1252 msg->MinMaxInfo->MaxHeight = MUI_MAXMAX;
1255 else
1257 msg->MinMaxInfo->MinHeight += 36;
1258 msg->MinMaxInfo->DefHeight += 96;
1259 msg->MinMaxInfo->MaxHeight = MUI_MAXMAX;
1261 D(bug("List %p minheigh=%d, line maxh=%d\n",
1262 obj, msg->MinMaxInfo->MinHeight, data->entry_maxheight));
1263 return TRUE;
1266 /****i*************************************************************************************
1268 NAME
1269 MUIM_Layout
1271 SYNOPSIS
1273 LOCATION
1274 List.mui
1276 FUNCTION
1278 NOTES
1280 EXAMPLE
1282 BUGS
1284 SEE ALSO
1286 INTERNALS
1288 *****************************************************************************************/
1291 IPTR List__MUIM_Layout(struct IClass *cl, Object *obj,
1292 struct MUIP_Layout *msg)
1294 struct MUI_ListData *data = INST_DATA(cl, obj);
1295 ULONG rc = DoSuperMethodA(cl, obj, (Msg) msg);
1296 LONG new_entries_first = data->entries_first;
1298 /* Calc the numbers of entries visible */
1299 CalcVertVisible(cl, obj);
1301 /* Ensure active entry is visible if requested */
1302 if (data->entries_active + 1 >=
1303 (data->entries_first + data->entries_visible)
1304 && (data->flags & LIST_AUTOVISIBLE) != 0)
1305 new_entries_first =
1306 data->entries_active - data->entries_visible + 1;
1308 /* Ensure there are no unnecessary empty lines */
1309 if ((new_entries_first + data->entries_visible >=
1310 data->entries_num)
1311 && (data->entries_visible <= data->entries_num))
1312 new_entries_first = data->entries_num - data->entries_visible;
1314 /* Always show the start of the list if it isn't long enough to fill the
1315 view */
1316 if (data->entries_num <= data->entries_visible)
1317 new_entries_first = 0;
1319 if (new_entries_first < 0)
1320 new_entries_first = 0;
1322 set(obj, new_entries_first != data->entries_first ?
1323 MUIA_List_First : TAG_IGNORE, new_entries_first);
1325 /* So the notify happens */
1326 set(obj, MUIA_List_VertProp_Visible, data->entries_visible);
1328 return rc;
1332 /**************************************************************************
1333 MUIM_Show
1334 **************************************************************************/
1335 IPTR List__MUIM_Show(struct IClass *cl, Object *obj,
1336 struct MUIP_Show *msg)
1338 struct MUI_ListData *data = INST_DATA(cl, obj);
1339 ULONG rc = DoSuperMethodA(cl, obj, (Msg) msg);
1341 zune_imspec_show(data->list_cursor, obj);
1342 zune_imspec_show(data->list_select, obj);
1343 zune_imspec_show(data->list_selcur, obj);
1344 return rc;
1348 /**************************************************************************
1349 MUIM_Hide
1350 **************************************************************************/
1351 IPTR List__MUIM_Hide(struct IClass *cl, Object *obj,
1352 struct MUIP_Hide *msg)
1354 struct MUI_ListData *data = INST_DATA(cl, obj);
1356 zune_imspec_hide(data->list_cursor);
1357 zune_imspec_hide(data->list_select);
1358 zune_imspec_hide(data->list_selcur);
1360 return DoSuperMethodA(cl, obj, (Msg) msg);
1364 /**************************************************************************
1365 Draw an entry at entry_pos at the given row. To draw the title, set pos to
1366 ENTRY_TITLE
1367 **************************************************************************/
1368 static VOID List_DrawEntry(struct IClass *cl, Object *obj, int entry_pos,
1369 int y)
1371 struct MUI_ListData *data = INST_DATA(cl, obj);
1372 int col, x1, x2;
1374 /* To be sure we don't draw anything if there is no title */
1375 if (entry_pos == ENTRY_TITLE && !data->title)
1376 return;
1378 DisplayEntry(cl, obj, entry_pos);
1379 x1 = _mleft(obj);
1381 for (col = 0; col < data->columns; col++)
1383 ZText *text;
1384 x2 = x1 + data->ci[col].entries_width;
1386 if ((text =
1387 zune_text_new(data->preparses[col], data->strings[col],
1388 ZTEXT_ARG_NONE, 0)))
1390 /* Could be made simpler, as we don't really need the bounds */
1391 zune_text_get_bounds(text, obj);
1392 /* Note, this was MPEN_SHADOW before */
1393 SetAPen(_rp(obj), muiRenderInfo(obj)->mri_Pens[MPEN_TEXT]);
1394 zune_text_draw(text, obj, x1, x2, y); /* totally wrong! */
1395 zune_text_destroy(text);
1397 x1 = x2 + data->ci[col].delta + (data->ci[col].bar ? BAR_WIDTH : 0);
1401 /**************************************************************************
1402 MUIM_Draw
1403 **************************************************************************/
1404 IPTR List__MUIM_Draw(struct IClass *cl, Object *obj, struct MUIP_Draw *msg)
1406 struct MUI_ListData *data = INST_DATA(cl, obj);
1407 int entry_pos, y;
1408 APTR clip;
1409 int start, end;
1410 BOOL scroll_caused_damage = FALSE;
1411 struct MUI_ImageSpec_intern *highlight;
1413 if (data->flags & LIST_QUIET)
1414 return 0;
1416 DoSuperMethodA(cl, obj, (Msg) msg);
1418 /* Calculate the title height */
1419 if (data->title)
1421 data->title_height = data->entries[ENTRY_TITLE]->height + 2;
1423 else
1425 data->title_height = 0;
1428 /* Calc the numbers of entries visible */
1429 CalcVertVisible(cl, obj);
1431 if ((msg->flags & MADF_DRAWUPDATE) == 0 || data->update == 1)
1433 DoMethod(obj, MUIM_DrawBackground, _mleft(obj), _mtop(obj),
1434 _mwidth(obj), _mheight(obj),
1435 0, data->entries_first * data->entry_maxheight, 0);
1438 clip = MUI_AddClipping(muiRenderInfo(obj), _mleft(obj), _mtop(obj),
1439 _mwidth(obj), _mheight(obj));
1441 if ((msg->flags & MADF_DRAWUPDATE) == 0 || data->update == 1)
1443 y = _mtop(obj);
1444 /* Draw Title
1446 if (data->title_height && data->title)
1448 List_DrawEntry(cl, obj, ENTRY_TITLE, y);
1449 y += data->entries[ENTRY_TITLE]->height;
1450 SetAPen(_rp(obj), _pens(obj)[MPEN_SHADOW]);
1451 Move(_rp(obj), _mleft(obj), y);
1452 Draw(_rp(obj), _mright(obj), y);
1453 SetAPen(_rp(obj), _pens(obj)[MPEN_SHINE]);
1454 y++;
1455 Move(_rp(obj), _mleft(obj), y);
1456 Draw(_rp(obj), _mright(obj), y);
1460 y = data->entries_top_pixel;
1462 start = data->entries_first;
1463 end = data->entries_first + data->entries_visible;
1465 if ((msg->flags & MADF_DRAWUPDATE) && data->update == 3)
1467 int diffy = data->entries_first - data->update_pos;
1468 int top, bottom;
1469 if (abs(diffy) < data->entries_visible)
1471 scroll_caused_damage =
1472 (_rp(obj)->Layer->Flags & LAYERREFRESH) ? FALSE : TRUE;
1474 ScrollRaster(_rp(obj), 0, diffy * data->entry_maxheight,
1475 _mleft(obj), y,
1476 _mright(obj),
1477 y + data->entry_maxheight * data->entries_visible);
1479 scroll_caused_damage =
1480 scroll_caused_damage
1481 && (_rp(obj)->Layer->Flags & LAYERREFRESH);
1483 if (diffy > 0)
1485 start = end - diffy;
1486 y += data->entry_maxheight * (data->entries_visible -
1487 diffy);
1489 else
1490 end = start - diffy;
1493 top = y;
1494 bottom = y + (end - start) * data->entry_maxheight;
1496 DoMethod(obj, MUIM_DrawBackground, _mleft(obj), top,
1497 _mwidth(obj), bottom - top + 1,
1499 top - _mtop(obj) + data->entries_first * data->entry_maxheight,
1503 for (entry_pos = start;
1504 entry_pos < end && entry_pos < data->entries_num; entry_pos++)
1506 struct ListEntry *entry = data->entries[entry_pos];
1508 if (!(msg->flags & MADF_DRAWUPDATE) ||
1509 ((msg->flags & MADF_DRAWUPDATE) && data->update == 1) ||
1510 ((msg->flags & MADF_DRAWUPDATE) && data->update == 3) ||
1511 ((msg->flags & MADF_DRAWUPDATE) && data->update == 2
1512 && data->update_pos == entry_pos))
1514 /* Choose appropriate highlight image */
1516 if (entry_pos == data->entries_active
1517 && entry->flags & ENTRY_SELECTED)
1518 highlight = data->list_selcur;
1519 else if (entry_pos == data->entries_active)
1520 highlight = data->list_cursor;
1521 else if (entry->flags & ENTRY_SELECTED)
1522 highlight = data->list_select;
1523 else
1524 highlight = NULL;
1526 /* Draw highlight or background */
1528 if (highlight != NULL)
1530 zune_imspec_draw(highlight, muiRenderInfo(obj),
1531 _mleft(obj), y, _mwidth(obj), data->entry_maxheight,
1532 0, y - data->entries_top_pixel, 0);
1534 else if ((msg->flags & MADF_DRAWUPDATE) && data->update == 2
1535 && data->update_pos == entry_pos)
1537 DoMethod(obj, MUIM_DrawBackground, _mleft(obj), y,
1538 _mwidth(obj), data->entry_maxheight, 0,
1539 y - _mtop(obj) +
1540 data->entries_first * data->entry_maxheight, 0);
1543 List_DrawEntry(cl, obj, entry_pos, y);
1545 y += data->entry_maxheight;
1548 MUI_RemoveClipping(muiRenderInfo(obj), clip);
1550 data->update = 0;
1552 if (scroll_caused_damage)
1554 if (MUI_BeginRefresh(muiRenderInfo(obj), 0))
1556 /* Theoretically it might happen that more damage is caused
1557 after ScrollRaster. By something else, like window movement
1558 in front of our window. Therefore refresh root object of
1559 window, not just this object */
1561 Object *o = NULL;
1563 get(_win(obj), MUIA_Window_RootObject, &o);
1564 MUI_Redraw(o, MADF_DRAWOBJECT);
1566 MUI_EndRefresh(muiRenderInfo(obj), 0);
1570 ULONG x1 = _mleft(obj);
1571 ULONG col;
1572 y = _mtop(obj);
1574 if (data->title_height && data->title)
1576 for (col = 0; col < data->columns; col++)
1578 ULONG halfdelta = data->ci[col].delta / 2;
1579 x1 += data->ci[col].entries_width + halfdelta;
1581 if (x1 + (data->ci[col].bar ? BAR_WIDTH : 0) > _mright(obj))
1582 break;
1584 if (data->ci[col].bar)
1586 SetAPen(_rp(obj), _pens(obj)[MPEN_SHINE]);
1587 Move(_rp(obj), x1, y);
1588 Draw(_rp(obj), x1,
1589 y + data->entries[ENTRY_TITLE]->height - 1);
1590 SetAPen(_rp(obj), _pens(obj)[MPEN_SHADOW]);
1591 Move(_rp(obj), x1 + 1, y);
1592 Draw(_rp(obj), x1 + 1,
1593 y + data->entries[ENTRY_TITLE]->height - 1);
1595 x1 += BAR_WIDTH;
1597 x1 += data->ci[col].delta - halfdelta;
1599 y += data->entries[ENTRY_TITLE]->height + 1;
1602 x1 = _mleft(obj);
1604 for (col = 0; col < data->columns; col++)
1606 ULONG halfdelta = data->ci[col].delta / 2;
1607 x1 += data->ci[col].entries_width + halfdelta;
1609 if (x1 + (data->ci[col].bar ? BAR_WIDTH : 0) > _mright(obj))
1610 break;
1612 if (data->ci[col].bar)
1614 SetAPen(_rp(obj), _pens(obj)[MPEN_SHINE]);
1615 Move(_rp(obj), x1, y);
1616 Draw(_rp(obj), x1, _mbottom(obj));
1617 SetAPen(_rp(obj), _pens(obj)[MPEN_SHADOW]);
1618 Move(_rp(obj), x1 + 1, y);
1619 Draw(_rp(obj), x1 + 1, _mbottom(obj));
1621 x1 += BAR_WIDTH;
1624 x1 += data->ci[col].delta - halfdelta;
1627 return 0;
1630 /*****************************************************************************************
1632 NAME
1633 MUIM_List_Clear
1635 SYNOPSIS
1636 DoMethod(obj, MUIM_List_Clear);
1638 LOCATION
1639 List.mui
1641 FUNCTION
1642 Removes all entries from the list.
1644 NOTES
1645 MUI V4
1647 EXAMPLE
1649 BUGS
1651 SEE ALSO
1653 INTERNALS
1655 *****************************************************************************************/
1658 IPTR List__MUIM_Clear(struct IClass *cl, Object *obj,
1659 struct MUIP_List_Clear *msg)
1661 struct MUI_ListData *data = INST_DATA(cl, obj);
1663 while (data->confirm_entries_num)
1665 struct ListEntry *lentry =
1666 data->entries[--data->confirm_entries_num];
1667 DoMethod(obj, MUIM_List_Destruct, (IPTR) lentry->data,
1668 (IPTR) data->pool);
1669 FreeListEntry(data, lentry);
1671 /* Should never fail when shrinking */
1672 SetListSize(data, 0);
1675 if (data->confirm_entries_num != data->entries_num)
1677 SetAttrs(obj, MUIA_List_Entries, 0, MUIA_List_First, 0,
1678 /* Notify only when no entry was active */
1679 data->entries_active !=
1680 MUIV_List_Active_Off ? MUIA_List_Active : TAG_DONE,
1681 MUIV_List_Active_Off, TAG_DONE);
1683 data->update = 1;
1684 MUI_Redraw(obj, MADF_DRAWUPDATE);
1687 return 0;
1690 /**************************************************************************
1691 MUIM_List_Exchange
1692 **************************************************************************/
1693 IPTR List__MUIM_Exchange(struct IClass *cl, Object *obj,
1694 struct MUIP_List_Exchange *msg)
1696 struct MUI_ListData *data = INST_DATA(cl, obj);
1697 LONG pos1, pos2;
1699 switch (msg->pos1)
1701 case MUIV_List_Exchange_Top:
1702 pos1 = 0;
1703 break;
1704 case MUIV_List_Exchange_Active:
1705 pos1 = data->entries_active;
1706 break;
1707 case MUIV_List_Exchange_Bottom:
1708 pos1 = data->entries_num - 1;
1709 break;
1710 default:
1711 pos1 = msg->pos1;
1714 switch (msg->pos2)
1716 case MUIV_List_Exchange_Top:
1717 pos2 = 0;
1718 break;
1719 case MUIV_List_Exchange_Active:
1720 pos2 = data->entries_active;
1721 break;
1722 case MUIV_List_Exchange_Bottom:
1723 pos2 = data->entries_num - 1;
1724 break;
1725 case MUIV_List_Exchange_Next:
1726 pos2 = pos1 + 1;
1727 break;
1728 case MUIV_List_Exchange_Previous:
1729 pos2 = pos1 - 1;
1730 break;
1731 default:
1732 pos2 = msg->pos2;
1735 if (pos1 >= 0 && pos1 < data->entries_num && pos2 >= 0
1736 && pos2 < data->entries_num && pos1 != pos2)
1738 struct ListEntry *save = data->entries[pos1];
1739 data->entries[pos1] = data->entries[pos2];
1740 data->entries[pos2] = save;
1742 data->update = 2;
1743 data->update_pos = pos1;
1744 MUI_Redraw(obj, MADF_DRAWUPDATE);
1746 data->update = 2;
1747 data->update_pos = pos2;
1748 MUI_Redraw(obj, MADF_DRAWUPDATE);
1750 return TRUE;
1752 else
1754 return FALSE;
1758 /**************************************************************************
1759 MUIM_List_Redraw
1760 **************************************************************************/
1761 IPTR List__MUIM_Redraw(struct IClass *cl, Object *obj,
1762 struct MUIP_List_Redraw *msg)
1764 struct MUI_ListData *data = INST_DATA(cl, obj);
1766 if (!(data->flags & LIST_QUIET))
1768 if (msg->pos == MUIV_List_Redraw_All)
1770 data->update = 1;
1771 CalcWidths(cl, obj);
1772 MUI_Redraw(obj, MADF_DRAWUPDATE);
1774 else
1776 LONG pos = -1;
1777 if (msg->pos == MUIV_List_Redraw_Active)
1778 pos = data->entries_active;
1779 else if (msg->pos == MUIV_List_Redraw_Entry)
1781 LONG i;
1782 for (i = 0; i < data->entries_num; i++)
1783 if (data->entries[i]->data == msg->entry)
1785 pos = i;
1786 break;
1789 else
1790 pos = msg->pos;
1792 if (pos != -1)
1794 if (CalcDimsOfEntry(cl, obj, pos))
1795 data->update = 1;
1796 else
1798 data->update = 2;
1799 data->update_pos = pos;
1801 MUI_Redraw(obj, MADF_DRAWUPDATE);
1805 return 0;
1808 /**************************************************************************
1809 MUIM_List_Remove
1810 **************************************************************************/
1811 IPTR List__MUIM_Remove(struct IClass *cl, Object *obj,
1812 struct MUIP_List_Remove *msg)
1814 struct MUI_ListData *data = INST_DATA(cl, obj);
1815 LONG pos, cur;
1816 LONG new_act;
1817 struct ListEntry *lentry;
1818 //int rem_count = 1;
1820 if (!data->entries_num)
1821 return 0;
1823 switch (msg->pos)
1825 case MUIV_List_Remove_First:
1826 pos = 0;
1827 break;
1829 case MUIV_List_Remove_Active:
1830 pos = data->entries_active;
1831 break;
1833 case MUIV_List_Remove_Last:
1834 pos = data->entries_num - 1;
1835 break;
1837 case MUIV_List_Remove_Selected:
1838 /* TODO: needs special handling */
1839 pos = data->entries_active;
1840 break;
1842 default:
1843 pos = msg->pos;
1844 break;
1847 if (pos < 0 || pos >= data->entries_num)
1848 return 0;
1850 new_act = data->entries_active;
1852 if (pos == new_act && new_act == data->entries_num - 1)
1853 new_act--; /* might become MUIV_List_Active_Off */
1855 lentry = data->entries[pos];
1856 DoMethod(obj, MUIM_List_Destruct, (IPTR) lentry->data,
1857 (IPTR) data->pool);
1859 cur = pos + 1;
1861 RemoveListEntries(data, pos, cur - pos);
1862 data->confirm_entries_num -= cur - pos;
1864 /* ensure that the active element is in a valid range */
1865 if (new_act >= data->entries_num)
1866 new_act = data->entries_num - 1;
1868 SetAttrs(obj, MUIA_List_Entries, data->confirm_entries_num,
1869 (new_act >= pos) || (new_act != data->entries_active) ?
1870 MUIA_List_Active : TAG_DONE,
1871 new_act, /* Inform only if neccessary (for notify) */
1872 TAG_DONE);
1874 data->update = 1;
1875 MUI_Redraw(obj, MADF_DRAWUPDATE);
1877 return 0;
1880 /**************************************************************************
1881 MUIM_List_Select
1882 **************************************************************************/
1883 IPTR List__MUIM_Select(struct IClass *cl, Object *obj,
1884 struct MUIP_List_Select *msg)
1886 struct MUI_ListData *data = INST_DATA(cl, obj);
1887 LONG pos, i, count, selcount=0, state=0;
1888 BOOL multi_allowed = TRUE, new_select_state = FALSE;
1890 /* Establish the range of entries affected */
1891 switch (msg->pos)
1893 case MUIV_List_Select_Active:
1894 pos = data->entries_active;
1895 if (pos == MUIV_List_Active_Off)
1896 count = 0;
1897 else
1898 count = 1;
1899 break;
1901 case MUIV_List_Select_All:
1902 pos = 0;
1903 count = data->entries_num;
1904 break;
1906 default:
1907 pos = msg->pos;
1908 count = 1;
1909 if (pos < 0 || pos >= data->entries_num)
1910 return 0;
1911 break;
1914 if (msg->seltype != MUIV_List_Select_Ask && data->multi_test_hook != NULL)
1916 /* Disallow selection of an additional entry if there is a currently
1917 selected entry that is not multi-selectable (in such case there
1918 will only be one entry currently selected, so no need to iterate) */
1919 i = MUIV_List_NextSelected_Start;
1920 DoMethod(obj, MUIM_List_NextSelected, (IPTR) &i);
1921 if (i != MUIV_List_NextSelected_End)
1922 selcount++;
1923 if (data->multi_test_hook != NULL && selcount != 0)
1924 multi_allowed = CallHookPkt(data->multi_test_hook, NULL,
1925 data->entries[i]->data);
1928 /* Change or check state of each entry in the range */
1929 for (i = pos; i < pos + count; i++)
1931 state = data->entries[i]->flags & ENTRY_SELECTED;
1932 switch (msg->seltype)
1934 case MUIV_List_Select_Off:
1935 new_select_state = FALSE;
1936 break;
1938 case MUIV_List_Select_On:
1939 new_select_state = TRUE;
1940 break;
1942 case MUIV_List_Select_Toggle:
1943 new_select_state = !state;
1944 break;
1946 default:
1947 if (data->entries[i]->flags & ENTRY_SELECTED)
1948 selcount++;
1949 break;
1952 if (msg->seltype != MUIV_List_Select_Ask)
1954 /* Disallow selection if entry is not multi-selectable and
1955 * there are already selected entries */
1956 if (data->multi_test_hook != NULL && new_select_state)
1957 new_select_state = multi_allowed && (selcount == 0 ||
1958 CallHookPkt(data->multi_test_hook, NULL,
1959 data->entries[i]->data));
1961 if (new_select_state)
1962 data->entries[i]->flags |= ENTRY_SELECTED;
1963 else
1964 data->entries[i]->flags &= ~ENTRY_SELECTED;
1968 /* Report old state or number of selected entries */
1969 if (msg->info)
1971 if (msg->pos == MUIV_List_Select_All
1972 && msg->seltype == MUIV_List_Select_Ask)
1973 *msg->info = selcount;
1974 else
1975 *msg->info = state;
1978 /* Redraw unless it was just an enquiry */
1979 if (msg->seltype != MUIV_List_Select_Ask)
1981 if (count > 1)
1982 data->update = 1;
1983 else
1985 data->update = 2;
1986 data->update_pos = pos;
1988 MUI_Redraw(obj, MADF_DRAWUPDATE);
1991 return 0;
1994 /**************************************************************************
1995 MUIM_List_Insert
1996 **************************************************************************/
1998 IPTR List__MUIM_Insert(struct IClass *cl, Object *obj,
1999 struct MUIP_List_Insert *msg)
2001 struct MUI_ListData *data = INST_DATA(cl, obj);
2002 LONG pos, count, sort;
2004 count = msg->count;
2005 sort = 0;
2007 if (count == -1)
2009 /* Count the number of entries */
2010 for (count = 0; msg->entries[count] != NULL; count++)
2014 if (count <= 0)
2015 return ~0;
2017 switch (msg->pos)
2019 case MUIV_List_Insert_Top:
2020 pos = 0;
2021 break;
2023 case MUIV_List_Insert_Active:
2024 if (data->entries_active != -1)
2025 pos = data->entries_active;
2026 else
2027 pos = 0;
2028 break;
2030 case MUIV_List_Insert_Sorted:
2031 pos = data->entries_num;
2032 sort = 1; /* we sort'em later */
2033 break;
2035 case MUIV_List_Insert_Bottom:
2036 pos = data->entries_num;
2037 break;
2039 default:
2040 if (msg->pos > data->entries_num)
2041 pos = data->entries_num;
2042 else if (msg->pos < 0)
2043 pos = 0;
2044 else
2045 pos = msg->pos;
2046 break;
2049 if (!(SetListSize(data, data->entries_num + count)))
2050 return ~0;
2052 LONG until = pos + count;
2053 APTR *toinsert = msg->entries;
2055 if (!(PrepareInsertListEntries(data, pos, count)))
2056 return ~0;
2058 while (pos < until)
2060 struct ListEntry *lentry;
2062 if (!(lentry = AllocListEntry(data)))
2064 /* Panic, but we must be in a consistent state, so remove
2065 * the space where the following list entries should have gone
2067 RemoveListEntries(data, pos, until - pos);
2068 return ~0;
2071 /* now call the construct method which returns us a pointer which
2072 we need to store */
2073 lentry->data = (APTR) DoMethod(obj, MUIM_List_Construct,
2074 (IPTR) * toinsert, (IPTR) data->pool);
2075 if (!lentry->data)
2077 FreeListEntry(data, lentry);
2078 RemoveListEntries(data, pos, until - pos);
2080 /* TODO: Also check for visible stuff like below */
2081 if (data->entries_num != data->confirm_entries_num)
2082 set(obj, MUIA_List_Entries, data->confirm_entries_num);
2083 return ~0;
2086 data->entries[pos] = lentry;
2087 data->confirm_entries_num++;
2089 if (_flags(obj) & MADF_SETUP)
2091 /* We have to calculate the width and height of the newly
2092 * inserted entry. This has to be done after inserting the
2093 * element into the list */
2094 CalcDimsOfEntry(cl, obj, pos);
2097 toinsert++;
2098 pos++;
2101 /* Recalculate the number of visible entries */
2102 if (_flags(obj) & MADF_SETUP)
2103 CalcVertVisible(cl, obj);
2105 if (data->entries_num != data->confirm_entries_num)
2107 SetAttrs(obj,
2108 MUIA_List_Entries, data->confirm_entries_num,
2109 MUIA_List_Visible, data->entries_visible, TAG_DONE);
2112 /* If the array is already sorted, we could do a simple insert
2113 * sort and would be much faster than with qsort.
2114 * If an array is not yet sorted, does a MUIV_List_Insert_Sorted
2115 * sort the whole array?
2117 * I think, we better sort the whole array:
2119 if (sort)
2121 DoMethod(obj, MUIM_List_Sort);
2122 /* TODO: which pos to return here !? */
2123 /* MUIM_List_Sort already called MUI_Redraw */
2125 else
2127 data->update = 1;
2128 MUI_Redraw(obj, MADF_DRAWUPDATE);
2130 data->insert_position = pos;
2132 return (ULONG) pos;
2135 /**************************************************************************
2136 MUIM_List_InsertSingle
2137 **************************************************************************/
2138 IPTR List__MUIM_InsertSingle(struct IClass *cl, Object *obj,
2139 struct MUIP_List_InsertSingle *msg)
2141 return DoMethod(obj, MUIM_List_Insert, (IPTR) & msg->entry, 1,
2142 msg->pos);
2145 /**************************************************************************
2146 MUIM_List_GetEntry
2147 **************************************************************************/
2148 IPTR List__MUIM_GetEntry(struct IClass *cl, Object *obj,
2149 struct MUIP_List_GetEntry *msg)
2151 struct MUI_ListData *data = INST_DATA(cl, obj);
2152 int pos = msg->pos;
2154 if (pos == MUIV_List_GetEntry_Active)
2155 pos = data->entries_active;
2157 if (pos < 0 || pos >= data->entries_num)
2159 *msg->entry = NULL;
2160 return 0;
2162 *msg->entry = data->entries[pos]->data;
2163 return (IPTR) *msg->entry;
2166 /**************************************************************************
2167 MUIM_List_Construct
2168 **************************************************************************/
2169 IPTR List__MUIM_Construct(struct IClass *cl, Object *obj,
2170 struct MUIP_List_Construct *msg)
2172 struct MUI_ListData *data = INST_DATA(cl, obj);
2174 if (NULL == data->construct_hook)
2175 return (IPTR) msg->entry;
2176 if ((IPTR) data->construct_hook == MUIV_List_ConstructHook_String)
2178 int len = msg->entry ? strlen((STRPTR) msg->entry) : 0;
2179 ULONG *mem = AllocPooled(msg->pool, len + 5);
2181 if (NULL == mem)
2182 return 0;
2183 mem[0] = len + 5;
2184 if (msg->entry != NULL)
2185 strcpy((STRPTR) (mem + 1), (STRPTR) msg->entry);
2186 else
2187 *(STRPTR) (mem + 1) = 0;
2188 return (IPTR) (mem + 1);
2190 return CallHookPkt(data->construct_hook, msg->pool, msg->entry);
2193 /**************************************************************************
2194 MUIM_List_Destruct
2195 **************************************************************************/
2196 IPTR List__MUIM_Destruct(struct IClass *cl, Object *obj,
2197 struct MUIP_List_Destruct *msg)
2199 struct MUI_ListData *data = INST_DATA(cl, obj);
2201 if (NULL == data->destruct_hook)
2202 return 0;
2204 if ((IPTR) data->destruct_hook == MUIV_List_DestructHook_String)
2206 ULONG *mem = ((ULONG *) msg->entry) - 1;
2207 FreePooled(msg->pool, mem, mem[0]);
2209 else
2211 CallHookPkt(data->destruct_hook, msg->pool, msg->entry);
2213 return 0;
2216 /*****************************************************************************************
2218 NAME
2219 MUIM_List_Compare
2221 SYNOPSIS
2222 DoMethod(obj, MUIM_List_Compare, APTR entry1, APTR entry2,
2223 LONG sort_type1, LONG sort_type2);
2225 LOCATION
2226 List.mui
2228 FUNCTION
2229 Compare two list entries according to the current comparison hook
2230 (MUIA_List_CompareHook).
2232 INPUTS
2233 entry1 - the first entry data.
2234 entry2 - the second entry data.
2235 sort_type1 - undocumented.
2236 sort_type2 - undocumented.
2238 NOTES
2239 MUI V20
2241 EXAMPLE
2243 BUGS
2245 SEE ALSO
2246 MUIA_List_CompareHook, MUIM_List_Sort.
2248 INTERNALS
2250 *****************************************************************************************/
2253 IPTR List__MUIM_Compare(struct IClass *cl, Object *obj,
2254 struct MUIP_List_Compare *msg)
2256 struct MUI_ListData *data = INST_DATA(cl, obj);
2258 return CallHookPkt(data->compare_hook, msg->entry2, msg->entry1);
2261 /**************************************************************************
2262 MUIM_List_Display
2263 **************************************************************************/
2264 IPTR List__MUIM_Display(struct IClass *cl, Object *obj,
2265 struct MUIP_List_Display *msg)
2267 struct MUI_ListData *data = INST_DATA(cl, obj);
2269 if (NULL == data->display_hook)
2271 if (msg->entry)
2272 *msg->array = msg->entry;
2273 else
2274 *msg->array = 0;
2275 return 1;
2278 *((ULONG *) (msg->array - 1)) = msg->entry_pos;
2279 return CallHookPkt(data->display_hook, msg->array, msg->entry);
2282 /**************************************************************************
2283 MUIM_List_SelectChange
2284 **************************************************************************/
2285 IPTR List__MUIM_SelectChange(struct IClass *cl, Object *obj,
2286 struct MUIP_List_SelectChange *msg)
2288 return 1;
2291 /**************************************************************************
2292 MUIM_List_CreateImage
2293 Called by a List subclass in its Setup method.
2294 Connects an Area subclass object to the list, much like an object gets
2295 connected to a window. List calls Setup and AskMinMax on that object,
2296 keeps a reference to it (that reference will be returned).
2297 Text engine will dereference that pointer and draw the object with its
2298 default size.
2299 **************************************************************************/
2300 IPTR List__MUIM_CreateImage(struct IClass *cl, Object *obj,
2301 struct MUIP_List_CreateImage *msg)
2303 struct MUI_ListData *data = INST_DATA(cl, obj);
2304 struct ListImage *li;
2306 /* List must be already setup in Setup of your subclass */
2307 if (!(_flags(obj) & MADF_SETUP))
2308 return 0;
2309 li = AllocPooled(data->pool, sizeof(struct ListImage));
2310 if (!li)
2311 return 0;
2312 li->obj = msg->obj;
2314 AddTail((struct List *)&data->images, (struct Node *)li);
2315 DoMethod(li->obj, MUIM_ConnectParent, (IPTR) obj);
2316 DoSetupMethod(li->obj, muiRenderInfo(obj));
2319 return (IPTR) li;
2322 /**************************************************************************
2323 MUIM_List_DeleteImage
2324 **************************************************************************/
2325 IPTR List__MUIM_DeleteImage(struct IClass *cl, Object *obj,
2326 struct MUIP_List_DeleteImage *msg)
2328 struct MUI_ListData *data = INST_DATA(cl, obj);
2329 struct ListImage *li = (struct ListImage *)msg->listimg;
2331 if (li)
2333 DoMethod(li->obj, MUIM_Cleanup);
2334 DoMethod(li->obj, MUIM_DisconnectParent);
2335 Remove((struct Node *)li);
2336 FreePooled(data->pool, li, sizeof(struct ListImage));
2339 return 0;
2342 /*****************************************************************************************
2344 NAME
2345 MUIM_List_Jump
2347 SYNOPSIS
2348 DoMethod(obj, MUIM_List_Jump, LONG pos);
2350 LOCATION
2351 List.mui
2353 FUNCTION
2354 Scrolls the list so that a particular entry is visible.
2356 INPUTS
2357 pos - index of entry that should become visible, or one of these
2358 special values:
2359 MUIV_List_Jump_Active: show the active entry.
2360 MUIV_List_Jump_Top: show the first entry.
2361 MUIV_List_Jump_Bottom: show the last entry.
2362 MUIV_List_Jump_Up: show the previous hidden entry.
2363 MUIV_List_Jump_Down: show the next hidden entry.
2365 NOTES
2366 MUI V4
2368 EXAMPLE
2370 BUGS
2372 SEE ALSO
2374 INTERNALS
2376 *****************************************************************************************/
2378 IPTR List__MUIM_Jump(struct IClass *cl, Object *obj,
2379 struct MUIP_List_Jump *msg)
2381 struct MUI_ListData *data = INST_DATA(cl, obj);
2382 LONG pos = msg->pos;
2384 switch (pos)
2386 case MUIV_List_Jump_Top:
2387 pos = 0;
2388 break;
2390 case MUIV_List_Jump_Active:
2391 pos = data->entries_active;
2392 break;
2394 case MUIV_List_Jump_Bottom:
2395 pos = data->entries_num - 1;
2396 break;
2398 case MUIV_List_Jump_Down:
2399 pos = data->entries_first + data->entries_visible;
2400 break;
2402 case MUIV_List_Jump_Up:
2403 pos = data->entries_first - 1;
2404 break;
2407 if (pos >= data->entries_num)
2409 pos = data->entries_num - 1;
2411 if (pos < 0)
2412 pos = 0;
2414 if (pos < data->entries_first)
2416 set(obj, MUIA_List_First, pos);
2418 else if (pos >= data->entries_first + data->entries_visible)
2420 pos -= (data->entries_visible - 1);
2421 if (pos < 0)
2422 pos = 0;
2423 if (pos != data->entries_first)
2425 set(obj, MUIA_List_First, pos);
2429 return TRUE;
2432 /*****************************************************************************************
2434 NAME
2435 MUIM_List_Sort
2437 SYNOPSIS
2438 DoMethod(obj, MUIM_List_Sort);
2440 LOCATION
2441 List.mui
2443 FUNCTION
2444 Sort the list's entries according to the current comparison hook
2445 (MUIA_List_CompareHook).
2447 NOTES
2448 MUI V4
2450 EXAMPLE
2452 BUGS
2454 SEE ALSO
2455 MUIA_List_CompareHook, MUIM_List_Compare.
2457 INTERNALS
2459 *****************************************************************************************/
2461 IPTR List__MUIM_Sort(struct IClass *cl, Object *obj,
2462 struct MUIP_List_Sort *msg)
2464 struct MUI_ListData *data = INST_DATA(cl, obj);
2466 int i, j, max;
2467 struct MUIP_List_Compare cmpmsg =
2468 { MUIM_List_Compare, NULL, NULL, 0, 0 };
2470 if (data->entries_num > 1)
2473 Simple sort algorithm. Feel free to improve it.
2475 for (i = 0; i < data->entries_num - 1; i++)
2477 max = i;
2478 for (j = i + 1; j < data->entries_num; j++)
2480 cmpmsg.entry1 = data->entries[max]->data;
2481 cmpmsg.entry2 = data->entries[j]->data;
2482 if ((LONG) DoMethodA(obj, (Msg) & cmpmsg) > 0)
2484 max = j;
2487 if (i != max)
2489 APTR tmp = data->entries[i];
2490 data->entries[i] = data->entries[max];
2491 data->entries[max] = tmp;
2496 data->update = 1;
2497 MUI_Redraw(obj, MADF_DRAWUPDATE);
2499 return 0;
2502 /*****************************************************************************************
2504 NAME
2505 MUIM_List_Move
2507 SYNOPSIS
2508 DoMethod(obj, MUIM_List_Move, LONG from, LONG to);
2510 LOCATION
2511 List.mui
2513 FUNCTION
2514 Move a list entry to a new position.
2516 INPUTS
2517 from - the current index of the entry that should be moved, or one of
2518 these special values:
2519 MUIV_List_Move_Active: the active entry.
2520 MUIV_List_Move_Top: the first entry.
2521 MUIV_List_Move_Bottom: the last entry.
2522 to - the index of the entry's new position, or one of
2523 these special values:
2524 MUIV_List_Move_Active: the active entry.
2525 MUIV_List_Move_Top: the first entry.
2526 MUIV_List_Move_Bottom: the last entry.
2528 NOTES
2529 MUI V9
2531 EXAMPLE
2533 BUGS
2535 SEE ALSO
2537 INTERNALS
2539 *****************************************************************************************/
2541 IPTR List__MUIM_Move(struct IClass *cl, Object *obj,
2542 struct MUIP_List_Move *msg)
2544 struct MUI_ListData *data = INST_DATA(cl, obj);
2546 LONG from, to;
2547 int i;
2549 /* Normalise special 'from' values */
2550 switch (msg->from)
2552 case MUIV_List_Move_Top:
2553 from = 0;
2554 break;
2555 case MUIV_List_Move_Active:
2556 from = data->entries_active;
2557 break;
2558 case MUIV_List_Move_Bottom:
2559 from = data->entries_num - 1;
2560 break;
2561 default:
2562 from = msg->from;
2565 /* Normalise special 'to' values */
2566 switch (msg->to)
2568 case MUIV_List_Move_Top:
2569 to = 0;
2570 break;
2571 case MUIV_List_Move_Active:
2572 to = data->entries_active;
2573 break;
2574 case MUIV_List_Move_Bottom:
2575 to = data->entries_num - 1;
2576 break;
2577 case MUIV_List_Move_Next:
2578 to = from + 1;
2579 break;
2580 case MUIV_List_Move_Previous:
2581 to = from - 1;
2582 break;
2583 default:
2584 to = msg->to;
2587 /* Check that values are within valid bounds */
2588 if (from > data->entries_num - 1 || from < 0
2589 || to > data->entries_num - 1 || to < 0 || from == to)
2590 return (IPTR) FALSE;
2592 /* Shift all entries in the range between the 'from' and 'to' positions */
2593 if (from < to)
2595 struct ListEntry *backup = data->entries[from];
2596 for (i = from; i < to; i++)
2597 data->entries[i] = data->entries[i + 1];
2598 data->entries[to] = backup;
2600 else
2602 struct ListEntry *backup = data->entries[from];
2603 for (i = from; i > to; i--)
2604 data->entries[i] = data->entries[i - 1];
2605 data->entries[to] = backup;
2608 /* Update index of active entry */
2609 if (from == data->entries_active)
2610 data->entries_active = to;
2611 else if (data->entries_active > from && data->entries_active < to)
2612 data->entries_active--;
2613 else if (data->entries_active < from && data->entries_active >= to)
2614 data->entries_active++;
2616 /* Reflect list changes visually */
2617 data->update = 1;
2618 MUI_Redraw(obj, MADF_DRAWUPDATE);
2620 return TRUE;
2623 /**************************************************************************
2624 MUIM_List_NextSelected
2625 **************************************************************************/
2626 IPTR List__MUIM_NextSelected(struct IClass *cl, Object *obj,
2627 struct MUIP_List_NextSelected *msg)
2629 struct MUI_ListData *data = INST_DATA(cl, obj);
2630 LONG pos, i;
2631 BOOL found = FALSE;
2633 /* Get the first entry to check */
2634 pos = *msg->pos;
2635 if (pos == MUIV_List_NextSelected_Start)
2636 pos = 0;
2637 else
2638 pos++;
2640 /* Find the next selected entry */
2641 for (i = pos; i < data->entries_num && !found; i++)
2643 if (data->entries[i]->flags & ENTRY_SELECTED)
2645 pos = i;
2646 found = TRUE;
2650 /* Return index of selected entry, or indicate there are no more */
2651 if (!found)
2652 pos = MUIV_List_NextSelected_End;
2653 *msg->pos = pos;
2655 return TRUE;
2658 /**************************************************************************
2659 MUIM_List_TestPos
2660 **************************************************************************/
2661 IPTR List__MUIM_TestPos(struct IClass *cl, Object *obj,
2662 struct MUIP_List_TestPos *msg)
2664 struct MUI_ListData *data = INST_DATA(cl, obj);
2665 struct MUI_List_TestPos_Result *result = msg->res;
2666 LONG col = -1, row = -1;
2667 UWORD flags = 0, i;
2668 LONG mx = msg->x - _left(obj);
2669 LONG entries_visible;
2671 if (data->entries_visible <= data->entries_num)
2672 entries_visible = data->entries_visible;
2673 else
2674 entries_visible = data->entries_num;
2675 LONG ey = msg->y - data->entries_top_pixel;
2676 /* y coordinates transformed to the entries */
2678 /* Now check if it was clicked on a title or on entries */
2679 if (ey < 0)
2680 flags |= MUI_LPR_ABOVE;
2681 else if (ey >= entries_visible * data->entry_maxheight)
2682 flags |= MUI_LPR_BELOW;
2683 else
2685 /* Identify row */
2686 row = ey / data->entry_maxheight + data->entries_first;
2687 result->yoffset =
2688 ey % data->entry_maxheight - data->entry_maxheight / 2;
2691 if (mx < 0)
2692 flags |= MUI_LPR_LEFT;
2693 else if (mx >= _width(obj))
2694 flags |= MUI_LPR_RIGHT;
2695 else
2697 /* Identify column */
2698 if (data->entries_num > 0 && data->columns > 0)
2700 LONG width_sum = 0;
2701 col = data->columns - 1;
2702 for (i = 0; i < data->columns; i++)
2704 result->xoffset = mx - width_sum;
2705 width_sum +=
2706 data->ci[i].entries_width +
2707 data->ci[i].delta +
2708 (data->ci[i].bar ? BAR_WIDTH : 0);
2709 D(bug("[List/MUIM_TestPos] i %d "
2710 "width %d width_sum %d mx %d\n",
2711 i, data->ci[i].entries_width, width_sum, mx));
2712 if (mx < width_sum)
2714 col = i;
2715 D(bug("[List/MUIM_TestPos] Column hit %d\n", col));
2716 break;
2722 result->entry = row;
2723 result->column = col;
2724 result->flags = flags;
2726 return TRUE;
2729 /****i*************************************************************************************
2731 NAME
2732 MUIM_DragQuery
2734 SYNOPSIS
2736 LOCATION
2737 List.mui
2739 FUNCTION
2741 NOTES
2743 EXAMPLE
2745 BUGS
2747 SEE ALSO
2749 INTERNALS
2751 *****************************************************************************************/
2753 IPTR List__MUIM_DragQuery(struct IClass *cl, Object *obj,
2754 struct MUIP_DragQuery *msg)
2756 if (msg->obj == obj)
2757 return MUIV_DragQuery_Accept;
2758 else
2759 return MUIV_DragQuery_Refuse;
2762 /****i************************************************************************************
2764 NAME
2765 MUIM_DragFinish
2767 SYNOPSIS
2769 LOCATION
2770 List.mui
2772 FUNCTION
2774 NOTES
2776 EXAMPLE
2778 BUGS
2780 SEE ALSO
2782 INTERNALS
2784 *****************************************************************************************/
2786 IPTR List__MUIM_DragFinish(struct IClass *cl, Object *obj,
2787 struct MUIP_DragFinish *msg)
2789 struct MUI_ListData *data = INST_DATA(cl, obj);
2791 data->drop_mark_y = -1;
2793 return DoSuperMethodA(cl, obj, (Msg) msg);
2797 /****i*************************************************************************************
2799 NAME
2800 MUIM_DragReport
2802 SYNOPSIS
2804 LOCATION
2805 List.mui
2807 FUNCTION
2809 NOTES
2811 EXAMPLE
2813 BUGS
2815 SEE ALSO
2817 INTERNALS
2819 *****************************************************************************************/
2821 IPTR List__MUIM_DragReport(struct IClass *cl, Object *obj,
2822 struct MUIP_DragReport *msg)
2824 struct MUI_ListData *data = INST_DATA(cl, obj);
2825 struct MUI_List_TestPos_Result pos;
2826 struct RastPort *rp = _rp(obj);
2827 LONG n, y;
2828 UWORD old_pattern;
2830 /* Choose new drop mark position */
2832 DoMethod(obj, MUIM_List_TestPos, msg->x, msg->y, (IPTR) &pos);
2833 if (pos.entry != -1)
2835 n = pos.entry;
2836 if (pos.yoffset > 0)
2837 n++;
2839 else if ((pos.flags & MUI_LPR_ABOVE) != 0)
2840 n = data->entries_first;
2841 else
2843 n = MIN(data->entries_visible, data->entries_num)
2844 - data->entries_first;
2847 /* Clear old drop mark */
2849 if ((data->flags & LIST_SHOWDROPMARKS) != 0)
2851 y = data->entries_top_pixel + (n - data->entries_first)
2852 * data->entry_maxheight;
2853 if (y != data->drop_mark_y)
2855 DoMethod(obj, MUIM_DrawBackground, _mleft(obj), data->drop_mark_y,
2856 _mwidth(obj), 1, 0, 0, 0);
2858 /* Draw new drop mark and store its position */
2860 SetABPenDrMd(rp, _pens(obj)[MPEN_SHINE], _pens(obj)[MPEN_SHADOW],
2861 JAM2);
2862 old_pattern = rp->LinePtrn;
2863 SetDrPt(rp, 0xF0F0);
2864 Move(rp, _mleft(obj), y);
2865 Draw(rp, _mright(obj), y);
2866 SetDrPt(rp, old_pattern);
2867 data->drop_mark_y = y;
2871 return TRUE;
2874 /****i*************************************************************************************
2876 NAME
2877 MUIM_DragDrop
2879 SYNOPSIS
2881 LOCATION
2882 List.mui
2884 FUNCTION
2886 NOTES
2888 EXAMPLE
2890 BUGS
2892 SEE ALSO
2894 INTERNALS
2896 *****************************************************************************************/
2898 IPTR List__MUIM_DragDrop(struct IClass *cl, Object *obj,
2899 struct MUIP_DragDrop *msg)
2901 struct MUI_ListData *data = INST_DATA(cl, obj);
2902 struct MUI_List_TestPos_Result pos;
2903 LONG n;
2905 /* Find drop position */
2907 DoMethod(obj, MUIM_List_TestPos, msg->x, msg->y, (IPTR) &pos);
2908 if (pos.entry != -1)
2910 /* Change drop position when coords move past centre of entry, not
2911 * entry boundary */
2913 n = pos.entry;
2914 if (pos.yoffset > 0)
2915 n++;
2917 /* Ensure that dropped entry will be positioned between the two
2918 * entries that are above and below the drop mark, rather than
2919 * strictly at the numeric index shown */
2921 if (n > data->entries_active)
2922 n--;
2924 else if ((pos.flags & MUI_LPR_ABOVE) != 0)
2925 n = MUIV_List_Move_Top;
2926 else
2927 n = MUIV_List_Move_Bottom;
2929 DoMethod(msg->obj, MUIM_List_Move, MUIV_List_Move_Active, n);
2931 return TRUE;
2934 /****i************************************************************************************
2936 NAME
2937 MUIM_CreateDragImage
2939 SYNOPSIS
2941 LOCATION
2942 List.mui
2944 FUNCTION
2946 NOTES
2948 EXAMPLE
2950 BUGS
2952 SEE ALSO
2954 INTERNALS
2956 *****************************************************************************************/
2958 static IPTR List__MUIM_CreateDragImage(struct IClass *cl, Object *obj,
2959 struct MUIP_CreateDragImage *msg)
2961 struct MUI_ListData *data = INST_DATA(cl, obj);
2962 BOOL success = TRUE;
2963 struct MUI_List_TestPos_Result pos;
2964 WORD width, height, left, top;
2965 struct MUI_DragImage *img = NULL;
2966 const struct ZuneFrameGfx *zframe;
2967 LONG depth;
2969 /* Get info on dragged entry */
2970 DoMethod(obj, MUIM_List_TestPos, _left(obj) - msg->touchx,
2971 _top(obj) - msg->touchy, (IPTR) &pos);
2972 if (pos.entry == -1)
2973 success = FALSE;
2975 if (success)
2977 /* Get boundaries of entry */
2978 width = _mwidth(obj);
2979 height = data->entry_maxheight;
2980 left = _mleft(obj);
2981 top = _top(obj) - msg->touchy
2982 - (pos.yoffset + data->entry_maxheight / 2);
2984 /* Allocate drag image structure */
2985 img = (struct MUI_DragImage *)
2986 AllocVec(sizeof(struct MUI_DragImage), MEMF_CLEAR);
2987 if (img == NULL)
2988 success = FALSE;
2991 if (success)
2993 /* Get drag frame */
2994 zframe = zune_zframe_get(obj,
2995 &muiGlobalInfo(obj)->mgi_Prefs->frames[MUIV_Frame_Drag]);
2997 /* Allocate drag image buffer */
2998 img->width = width + zframe->ileft + zframe->iright;
2999 img->height = height + zframe->itop + zframe->ibottom;
3000 depth = GetBitMapAttr(_screen(obj)->RastPort.BitMap, BMA_DEPTH);
3001 img->bm = AllocBitMap(img->width, img->height, depth, BMF_MINPLANES,
3002 _screen(obj)->RastPort.BitMap);
3004 if (img->bm != NULL)
3006 /* Render entry */
3007 struct RastPort temprp;
3008 InitRastPort(&temprp);
3009 temprp.BitMap = img->bm;
3010 ClipBlit(_rp(obj), left, top, &temprp,
3011 zframe->ileft, zframe->itop, width, height,
3012 0xc0);
3014 /* Render frame */
3015 struct RastPort *rp_save = muiRenderInfo(obj)->mri_RastPort;
3016 muiRenderInfo(obj)->mri_RastPort = &temprp;
3017 zframe->draw(zframe->customframe, muiRenderInfo(obj), 0, 0,
3018 img->width, img->height, 0, 0, img->width, img->height);
3019 muiRenderInfo(obj)->mri_RastPort = rp_save;
3022 /* Ensure drag point matches where user clicked */
3023 img->touchx = msg->touchx - zframe->ileft + _addleft(obj);
3024 img->touchy = -(pos.yoffset + data->entry_maxheight / 2)
3025 - zframe->itop;
3026 img->flags = 0;
3029 return (IPTR) img;
3032 /**************************************************************************
3033 Dispatcher
3034 **************************************************************************/
3035 BOOPSI_DISPATCHER(IPTR, List_Dispatcher, cl, obj, msg)
3037 switch (msg->MethodID)
3039 case OM_NEW:
3040 return List__OM_NEW(cl, obj, (struct opSet *)msg);
3041 case OM_DISPOSE:
3042 return List__OM_DISPOSE(cl, obj, msg);
3043 case OM_SET:
3044 return List__OM_SET(cl, obj, (struct opSet *)msg);
3045 case OM_GET:
3046 return List__OM_GET(cl, obj, (struct opGet *)msg);
3048 case MUIM_Setup:
3049 return List__MUIM_Setup(cl, obj, (struct MUIP_Setup *)msg);
3050 case MUIM_Cleanup:
3051 return List__MUIM_Cleanup(cl, obj, (struct MUIP_Cleanup *)msg);
3052 case MUIM_AskMinMax:
3053 return List__MUIM_AskMinMax(cl, obj, (struct MUIP_AskMinMax *)msg);
3054 case MUIM_Show:
3055 return List__MUIM_Show(cl, obj, (struct MUIP_Show *)msg);
3056 case MUIM_Hide:
3057 return List__MUIM_Hide(cl, obj, (struct MUIP_Hide *)msg);
3058 case MUIM_Draw:
3059 return List__MUIM_Draw(cl, obj, (struct MUIP_Draw *)msg);
3060 case MUIM_Layout:
3061 return List__MUIM_Layout(cl, obj, (struct MUIP_Layout *)msg);
3062 case MUIM_List_Clear:
3063 return List__MUIM_Clear(cl, obj, (struct MUIP_List_Clear *)msg);
3064 case MUIM_List_Sort:
3065 return List__MUIM_Sort(cl, obj, (struct MUIP_List_Sort *)msg);
3066 case MUIM_List_Exchange:
3067 return List__MUIM_Exchange(cl, obj,
3068 (struct MUIP_List_Exchange *)msg);
3069 case MUIM_List_Insert:
3070 return List__MUIM_Insert(cl, obj, (APTR) msg);
3071 case MUIM_List_InsertSingle:
3072 return List__MUIM_InsertSingle(cl, obj, (APTR) msg);
3073 case MUIM_List_GetEntry:
3074 return List__MUIM_GetEntry(cl, obj, (APTR) msg);
3075 case MUIM_List_Redraw:
3076 return List__MUIM_Redraw(cl, obj, (APTR) msg);
3077 case MUIM_List_Remove:
3078 return List__MUIM_Remove(cl, obj, (APTR) msg);
3079 case MUIM_List_Select:
3080 return List__MUIM_Select(cl, obj, (APTR) msg);
3081 case MUIM_List_Construct:
3082 return List__MUIM_Construct(cl, obj, (APTR) msg);
3083 case MUIM_List_Destruct:
3084 return List__MUIM_Destruct(cl, obj, (APTR) msg);
3085 case MUIM_List_Compare:
3086 return List__MUIM_Compare(cl, obj, (APTR) msg);
3087 case MUIM_List_Display:
3088 return List__MUIM_Display(cl, obj, (APTR) msg);
3089 case MUIM_List_SelectChange:
3090 return List__MUIM_SelectChange(cl, obj, (APTR) msg);
3091 case MUIM_List_CreateImage:
3092 return List__MUIM_CreateImage(cl, obj, (APTR) msg);
3093 case MUIM_List_DeleteImage:
3094 return List__MUIM_DeleteImage(cl, obj, (APTR) msg);
3095 case MUIM_List_Jump:
3096 return List__MUIM_Jump(cl, obj, (APTR) msg);
3097 case MUIM_List_Move:
3098 return List__MUIM_Move(cl, obj, (struct MUIP_List_Move *)msg);
3099 case MUIM_List_NextSelected:
3100 return List__MUIM_NextSelected(cl, obj,
3101 (struct MUIP_List_NextSelected *)msg);
3102 case MUIM_List_TestPos:
3103 return List__MUIM_TestPos(cl, obj, (APTR) msg);
3104 case MUIM_DragQuery:
3105 return List__MUIM_DragQuery(cl, obj, (APTR) msg);
3106 case MUIM_DragFinish:
3107 return List__MUIM_DragFinish(cl, obj, (APTR) msg);
3108 case MUIM_DragReport:
3109 return List__MUIM_DragReport(cl, obj, (APTR) msg);
3110 case MUIM_DragDrop:
3111 return List__MUIM_DragDrop(cl, obj, (APTR) msg);
3112 case MUIM_CreateDragImage:
3113 return List__MUIM_CreateDragImage(cl, obj, (APTR) msg);
3116 return DoSuperMethodA(cl, obj, msg);
3118 BOOPSI_DISPATCHER_END
3121 * Class descriptor.
3123 const struct __MUIBuiltinClass _MUI_List_desc =
3125 MUIC_List,
3126 MUIC_Area,
3127 sizeof(struct MUI_ListData),
3128 (void *) List_Dispatcher