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