revert between 56095 -> 55830 in arch
[AROS.git] / workbench / libs / muimaster / classes / list.c
blob2143422e61e9bcd0229ae466645ff1981449af12
1 /*
2 Copyright © 2002-2017, 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 <devices/rawkeycodes.h>
14 #include <clib/alib_protos.h>
15 #include <proto/exec.h>
16 #include <proto/graphics.h>
17 #include <proto/utility.h>
18 #include <proto/dos.h>
19 #include <proto/intuition.h>
20 #include <proto/muimaster.h>
22 /* #define MYDEBUG 1 */
23 #include "debug.h"
24 #include "mui.h"
25 #include "muimaster_intern.h"
26 #include "support.h"
27 #include "imspec.h"
28 #include "textengine.h"
29 #include "listimage.h"
30 #include "prefs.h"
32 extern struct Library *MUIMasterBase;
34 #define ENTRY_TITLE (-1)
36 #define FORMAT_TEMPLATE "DELTA=D/N,PREPARSE=P/K,WEIGHT=W/N,MINWIDTH=MIW/N," \
37 "MAXWIDTH=MAW/N,COL=C/N,BAR/S"
39 #define BAR_WIDTH 2
41 #define UPDATEMODE_ALL (1 << 0)
42 #define UPDATEMODE_ENTRY (1 << 1)
43 #define UPDATEMODE_NEEDED (1 << 2)
45 enum
47 ARG_DELTA,
48 ARG_PREPARSE,
49 ARG_WEIGHT,
50 ARG_MINWIDTH,
51 ARG_MAXWIDTH,
52 ARG_COL,
53 ARG_BAR,
54 ARG_CNT
58 struct ListEntry
60 APTR data;
61 LONG width; /* Line width */
62 LONG height; /* Line height */
63 WORD flags; /* see below */
64 LONG widths[]; /* Widths of the columns */
67 #define ENTRY_SELECTED (1<<0)
68 #define ENTRY_RENDER (1<<1)
71 struct ColumnInfo
73 int colno; /* Column number */
74 int user_width; /* user set width; -1 if entry width */
75 int min_width; /* min width percentage */
76 int max_width; /* min width percentage */
77 int weight;
78 int delta; /* ignored for the first and last column, defaults to 4 */
79 int bar;
80 STRPTR preparse;
81 int entries_width; /* width of the entries (maximum of all widths) */
84 struct MUI_ImageSpec_intern;
86 struct MUI_ListData
88 /* bool attrs */
89 ULONG flags;
91 APTR intern_pool; /* The internal pool which the class has allocated */
92 LONG intern_puddle_size;
93 LONG intern_thresh_size;
94 APTR pool; /* the pool which is used to allocate list entries */
96 struct Hook *construct_hook;
97 struct Hook *compare_hook;
98 struct Hook *destruct_hook;
99 struct Hook *display_hook;
100 struct Hook *multi_test_hook;
102 struct Hook default_compare_hook;
104 /* List management, currently we use a simple flat array, which is not
105 * good if many entries are inserted/deleted */
106 LONG entries_num; /* Number of Entries in the list */
107 LONG entries_allocated;
108 struct ListEntry **entries;
110 LONG entries_first; /* first visible entry */
111 LONG entries_visible; /* number of visible entries,
112 * determined at MUIM_Layout */
113 LONG entries_active;
114 LONG insert_position; /* pos of the last insertion */
116 LONG entry_maxheight; /* Maximum height of an entry */
117 ULONG entry_minheight; /* from MUIA_List_MinLineHeight */
119 LONG entries_totalheight;
120 LONG entries_maxwidth;
122 LONG vertprop_entries;
123 LONG vertprop_visible;
124 LONG vertprop_first;
126 LONG confirm_entries_num; /* These are the correct entries num, used
127 * so you cannot set MUIA_List_Entries to
128 * wrong values */
130 LONG entries_top_pixel; /* Where the entries start */
132 /* Column managment, is allocated by ParseListFormat() and freed
133 * by CleanListFormat() */
134 STRPTR format;
135 LONG columns; /* Number of columns the list has */
136 LONG columns_allocated; /* List has space for columns_allocated columns */
137 struct ColumnInfo *ci;
138 STRPTR *preparses;
139 STRPTR *strings_mem; /* safe pointer to allocated memory for strings[] */
140 STRPTR *strings; /* the strings for the display function, one
141 * more than needed (for the entry position) */
143 /* Titlestuff */
144 int title_height; /* The complete height of the title */
145 STRPTR title; /* On single column lists this is the title,
146 * otherwise 1. NULL for no title(s) */
148 /* Cursor images */
149 struct MUI_ImageSpec_intern *list_cursor;
150 struct MUI_ImageSpec_intern *list_select;
151 struct MUI_ImageSpec_intern *list_selcur;
153 /* Render optimization */
154 int update; /* 1 - update everything, 2 - redraw entry at update_pos,
155 * 3 - scroll to current entries_first (old value is in
156 * update_pos) */
157 int update_pos;
159 LONG drop_mark;
160 LONG drop_mark_y;
162 /* list images */
163 struct MinList images;
165 /* user prefs */
166 ListviewRefresh prefs_refresh;
167 UWORD prefs_linespacing;
168 BOOL prefs_smoothed;
169 UWORD prefs_smoothval;
171 /* render space handling */
172 Object *area;
173 BOOL area_replaced;
174 BOOL area_connected;
176 /***************************/
177 /* Former Listview members */
178 /***************************/
180 Object *vert;
181 BOOL vert_connected;
182 IPTR scroller_pos;
183 BOOL read_only;
184 IPTR multiselect;
185 LONG drag_type;
187 /* clicked column */
188 LONG click_column;
189 LONG def_click_column;
191 LONG mouse_click; /* see below if mouse is held down */
192 LONG mouse_x;
193 LONG mouse_y;
195 /* double click */
196 ULONG last_secs;
197 ULONG last_mics;
198 ULONG last_active;
200 struct MUI_EventHandlerNode ehn;
202 /* user prefs */
203 ListviewMulti prefs_multi;
205 BOOL doubleclick;
206 LONG seltype;
208 struct Hook hook;
211 #define MOUSE_CLICK_ENTRY 1 /* on entry clicked */
212 #define MOUSE_CLICK_TITLE 2 /* on title clicked */
214 #define LIST_ADJUSTWIDTH (1<<0)
215 #define LIST_ADJUSTHEIGHT (1<<1)
216 #define LIST_AUTOVISIBLE (1<<2)
217 #define LIST_DRAGSORTABLE (1<<3)
218 #define LIST_SHOWDROPMARKS (1<<4)
219 #define LIST_QUIET (1<<5)
220 #define LIST_CHANGED (1<<6)
222 static BOOL IncreaseColumns(struct MUI_ListData *data, int new_columns);
224 /****** List.mui/MUIA_List_Active ********************************************
226 * NAME
227 * MUIA_List_Active -- (V4) [ISG], LONG
229 * FUNCTION
230 * The index of the active entry. There can be at most one active entry
231 * in a list. The active entry is highlighted visibly, except for
232 * read-only lists (those whose Listview has MUIA_Listview_Input set to
233 * FALSE). Selecting an entry with the mouse, or moving through the list
234 * with keyboard controls will also change the active entry (again
235 * excepting read-only lists).
237 * When set programmatically through this attribute, some special values
238 * can be used:
240 * MUIV_List_Active_Off
241 * MUIV_List_Active_Top
242 * MUIV_List_Active_Bottom
243 * MUIV_List_Active_Up
244 * MUIV_List_Active_Down
245 * MUIV_List_Active_PageUp
246 * MUIV_List_Active_PageDown
248 * When this attribute is read, either the index of the active entry or
249 * the special value MUIV_List_Active_Off will be returned.
251 * Setting this attribute to a new value will additionally have the same
252 * effect as calling the MUIM_List_Jump method with the specified or
253 * implied index.
255 * NOTES
256 * The concept of an active entry must not be confused with that of a
257 * selected entry.
259 * SEE ALSO
260 * MUIM_List_Jump, MUIM_List_Select, MUIA_Listview_Input
262 ******************************************************************************
266 /****** List.mui/MUIA_List_AutoVisible ***************************************
268 * NAME
269 * MUIA_List_Visible -- (V11) [ISG], BOOL
271 * FUNCTION
272 * When this attribute is set to true, the active entry will be in the
273 * visible portion of the list whenever the list is unhidden.
275 * SEE ALSO
276 * MUIA_List_Active
278 ******************************************************************************
282 /****** List.mui/MUIA_List_CompareHook ***************************************
284 * NAME
285 * MUIA_List_CompareHook -- (V4) [IS.], struct Hook *
287 * FUNCTION
288 * The provided hook indicates the sort ordering of two list entries.
289 * The hook receives list-entry data pointers as its second and third
290 * arguments. The hook should return a negative value if the first entry
291 * should be placed before the second entry, a positive value if the
292 * first entry should be placed after the second entry, and zero if the
293 * entries are equal.
295 * In addition to being used internally for sorting operations, this hook
296 * will be called when MUIM_List_Compare is externally invoked.
298 * If this attribute is not specified or is set to NULL, all list entries
299 * must be strings.
301 ******************************************************************************
305 /****** List.mui/MUIA_List_ConstructHook *************************************
307 * NAME
308 * MUIA_List_ConstructHook -- (V4) [IS.], struct Hook *
310 * FUNCTION
311 * The provided hook creates a list entry. Whenever a data item is added
312 * to the list, it is the result of this hook that is really added.
314 * The hook receives the list's memory pool as its second argument and
315 * the data item for the entry as its third argument. The memory pool may
316 * be used for allocating memory for the new entry if desired. The hook
317 * should return the new data entry, or NULL if an error occurred.
319 * If this attribute is not specified or is set to NULL, all list entries
320 * must be strings (which must continue to exist for the lifetime of the
321 * list).
323 * If you want string entries to be duplicated within the list, so that
324 * they do not have to be preserved externally, a built-in hook for this
325 * purpose can be used by providing the special value
326 * MUIV_List_ConstructHook_String.
328 * Whenever this attribute is specified, a matching hook must be
329 * specified for the MUIA_ListDestructHook attribute.
331 * SEE ALSO
332 * MUIA_List_DestructHook
334 ******************************************************************************
338 /****** List.mui/MUIA_List_DestructHook **************************************
340 * NAME
341 * MUIA_List_DestructHook -- (V4) [IS.], struct Hook *
343 * FUNCTION
344 * The provided hook destroys a list entry, and is called whenever a data
345 * item is removed from the list. It can be used to deallocate any
346 * resources for the entry that were allocated by the hook specified for
347 * MUIA_List_ConstructHook.
349 * The hook receives the list's memory pool as its second argument and
350 * the list entry to be destroyed as its third argument. The hook should
351 * have no return value.
353 * If this attribute is not specified or is set to NULL, all list entries
354 * must be strings (which must continue to exist for the lifetime of the
355 * list).
357 * If you want string entries to be duplicated within the list, so that
358 * they do not have to be preserved externally, a built-in hook for this
359 * purpose can be used by providing the special value
360 * MUIV_List_ConstructHook_String.
362 * This attribute must only be specified when a matching hook has been
363 * specified for the MUIA_List_ConstructHook attribute. If you specified
364 * MUIV_List_ConstructHook_String for MUIA_List_ConstructHook, you must
365 * specify MUIV_List_DestructHook_String for this attribute.
367 * SEE ALSO
368 * MUIA_List_ConstructHook
370 ******************************************************************************
374 /****** List.mui/MUIA_List_DropMark ******************************************
376 * NAME
377 * MUIA_List_DropMark -- (V11) [..G], LONG
379 * FUNCTION
380 * Provides the index of the last position where a list entry was
381 * successfully dropped. The initial value before any entry has been
382 * dropped is undefined.
384 * SEE ALSO
385 * MUIA_List_DragSortable, MUIA_List_ShowDropMarks,
386 * MUIA_Listview_Draggable
388 ******************************************************************************
392 /****** List.mui/MUIA_List_First *********************************************
394 * NAME
395 * MUIA_List_First -- (V4) [..G], LONG
397 * FUNCTION
398 * The index of the first entry that can be seen (assuming nothing
399 * obscures the list) This value of this attribute is -1 when the
400 * list's window is not open.
402 * NOTES
403 * Notification does not occur on this attribute in MUI.
405 * SEE ALSO
406 * MUIA_List_First, MUIA_List_Entries
408 ******************************************************************************
412 /****** List.mui/MUIA_List_MultiTestHook *************************************
414 * NAME
415 * MUIA_List_MultiTestHook -- (V4) [IS.], struct Hook *
417 * FUNCTION
418 * The provided hook indicates whether a particular list entry
419 * may be multiselected. The hook receives the list-entry data pointer as
420 * its third argument, and returns a Boolean value. If this attribute is
421 * not specified or is set to NULL, all list entries are considered
422 * multi-selectable.
424 * Whenever an entry is about to be selected, this hook is called if
425 * there are other entries already selected. If the hook returns TRUE,
426 * the entry may be multi-selected; if the hook returns FALSE, the entry
427 * remains unselected.
429 * Additionally, if a non-multi-selectable entry has been selected (as
430 * the only selected entry in the list), any attempt to select an
431 * additional entry will fail.
433 ******************************************************************************
437 /****** List.mui/MUIA_List_ShowDropMarks *************************************
439 * NAME
440 * MUIA_List_ShowDropMarks -- (V11) [ISG], BOOL
442 * FUNCTION
443 * Specifies whether a visual indication will be shown of where a list
444 * item will be inserted if dropped during a drag-and-drop operation.
445 * Defaults to TRUE. Only has an affect when the list is a drop target
446 * (e.g. when MUIA_List_DragSortable is TRUE).
448 * SEE ALSO
449 * MUIA_List_DragSortable, MUIA_List_DropMark, MUIA_Listview_Draggable
451 ******************************************************************************
455 /****** List.mui/MUIA_List_Title *********************************************
457 * NAME
458 * MUIA_List_Title -- (V6) [ISG], char *
460 * FUNCTION
461 * A heading for the list, placed above list entries. A value of NULL
462 * means no title is used. A value of TRUE means that the custom
463 * display hook provides a separate title for each column; the hook
464 * must then provide column titles instead of normal column data when
465 * the entry pointer provided is NULL.
467 * NOTES
468 * If a string is set for this attribute, it is not cached within the
469 * object.
471 * SEE ALSO
472 * MUIA_List_DisplayHook
474 ******************************************************************************
478 /****** List.mui/MUIA_List_Visible *******************************************
480 * NAME
481 * MUIA_List_Visible -- (V4) [..G], LONG
483 * FUNCTION
484 * The number of entries that can be seen at once with the list's
485 * current dimensions. This value of this attribute is -1 when the
486 * list's window is not open.
488 * NOTES
489 * Notification does not occur on this attribute in MUI.
491 * SEE ALSO
492 * MUIA_List_First, MUIA_List_Entries
494 ******************************************************************************
498 /**************************************************************************
499 Allocate a single list entry, does not initialize it (except the pointer)
500 **************************************************************************/
501 static struct ListEntry *AllocListEntry(struct MUI_ListData *data)
503 struct ListEntry *le;
504 /* use IncreaseColumns() to enlarge column entry array */
505 IPTR size = sizeof(struct ListEntry) + sizeof(LONG) * (data->columns + 1);
507 le = (struct ListEntry *) AllocVecPooled(data->pool, size);
508 D(bug("List AllocListEntry %p, %ld bytes\n", le, size));
509 if (le)
511 /* possible, that we have an external pool, which does not have
512 MEMF_CLEAR set.. */
513 memset(le, 0, size);
515 return le;
518 /**************************************************************************
519 Deallocate a single list entry, does not deinitialize it
520 **************************************************************************/
521 static void FreeListEntry(struct MUI_ListData *data,
522 struct ListEntry *entry)
524 D(bug("FreeListEntry %p\n", entry));
525 FreeVecPooled(data->pool, entry);
528 /**************************************************************************
529 Ensures that there can be at least the given amount of entries within
530 the list. Returns 0 if not. It also allocates the space for the title.
531 It can be accessed with data->entries[ENTRY_TITLE]
532 **************************************************************************/
533 static int SetListSize(struct MUI_ListData *data, LONG size)
535 struct ListEntry **new_entries;
536 int new_entries_allocated;
538 if (size + 1 <= data->entries_allocated)
539 return 1;
541 new_entries_allocated = data->entries_allocated * 2 + 4;
542 if (new_entries_allocated < size + 1)
543 new_entries_allocated = size + 1 + 10; /* 10 is just random */
545 D(bug("List %p : SetListSize allocating %ld bytes\n", data,
546 new_entries_allocated * sizeof(struct ListEntry *)));
547 new_entries =
548 AllocVec(new_entries_allocated * sizeof(struct ListEntry *), 0);
549 if (NULL == new_entries)
550 return 0;
551 if (data->entries)
553 CopyMem(data->entries - 1, new_entries,
554 (data->entries_num + 1) * sizeof(struct ListEntry *));
555 FreeVec(data->entries - 1);
557 data->entries = new_entries + 1;
558 data->entries_allocated = new_entries_allocated;
559 return 1;
562 /**************************************************************************
563 Prepares the insertion of count entries at pos.
564 This function doesn't care if there is enough space in the datastructure.
565 SetListSize() must be used first.
566 With current implementation, this call will never fail
567 **************************************************************************/
568 static int PrepareInsertListEntries(struct MUI_ListData *data, int pos,
569 int count)
571 memmove(&data->entries[pos + count], &data->entries[pos],
572 (data->entries_num - pos) * sizeof(struct ListEntry *));
573 return 1;
576 /**************************************************************************
577 Removes count (already deinitalized) list entries starting az pos.
578 **************************************************************************/
579 static void RemoveListEntries(struct MUI_ListData *data, int pos, int count)
581 // FIXME: segfault if entries_num = pos = count = 1
582 memmove(&data->entries[pos], &data->entries[pos + count],
583 (data->entries_num - (pos + count)) * sizeof(struct ListEntry *));
586 /**************************************************************************
587 Frees all memory allocated by ParseListFormat()
588 **************************************************************************/
589 static void FreeListFormat(struct MUI_ListData *data)
591 int i;
593 if (data->ci)
595 for (i = 0; i < data->columns; i++)
597 FreeVec(data->ci[i].preparse);
598 data->ci[i].preparse = NULL;
600 FreeVec(data->ci);
601 data->ci = NULL;
603 FreeVec(data->preparses);
604 data->preparses = NULL;
605 if (data->strings_mem)
607 FreeVec(data->strings_mem);
608 data->strings_mem = NULL;
609 data->strings = NULL;
611 data->columns = 0;
614 /**************************************************************************
615 Parses the given format string (also frees a previously parsed format).
616 Use initial=FALSE for format changes outside OM_NEW.
617 Return FALSE on failure.
618 **************************************************************************/
619 static BOOL ParseListFormat(struct MUI_ListData *data, STRPTR format,
620 BOOL initial)
622 int new_columns, i;
623 STRPTR ptr;
624 STRPTR format_sep;
625 char c;
627 IPTR args[ARG_CNT];
628 struct RDArgs *rdargs;
630 if (!format)
631 format = (STRPTR) "";
633 ptr = format;
635 FreeListFormat(data);
637 new_columns = 1;
639 /* Count the number of columns first */
640 while ((c = *ptr++))
641 if (c == ',')
642 new_columns++;
644 if (!(data->preparses =
645 AllocVec((new_columns + 10) * sizeof(STRPTR), MEMF_CLEAR)))
646 return FALSE;
648 if (!(data->strings_mem = AllocVec((new_columns + 1 + 10)
649 * sizeof(STRPTR), MEMF_CLEAR)))
650 /* hold enough space also for the entry pos,
651 * used by orginal MUI and also some
652 * security space */
653 return FALSE;
654 data->strings=data->strings_mem;
656 if (!(data->ci = AllocVec(new_columns * sizeof(struct ColumnInfo), MEMF_CLEAR)))
657 return FALSE;
659 // set defaults
660 for (i = 0; i < new_columns; i++)
662 data->ci[i].colno = -1; // -1 means: use unassigned column
663 data->ci[i].weight = 100;
664 data->ci[i].delta = 4;
665 data->ci[i].min_width = -1;
666 data->ci[i].max_width = -1;
667 data->ci[i].user_width = -1;
668 data->ci[i].bar = FALSE;
669 data->ci[i].preparse = NULL;
672 if ((format_sep = StrDup(format)) != 0)
674 for (i = 0; format_sep[i] != '\0'; i++)
676 if (format_sep[i] == ',')
677 format_sep[i] = '\0';
680 if ((rdargs = AllocDosObject(DOS_RDARGS, NULL)) != 0)
682 ptr = format_sep;
683 i = 0;
686 rdargs->RDA_Source.CS_Buffer = ptr;
687 rdargs->RDA_Source.CS_Length = strlen(ptr);
688 rdargs->RDA_Source.CS_CurChr = 0;
689 rdargs->RDA_DAList = 0;
690 rdargs->RDA_Buffer = NULL;
691 rdargs->RDA_BufSiz = 0;
692 rdargs->RDA_ExtHelp = NULL;
693 rdargs->RDA_Flags = 0;
695 memset(args, 0, sizeof args);
696 if (ReadArgs(FORMAT_TEMPLATE, args, rdargs))
698 if (args[ARG_COL])
699 data->ci[i].colno = *(LONG *) args[ARG_COL];
700 if (args[ARG_WEIGHT])
701 data->ci[i].weight = *(LONG *) args[ARG_WEIGHT];
702 if (args[ARG_DELTA])
703 data->ci[i].delta = *(LONG *) args[ARG_DELTA];
704 if (args[ARG_MINWIDTH])
705 data->ci[i].min_width =
706 *(LONG *) args[ARG_MINWIDTH];
707 if (args[ARG_MAXWIDTH])
708 data->ci[i].max_width =
709 *(LONG *) args[ARG_MAXWIDTH];
710 data->ci[i].bar = args[ARG_BAR];
711 if (args[ARG_PREPARSE])
712 data->ci[i].preparse =
713 StrDup((STRPTR) args[ARG_PREPARSE]);
715 FreeArgs(rdargs);
717 ptr += strlen(ptr) + 1;
718 i++;
720 while (i < new_columns);
721 FreeDosObject(DOS_RDARGS, rdargs);
723 FreeVec(format_sep);
726 for (i = 0; i < new_columns; i++)
728 D(bug("colno %d weight %d delta %d preparse %s\n",
729 data->ci[i].colno, data->ci[i].weight, data->ci[i].delta,
730 data->ci[i].preparse));
733 if (initial)
735 /* called from OM_NEW */
736 data->columns_allocated = new_columns;
738 else if (data->columns_allocated < new_columns)
740 /* called by MUIA_List_Format */
741 if (!IncreaseColumns(data, new_columns))
743 bug("[Zune:List] not enough memory for new columns!!\n");
744 /* FIXME: proper handling? */
745 return FALSE;
747 data->columns_allocated = new_columns;
749 data->columns = new_columns;
750 data->strings++; /* Skip entry pos */
752 return TRUE;
755 /**************************************************************************
756 Call the MUIM_List_Display for the given entry. It fills out
757 data->string and data->preparses
758 **************************************************************************/
759 static void DisplayEntry(struct IClass *cl, Object *obj, int entry_pos)
761 struct MUI_ListData *data = INST_DATA(cl, obj);
762 APTR entry_data;
763 int col;
765 for (col = 0; col < data->columns; col++)
766 data->preparses[col] = data->ci[col].preparse;
768 if (entry_pos == ENTRY_TITLE)
770 if ((data->columns == 1) && (data->title != (STRPTR) 1))
772 *data->strings = data->title;
773 return;
775 entry_data = NULL; /* it's a title request */
777 else
778 entry_data = data->entries[entry_pos]->data;
780 /* Get the display formation */
781 DoMethod(obj, MUIM_List_Display, (IPTR) entry_data,
782 (IPTR) data->strings, entry_pos, (IPTR) data->preparses);
785 /**************************************************************************
786 Determine the dims of a single entry and adapt the columninfo according
787 to it. pos might be ENTRY_TITLE. Returns 0 if pos entry needs to
788 be redrawn after this operation, 1 if all entries need to be redrawn.
789 **************************************************************************/
790 static int CalcDimsOfEntry(struct IClass *cl, Object *obj, int pos)
792 struct MUI_ListData *data = INST_DATA(cl, obj);
793 struct ListEntry *entry = data->entries[pos];
794 int j;
795 int ret = 0;
797 if (!entry)
798 return ret;
800 if (!(_flags(obj) & MADF_SETUP))
801 return ret;
803 DisplayEntry(cl, obj, pos);
805 /* Set height to at least minheight */
806 if (data->entries[pos]->height < data->entry_minheight)
807 data->entries[pos]->height = data->entry_minheight;
809 for (j = 0; j < data->columns; j++)
811 ZText *text =
812 zune_text_new(data->preparses[j], data->strings[j],
813 ZTEXT_ARG_NONE, 0);
814 if (text != NULL)
816 zune_text_get_bounds(text, obj);
818 if (text->height > data->entries[pos]->height)
820 data->entries[pos]->height = text->height;
821 /* entry height changed, redraw all entries later */
822 ret = 1;
824 data->entries[pos]->widths[j] = text->width;
826 if (text->width > data->ci[j].entries_width)
828 /* This entry has a greater width for this column than any
829 * other entry, so we store this value
831 data->ci[j].entries_width = text->width;
832 /* column width changed, redraw all entries later */
833 ret = 1;
836 zune_text_destroy(text);
839 if (data->entries[pos]->height > data->entry_maxheight)
841 data->entry_maxheight = data->entries[pos]->height;
842 /* maximum entry height changed, redraw all entries later */
843 ret = 1;
846 return ret;
849 /**************************************************************************
850 Determine the widths of the entries
851 **************************************************************************/
852 static void CalcWidths(struct IClass *cl, Object *obj)
854 int i, j;
855 struct MUI_ListData *data = INST_DATA(cl, obj);
857 if (!(_flags(obj) & MADF_SETUP))
858 return;
860 for (j = 0; j < data->columns; j++)
861 data->ci[j].entries_width = 0;
863 data->entry_maxheight = 0;
864 data->entries_totalheight = 0;
865 data->entries_maxwidth = 0;
867 for (i = (data->title ? ENTRY_TITLE : 0); i < data->entries_num; i++)
869 CalcDimsOfEntry(cl, obj, i);
870 data->entries_totalheight += data->entries[i]->height;
873 for (j = 0; j < data->columns; j++)
874 data->entries_maxwidth += data->ci[j].entries_width
875 + data->ci[j].delta + (data->ci[j].bar ? BAR_WIDTH : 0);
877 if (!data->entry_maxheight)
878 data->entry_maxheight = 1;
881 /**************************************************************************
882 Calculates the number of visible entry lines. Returns 1 if it has
883 changed
884 **************************************************************************/
885 static int CalcVertVisible(struct IClass *cl, Object *obj)
887 struct MUI_ListData *data = INST_DATA(cl, obj);
888 int old_entries_visible = data->entries_visible;
889 int old_entries_top_pixel = data->entries_top_pixel;
891 data->vertprop_visible = data->entries_visible =
892 (_mheight(data->area) - data->title_height)
893 / (data->entry_maxheight /* + data->prefs_linespacing */ );
895 /* Distribute extra vertical space evenly between top and bottom of
896 * list */
898 data->entries_top_pixel = _mtop(data->area) + data->title_height
899 + (_mheight(data->area) - data->title_height
901 data->entries_visible *
902 (data->entry_maxheight /* + data->prefs_linespacing */ )) / 2;
904 if (data->entries_visible != old_entries_visible)
906 superset(cl, obj, MUIA_List_Visible, data->entries_visible);
907 superset(cl, obj, MUIA_List_VertProp_Visible, data->entries_visible);
910 return (old_entries_visible != data->entries_visible)
911 || (old_entries_top_pixel != data->entries_top_pixel);
914 /**************************************************************************
915 Resize arrays, so that there is enough space for new columns.
916 The widths[] array must grow with increasing columns.
917 columns_allocated must be checked, before calling this function.
918 Space can only grow, not shrink.
919 Return FALSE on error (no memory).
920 **************************************************************************/
921 static BOOL IncreaseColumns(struct MUI_ListData *data, int new_columns)
923 int i = 0;
924 IPTR newsize, oldsize;
925 struct ListEntry *le;
927 D(bug("IncreaseColumns: %d => %d columns\n", data->columns_allocated, new_columns));
929 newsize = sizeof(struct ListEntry) + sizeof(LONG) * (new_columns + 1);
930 oldsize = sizeof(struct ListEntry) + sizeof(LONG) * (data->columns_allocated + 1);
932 if (data->title)
934 i = -1;
937 for (; i < data->entries_num; i++)
939 D(bug("IncreaseColumns: i: %d, size: %d => %d\n", i, oldsize, newsize));
940 le = (struct ListEntry *) AllocVecPooled(data->pool, newsize);
941 if (!le)
943 return FALSE;
945 memset(le, 0, newsize);
946 D(bug("IncreaseColumns: CopyMem(%p, %p, %d)\n", data->entries[i],
947 le, oldsize));
948 CopyMem(data->entries[i], le, oldsize);
949 FreeVecPooled(data->pool, data->entries[i]);
950 data->entries[i]=le;
953 return TRUE;
956 /**************************************************************************
957 Default hook to compare two list entries. Works for strings only.
958 **************************************************************************/
959 AROS_UFH3S(int, default_compare_func,
960 AROS_UFHA(struct Hook *, h, A0),
961 AROS_UFHA(char *, s2, A2),
962 AROS_UFHA(char *, s1, A1))
964 AROS_USERFUNC_INIT
966 return Stricmp(s1, s2);
968 AROS_USERFUNC_EXIT
971 #define PROP_VERT_FIRST 1
973 static ULONG List_Function(struct Hook *hook, Object * obj, void **msg)
975 struct MUI_ListData *data = (struct MUI_ListData *)hook->h_Data;
976 SIPTR type = (SIPTR) msg[0];
977 SIPTR val = (SIPTR) msg[1];
979 switch (type)
981 case PROP_VERT_FIRST:
982 get(data->vert, MUIA_Prop_First, &val);
983 nnset(obj, MUIA_List_VertProp_First, val);
984 break;
986 return 0;
989 /* At entry to this function, data->area is always set, but data->vert may
990 * or may not be set */
991 static void List_HandleScrollerPos(struct IClass *cl, Object *obj)
993 struct MUI_ListData *data = INST_DATA(cl, obj);
994 BOOL vert_not_used = FALSE;
996 /* Disallow any changes after setup. This function should basically be
997 * creation-time only */
998 if (_flags(obj) & MADF_SETUP)
999 return;
1001 /* Remove both objects */
1002 if (data->area_connected)
1003 DoMethod(obj, OM_REMMEMBER, data->area);
1004 if (data->vert_connected)
1005 DoMethod(obj, OM_REMMEMBER, data->vert);
1007 /* Add list and/or scroller */
1008 switch (data->scroller_pos)
1010 case MUIV_Listview_ScrollerPos_None:
1011 vert_not_used = TRUE;
1012 DoMethod(obj, OM_ADDMEMBER, data->area);
1013 break;
1014 case MUIV_Listview_ScrollerPos_Left:
1015 if (!data->vert)
1016 data->vert =ScrollbarObject, MUIA_Group_Horiz, FALSE, End;
1017 DoMethod(obj, OM_ADDMEMBER, data->vert);
1018 DoMethod(obj, OM_ADDMEMBER, data->area);
1019 break;
1020 default:
1021 if (!data->vert)
1022 data->vert = ScrollbarObject, MUIA_Group_Horiz, FALSE, End;
1023 DoMethod(obj, OM_ADDMEMBER, data->area);
1024 DoMethod(obj, OM_ADDMEMBER, data->vert);
1025 break;
1028 data->area_connected = TRUE;
1030 /* Handle case where it was decided that vert will not be used */
1031 if (vert_not_used)
1033 if (data->vert)
1035 if (data->vert_connected)
1037 DoMethod(obj, MUIM_KillNotifyObj, MUIA_List_VertProp_First,
1038 (IPTR) data->vert);
1039 DoMethod(obj, MUIM_KillNotifyObj, MUIA_List_VertProp_Visible,
1040 (IPTR) data->vert);
1041 DoMethod(obj, MUIM_KillNotifyObj, MUIA_List_VertProp_Entries,
1042 (IPTR) data->vert);
1043 data->vert_connected = FALSE;
1046 MUI_DisposeObject(data->vert);
1047 data->vert = NULL;
1051 /* If at this point data->vert is not null, it means vert is to be
1052 * connected */
1053 if (data->vert && !data->vert_connected)
1055 LONG entries = 0, first = 0, visible = 0;
1057 get(obj, MUIA_List_VertProp_First, &first);
1058 get(obj, MUIA_List_VertProp_Visible, &visible);
1059 get(obj, MUIA_List_VertProp_Entries, &entries);
1061 SetAttrs(data->vert, MUIA_Prop_First, first,
1062 MUIA_Prop_Visible, visible, MUIA_Prop_Entries, entries, TAG_DONE);
1064 DoMethod(data->vert, MUIM_Notify, MUIA_Prop_First, MUIV_EveryTime,
1065 (IPTR) obj, 4, MUIM_CallHook, (IPTR) &data->hook, PROP_VERT_FIRST,
1066 MUIV_TriggerValue);
1068 /* Pass prop object as DestObj (based on code in NList) */
1069 DoMethod(obj, MUIM_Notify, MUIA_List_VertProp_First, MUIV_EveryTime,
1070 (IPTR) data->vert, 3, MUIM_NoNotifySet,
1071 MUIA_Prop_First, MUIV_TriggerValue);
1072 DoMethod(obj, MUIM_Notify, MUIA_List_VertProp_Visible, MUIV_EveryTime,
1073 (IPTR) data->vert, 3, MUIM_NoNotifySet,
1074 MUIA_Prop_Visible, MUIV_TriggerValue);
1075 DoMethod(obj, MUIM_Notify, MUIA_List_VertProp_Entries, MUIV_EveryTime,
1076 (IPTR) data->vert, 3, MUIM_NoNotifySet,
1077 MUIA_Prop_Entries, MUIV_TriggerValue);
1079 data->vert_connected = TRUE;
1083 /**************************************************************************
1084 OM_NEW
1085 **************************************************************************/
1086 IPTR List__OM_NEW(struct IClass *cl, Object *obj, struct opSet *msg)
1088 struct MUI_ListData *data;
1089 struct TagItem *tag;
1090 struct TagItem *tags;
1091 APTR *array = NULL;
1092 LONG new_entries_active = MUIV_List_Active_Off;
1093 struct TagItem rectattrs[2] =
1094 {{TAG_IGNORE, TAG_IGNORE }, {TAG_DONE, TAG_DONE}};
1095 Object *area;
1097 /* search for MUIA_Frame as it has to be passed to rectangle object */
1098 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
1100 if (tag->ti_Tag == MUIA_Frame)
1102 rectattrs[0].ti_Tag = MUIA_Frame;
1103 rectattrs[0].ti_Data = tag->ti_Data;
1104 tag->ti_Tag = TAG_IGNORE;
1105 break;
1109 obj = (Object *) DoSuperNewTags(cl, obj, NULL,
1110 MUIA_Group_Horiz, TRUE,
1111 MUIA_InnerLeft, 0,
1112 MUIA_InnerRight, 0,
1113 MUIA_Group_Spacing, 0,
1114 MUIA_Font, MUIV_Font_List,
1115 MUIA_ShowSelState, FALSE,
1116 MUIA_InputMode, MUIV_InputMode_RelVerify,
1117 MUIA_Background, MUII_ListBack,
1118 TAG_MORE, (IPTR) msg->ops_AttrList,
1119 TAG_DONE);
1121 if (!obj)
1122 return FALSE;
1124 data = INST_DATA(cl, obj);
1126 data->columns = 1;
1127 data->entries_active = MUIV_List_Active_Off;
1128 data->intern_puddle_size = 2008;
1129 data->intern_thresh_size = 1024;
1130 data->default_compare_hook.h_Entry = (HOOKFUNC) default_compare_func;
1131 data->default_compare_hook.h_SubEntry = 0;
1132 data->compare_hook = &(data->default_compare_hook);
1133 data->flags = LIST_SHOWDROPMARKS;
1134 data->area_replaced = FALSE;
1135 data->area_connected = FALSE;
1136 data->vert_connected = FALSE;
1138 data->entries_visible = data->vertprop_visible = -1;
1139 data->last_active = -1;
1140 data->drop_mark = 0;
1142 data->ehn.ehn_Events = IDCMP_MOUSEBUTTONS | IDCMP_RAWKEY;
1143 data->ehn.ehn_Priority = 0;
1144 data->ehn.ehn_Flags = 0;
1145 data->ehn.ehn_Object = obj;
1146 data->ehn.ehn_Class = cl;
1148 data->mouse_click = 0;
1149 data->mouse_x = MUI_MAXMAX;
1150 data->mouse_y = MUI_MAXMAX;
1152 /* HACK:
1153 * List is a group where part of area is rendered and part is filled
1154 * with other objects (inside of List dimensions). One such object is
1155 * up/down arrow. This object depends on RelVerify mode to control
1156 * behaviour. List also has the RelVerify mode. Area super class in case
1157 * of both of those objects adds an event handler node with the same
1158 * priority. Depending on the sort order, the event handler node of
1159 * "list" can eat a click event and up/down arrows stop working. The
1160 * hack is to decrease the priority of Area event handler for list to
1161 * always favor up/down arrow. There are other hacky ways of solving
1162 * this, but this seems least evil approach, as this hack is
1163 * encapsulated in the List class itself.
1165 muiAreaData(obj)->mad_ehn.ehn_Priority--;
1167 data->hook.h_Entry = HookEntry;
1168 data->hook.h_SubEntry = (HOOKFUNC) List_Function;
1169 data->hook.h_Data = data;
1171 area = (Object *)GetTagData(MUIA_List_ListArea, (IPTR) 0,
1172 msg->ops_AttrList);
1174 if (!area)
1175 area = RectangleObject, MUIA_FillArea, FALSE, TAG_MORE,
1176 (IPTR) rectattrs, End;
1177 else
1178 data->area_replaced = TRUE;
1179 data->area = area;
1181 /* parse initial taglist */
1182 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
1184 switch (tag->ti_Tag)
1186 case MUIA_List_Active:
1187 new_entries_active = tag->ti_Data;
1188 break;
1190 case MUIA_List_Pool:
1191 data->pool = (APTR) tag->ti_Data;
1192 break;
1194 case MUIA_List_PoolPuddleSize:
1195 data->intern_puddle_size = tag->ti_Data;
1196 break;
1198 case MUIA_List_PoolThreshSize:
1199 data->intern_thresh_size = tag->ti_Data;
1200 break;
1202 case MUIA_List_CompareHook:
1203 data->compare_hook = (struct Hook *)tag->ti_Data;
1204 if (data->compare_hook == NULL)
1205 data->compare_hook = &data->default_compare_hook;
1206 break;
1208 case MUIA_List_ConstructHook:
1209 data->construct_hook = (struct Hook *)tag->ti_Data;
1210 break;
1212 case MUIA_List_DestructHook:
1213 data->destruct_hook = (struct Hook *)tag->ti_Data;
1214 break;
1216 case MUIA_List_DisplayHook:
1217 data->display_hook = (struct Hook *)tag->ti_Data;
1218 break;
1220 case MUIA_List_MultiTestHook:
1221 data->multi_test_hook = (struct Hook *)tag->ti_Data;
1222 break;
1224 case MUIA_List_SourceArray:
1225 array = (APTR *) tag->ti_Data;
1226 break;
1228 case MUIA_List_Format:
1229 data->format = (STRPTR) tag->ti_Data;
1230 break;
1232 case MUIA_List_Title:
1233 data->title = (STRPTR) tag->ti_Data;
1234 break;
1236 case MUIA_List_MinLineHeight:
1237 data->entry_minheight = tag->ti_Data;
1238 break;
1240 case MUIA_List_AdjustHeight:
1241 _handle_bool_tag(data->flags, tag->ti_Data, LIST_ADJUSTHEIGHT);
1242 break;
1244 case MUIA_List_AdjustWidth:
1245 _handle_bool_tag(data->flags, tag->ti_Data, LIST_ADJUSTWIDTH);
1246 break;
1248 case MUIA_List_AutoVisible:
1249 _handle_bool_tag(data->flags, tag->ti_Data, LIST_AUTOVISIBLE);
1250 break;
1252 case MUIA_List_ShowDropMarks:
1253 _handle_bool_tag(data->flags, tag->ti_Data, LIST_SHOWDROPMARKS);
1254 break;
1256 case MUIA_List_DragSortable:
1257 _handle_bool_tag(data->flags, tag->ti_Data, LIST_DRAGSORTABLE);
1258 break;
1260 case MUIA_Listview_ScrollerPos:
1261 data->scroller_pos = tag->ti_Data;
1262 break;
1264 case MUIA_Listview_Input:
1265 data->read_only = !tag->ti_Data;
1266 break;
1268 case MUIA_Listview_MultiSelect:
1269 data->multiselect = tag->ti_Data;
1270 break;
1272 case MUIA_Listview_DefClickColumn:
1273 data->def_click_column = tag->ti_Data;
1274 break;
1276 case MUIA_Listview_DragType:
1277 data->drag_type = tag->ti_Data;
1278 if (data->drag_type != MUIV_Listview_DragType_None)
1279 set(obj, MUIA_Draggable, TRUE);
1280 break;
1284 List_HandleScrollerPos(cl, obj);
1286 if (!data->pool)
1288 /* No memory pool given, so we create our own */
1289 data->pool = data->intern_pool =
1290 CreatePool(0, data->intern_puddle_size,
1291 data->intern_thresh_size);
1292 if (!data->pool)
1294 CoerceMethod(cl, obj, OM_DISPOSE);
1295 return 0;
1299 /* parse the list format */
1300 if (!ParseListFormat(data, data->format, TRUE))
1302 CoerceMethod(cl, obj, OM_DISPOSE);
1303 return 0;
1306 /* This is necessary for at least the title */
1307 if (!SetListSize(data, 0))
1309 CoerceMethod(cl, obj, OM_DISPOSE);
1310 return 0;
1313 if (!(data->entries[ENTRY_TITLE] = AllocListEntry(data)))
1315 CoerceMethod(cl, obj, OM_DISPOSE);
1316 return 0;
1319 if (array)
1321 int i;
1322 /* Count the number of elements */
1323 for (i = 0; array[i] != NULL; i++)
1325 /* Insert them */
1326 DoMethod(obj, MUIM_List_Insert, (IPTR) array, i,
1327 MUIV_List_Insert_Top);
1330 if ((data->entries_num) && (new_entries_active != MUIV_List_Active_Off))
1332 switch (new_entries_active)
1334 case MUIV_List_Active_Top:
1335 new_entries_active = 0;
1336 break;
1338 case MUIV_List_Active_Bottom:
1339 new_entries_active = data->entries_num - 1;
1340 break;
1343 if (new_entries_active < 0)
1344 new_entries_active = 0;
1345 else if (new_entries_active >= data->entries_num)
1346 new_entries_active = data->entries_num - 1;
1348 data->entries_active = new_entries_active;
1349 /* Selected entry will be moved into visible area */
1352 NewList((struct List *)&data->images);
1354 D(bug("List_New(%lx)\n", obj));
1356 return (IPTR) obj;
1359 /**************************************************************************
1360 OM_DISPOSE
1361 **************************************************************************/
1362 IPTR List__OM_DISPOSE(struct IClass *cl, Object *obj, Msg msg)
1364 struct MUI_ListData *data = INST_DATA(cl, obj);
1366 D(bug("List Dispose\n"));
1368 /* Call destruct method for every entry and free the entries manually
1369 * to avoid notification */
1370 while (data->confirm_entries_num)
1372 struct ListEntry *lentry =
1373 data->entries[--data->confirm_entries_num];
1374 DoMethod(obj, MUIM_List_Destruct, (IPTR) lentry->data,
1375 (IPTR) data->pool);
1376 FreeListEntry(data, lentry);
1379 if (data->intern_pool)
1380 DeletePool(data->intern_pool);
1381 if (data->entries)
1382 FreeVec(data->entries - 1);
1383 /* title is currently before all other elements */
1385 FreeListFormat(data);
1387 return DoSuperMethodA(cl, obj, msg);
1391 /**************************************************************************
1392 OM_SET
1393 **************************************************************************/
1394 IPTR List__OM_SET(struct IClass *cl, Object *obj, struct opSet *msg)
1396 struct MUI_ListData *data = INST_DATA(cl, obj);
1397 struct TagItem *tag;
1398 struct TagItem *tags;
1400 /* parse taglist */
1401 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
1403 switch (tag->ti_Tag)
1405 case MUIA_List_CompareHook:
1406 data->compare_hook = (struct Hook *)tag->ti_Data;
1407 if (data->compare_hook == NULL)
1408 data->compare_hook = &data->default_compare_hook;
1409 break;
1411 case MUIA_List_ConstructHook:
1412 data->construct_hook = (struct Hook *)tag->ti_Data;
1413 break;
1415 case MUIA_List_DestructHook:
1416 data->destruct_hook = (struct Hook *)tag->ti_Data;
1417 break;
1419 case MUIA_List_DisplayHook:
1420 data->display_hook = (struct Hook *)tag->ti_Data;
1421 break;
1423 case MUIA_List_MultiTestHook:
1424 data->multi_test_hook = (struct Hook *)tag->ti_Data;
1425 if (data->multi_test_hook != NULL)
1427 /* Clearing current selections is the easiest way to keep
1428 * selections consistent with the new hook */
1429 DoMethod(obj, MUIM_List_Select, MUIV_List_Select_All,
1430 MUIV_List_Select_Off, NULL);
1432 break;
1434 case MUIA_List_Title:
1435 data->title = (STRPTR) tag->ti_Data;
1436 DoMethod(obj, MUIM_List_Redraw, MUIV_List_Redraw_All);
1437 break;
1439 case MUIA_List_VertProp_First:
1440 data->vertprop_first = tag->ti_Data;
1441 if (data->entries_first != tag->ti_Data)
1443 set(obj, MUIA_List_First, tag->ti_Data);
1445 break;
1447 case MUIA_List_Format:
1448 data->format = (STRPTR) tag->ti_Data;
1449 ParseListFormat(data, data->format, FALSE);
1450 // FIXME: should we check for errors?
1451 DoMethod(obj, MUIM_List_Redraw, MUIV_List_Redraw_All);
1452 break;
1454 case MUIA_List_VertProp_Entries:
1455 data->vertprop_entries = tag->ti_Data;
1456 break;
1458 case MUIA_List_VertProp_Visible:
1459 data->vertprop_visible = tag->ti_Data;
1460 data->entries_visible = tag->ti_Data;
1461 break;
1463 case MUIA_List_Active:
1465 LONG new_entries_active = tag->ti_Data;
1467 if ((data->entries_num)
1468 && (new_entries_active != MUIV_List_Active_Off))
1470 switch (new_entries_active)
1472 case MUIV_List_Active_Top:
1473 new_entries_active = 0;
1474 break;
1476 case MUIV_List_Active_Bottom:
1477 new_entries_active = data->entries_num - 1;
1478 break;
1480 case MUIV_List_Active_Up:
1481 new_entries_active = data->entries_active - 1;
1482 break;
1484 case MUIV_List_Active_Down:
1485 new_entries_active = data->entries_active + 1;
1486 break;
1488 case MUIV_List_Active_PageUp:
1489 new_entries_active =
1490 data->entries_active - data->entries_visible;
1491 break;
1493 case MUIV_List_Active_PageDown:
1494 new_entries_active =
1495 data->entries_active + data->entries_visible;
1496 break;
1499 if (new_entries_active < 0)
1500 new_entries_active = 0;
1501 else if (new_entries_active >= data->entries_num)
1502 new_entries_active = data->entries_num - 1;
1504 else
1505 new_entries_active = -1;
1507 if (data->entries_active != new_entries_active)
1509 LONG old = data->entries_active;
1510 data->entries_active = new_entries_active;
1512 /* SelectChange stuff */
1513 if (new_entries_active != -1)
1515 DoMethod(obj, MUIM_List_SelectChange,
1516 new_entries_active, MUIV_List_Select_On, 0);
1517 DoMethod(obj, MUIM_List_SelectChange,
1518 new_entries_active, MUIV_List_Select_Active, 0);
1520 else
1521 DoMethod(obj, MUIM_List_SelectChange,
1522 MUIV_List_Active_Off, MUIV_List_Select_Off, 0);
1524 if (!data->read_only)
1526 data->entries[old]->flags |= ENTRY_RENDER;
1527 if (!(data->flags & LIST_QUIET))
1529 data->update = UPDATEMODE_ENTRY;
1530 data->update_pos = old;
1531 MUI_Redraw(obj, MADF_DRAWUPDATE);
1533 data->entries[data->entries_active]->flags |= ENTRY_RENDER;
1534 if (!(data->flags & LIST_QUIET))
1536 data->update = UPDATEMODE_ENTRY;
1537 data->update_pos = data->entries_active;
1538 MUI_Redraw(obj, MADF_DRAWUPDATE);
1540 else
1542 data->update = UPDATEMODE_NEEDED;
1543 data->flags |= LIST_CHANGED;
1547 /* Make new active entry visible (if there is one and
1548 list is visible) */
1549 if (new_entries_active != -1
1550 && (_flags(obj) & MADF_SETUP))
1552 DoMethod(obj, MUIM_List_Jump,
1553 MUIV_List_Jump_Active);
1557 break;
1559 case MUIA_List_First:
1560 data->update_pos = data->entries_first;
1561 data->entries_first = tag->ti_Data;
1562 if (!(data->flags & LIST_QUIET))
1564 data->update = (UPDATEMODE_ENTRY|UPDATEMODE_ALL);
1565 MUI_Redraw(obj, MADF_DRAWUPDATE);
1566 if (data->vertprop_first != tag->ti_Data)
1568 set(obj, MUIA_List_VertProp_First, tag->ti_Data);
1571 else
1573 data->update = UPDATEMODE_ALL;
1574 data->flags |= LIST_CHANGED;
1576 break;
1578 case MUIA_List_Visible: /* Shouldn't be settable? */
1579 if (data->vertprop_visible != tag->ti_Data)
1580 set(obj, MUIA_List_VertProp_Visible, tag->ti_Data);
1581 break;
1583 case MUIA_List_Entries:
1584 if (data->confirm_entries_num == tag->ti_Data)
1586 data->entries_num = tag->ti_Data;
1587 if (!(data->flags & LIST_QUIET))
1589 set(obj, MUIA_List_VertProp_Entries, data->entries_num);
1592 else
1594 D(bug("Bug: confirm_entries != MUIA_List_Entries!\n"));
1596 break;
1598 case MUIA_List_Quiet:
1599 _handle_bool_tag(data->flags, tag->ti_Data, LIST_QUIET);
1600 if (!(data->flags & LIST_QUIET))
1602 if (data->flags & LIST_CHANGED)
1604 MUI_Redraw(obj, MADF_DRAWUPDATE);
1605 if (data->entries_num != XGET(obj, MUIA_List_VertProp_Entries))
1606 set(obj, MUIA_List_VertProp_Entries, data->entries_num);
1607 if (data->entries_first != XGET(obj, MUIA_List_VertProp_First))
1608 set(obj, MUIA_List_VertProp_First, data->entries_first);
1610 data->flags &= ~LIST_CHANGED;
1612 break;
1614 case MUIA_List_AutoVisible:
1615 _handle_bool_tag(data->flags, tag->ti_Data, LIST_AUTOVISIBLE);
1616 break;
1618 case MUIA_List_ShowDropMarks:
1619 _handle_bool_tag(data->flags, tag->ti_Data, LIST_SHOWDROPMARKS);
1620 break;
1622 case MUIA_List_DragSortable:
1623 _handle_bool_tag(data->flags, tag->ti_Data, LIST_DRAGSORTABLE);
1624 break;
1626 case MUIA_Selected:
1627 /* Swallow this so the Area class doesn't redraw us */
1628 tag->ti_Tag = TAG_IGNORE;
1629 break;
1631 case MUIA_Disabled:
1632 if (_flags(obj) & MADF_SETUP)
1634 /* Stop listening for events we only listen to when mouse
1635 button is down: we will not be informed of the button
1636 being released */
1637 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
1638 (IPTR) &data->ehn);
1639 data->ehn.ehn_Events &= ~(IDCMP_MOUSEMOVE | IDCMP_INTUITICKS
1640 | IDCMP_INACTIVEWINDOW);
1641 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
1642 (IPTR) &data->ehn);
1644 break;
1646 case MUIA_Listview_DoubleClick: /* private set */
1647 data->doubleclick = tag->ti_Data != 0;
1648 break;
1650 case MUIA_Listview_ScrollerPos: /* private set */
1651 data->scroller_pos = tag->ti_Data;
1652 List_HandleScrollerPos(cl, obj);
1653 break;
1655 case MUIA_Listview_Input: /* private set */
1656 data->read_only = !tag->ti_Data;
1657 break;
1659 case MUIA_Listview_MultiSelect: /* private set */
1660 data->multiselect = tag->ti_Data;
1661 break;
1663 case MUIA_Listview_DefClickColumn:
1664 data->def_click_column = tag->ti_Data;
1665 break;
1667 case MUIA_Listview_DragType:
1668 data->drag_type = tag->ti_Data;
1669 set(obj, MUIA_Draggable,
1670 tag->ti_Data != MUIV_Listview_DragType_None);
1671 break;
1675 return DoSuperMethodA(cl, obj, (Msg) msg);
1678 /**************************************************************************
1679 OM_GET
1680 **************************************************************************/
1681 IPTR List__OM_GET(struct IClass *cl, Object *obj, struct opGet *msg)
1683 /* small macro to simplify return value storage */
1684 #define STORE *(msg->opg_Storage)
1685 struct MUI_ListData *data = INST_DATA(cl, obj);
1687 switch (msg->opg_AttrID)
1689 case MUIA_List_Entries:
1690 STORE = data->entries_num;
1691 return 1;
1692 case MUIA_List_First:
1693 STORE = data->entries_first;
1694 return 1;
1695 case MUIA_List_Active:
1696 STORE = data->entries_active;
1697 return 1;
1698 case MUIA_List_InsertPosition:
1699 STORE = data->insert_position;
1700 return 1;
1701 case MUIA_List_Title:
1702 STORE = (IPTR) data->title;
1703 return 1;
1704 case MUIA_List_VertProp_Entries:
1705 STORE = data->vertprop_entries;
1706 return 1;
1707 case MUIA_List_VertProp_Visible:
1708 case MUIA_List_Visible:
1709 STORE = data->vertprop_visible;
1710 return 1;
1711 case MUIA_List_VertProp_First:
1712 STORE = data->vertprop_first;
1713 return 1;
1714 case MUIA_List_Format:
1715 STORE = (IPTR) data->format;
1716 return 1;
1717 case MUIA_List_AutoVisible:
1718 STORE = data->flags & LIST_AUTOVISIBLE;
1719 return 1;
1720 case MUIA_List_ShowDropMarks:
1721 STORE = data->flags & LIST_SHOWDROPMARKS;
1722 return 1;
1723 case MUIA_List_DragSortable:
1724 STORE = data->flags & LIST_DRAGSORTABLE;
1725 return 1;
1726 case MUIA_List_DropMark:
1727 STORE = data->drop_mark;
1728 return 1;
1729 case MUIA_Listview_ClickColumn:
1730 STORE = data->click_column;
1731 return 1;
1732 case MUIA_Listview_DoubleClick:
1733 STORE = data->doubleclick;
1734 return 1;
1735 case MUIA_Listview_SelectChange:
1736 STORE = FALSE;
1737 return 1;
1738 case MUIA_Listview_List:
1739 STORE = (IPTR)obj;
1740 return 1;
1741 case MUIA_Listview_DefClickColumn:
1742 STORE = data->def_click_column;
1743 return 1;
1744 case MUIA_Listview_DragType:
1745 STORE = data->drag_type;
1746 return 1;
1749 if (DoSuperMethodA(cl, obj, (Msg) msg))
1750 return 1;
1751 return 0;
1752 #undef STORE
1755 /**************************************************************************
1756 MUIM_Setup
1757 **************************************************************************/
1758 IPTR List__MUIM_Setup(struct IClass *cl, Object *obj,
1759 struct MUIP_Setup *msg)
1761 struct MUI_ListData *data = INST_DATA(cl, obj);
1763 if (!DoSuperMethodA(cl, obj, (Msg) msg))
1764 return 0;
1766 data->prefs_refresh = muiGlobalInfo(obj)->mgi_Prefs->list_refresh;
1767 data->prefs_linespacing =
1768 muiGlobalInfo(obj)->mgi_Prefs->list_linespacing;
1769 data->prefs_smoothed = muiGlobalInfo(obj)->mgi_Prefs->list_smoothed;
1770 data->prefs_smoothval = muiGlobalInfo(obj)->mgi_Prefs->list_smoothval;
1772 data->list_cursor =
1773 zune_imspec_setup(MUII_ListCursor, muiRenderInfo(obj));
1774 data->list_select =
1775 zune_imspec_setup(MUII_ListSelect, muiRenderInfo(obj));
1776 data->list_selcur =
1777 zune_imspec_setup(MUII_ListSelCur, muiRenderInfo(obj));
1779 data->prefs_multi = muiGlobalInfo(obj)->mgi_Prefs->list_multi;
1780 if (data->multiselect == MUIV_Listview_MultiSelect_Default)
1782 if (data->prefs_multi == LISTVIEW_MULTI_SHIFTED)
1783 data->multiselect = MUIV_Listview_MultiSelect_Shifted;
1784 else
1785 data->multiselect = MUIV_Listview_MultiSelect_Always;
1788 DoMethod(_win(obj), MUIM_Window_AddEventHandler, (IPTR) &data->ehn);
1790 return 1;
1793 /**************************************************************************
1794 MUIM_Cleanup
1795 **************************************************************************/
1796 IPTR List__MUIM_Cleanup(struct IClass *cl, Object *obj,
1797 struct MUIP_Cleanup *msg)
1799 struct MUI_ListData *data = INST_DATA(cl, obj);
1801 zune_imspec_cleanup(data->list_cursor);
1802 zune_imspec_cleanup(data->list_select);
1803 zune_imspec_cleanup(data->list_selcur);
1805 DoMethod(_win(obj), MUIM_Window_RemEventHandler, (IPTR) &data->ehn);
1806 data->mouse_click = 0;
1808 return DoSuperMethodA(cl, obj, (Msg) msg);
1811 /**************************************************************************
1812 MUIM_AskMinMax
1813 **************************************************************************/
1814 IPTR List__MUIM_AskMinMax(struct IClass *cl, Object *obj,
1815 struct MUIP_AskMinMax *msg)
1817 struct MUI_ListData *data = INST_DATA(cl, obj);
1819 DoSuperMethodA(cl, obj, (Msg) msg);
1821 CalcWidths(cl, obj);
1823 if ((data->flags & LIST_ADJUSTWIDTH) && (data->entries_num > 0))
1825 msg->MinMaxInfo->MinWidth += data->entries_maxwidth;
1826 msg->MinMaxInfo->DefWidth = msg->MinMaxInfo->MinWidth;
1827 msg->MinMaxInfo->MaxWidth = msg->MinMaxInfo->MinWidth;
1829 else
1831 msg->MinMaxInfo->MinWidth += 40;
1832 msg->MinMaxInfo->DefWidth += 100;
1833 msg->MinMaxInfo->MaxWidth = MUI_MAXMAX;
1836 if (data->entries_num > 0)
1838 if (data->flags & LIST_ADJUSTHEIGHT)
1840 msg->MinMaxInfo->MinHeight += data->entries_totalheight;
1841 msg->MinMaxInfo->DefHeight = msg->MinMaxInfo->MinHeight;
1842 msg->MinMaxInfo->MaxHeight = msg->MinMaxInfo->MinHeight;
1844 else
1846 ULONG h = data->entry_maxheight + data->prefs_linespacing;
1847 msg->MinMaxInfo->MinHeight += 2 * h + data->prefs_linespacing;
1848 msg->MinMaxInfo->DefHeight += 8 * h + data->prefs_linespacing;
1849 msg->MinMaxInfo->MaxHeight = MUI_MAXMAX;
1852 else
1854 msg->MinMaxInfo->MinHeight += 36;
1855 msg->MinMaxInfo->DefHeight += 96;
1856 msg->MinMaxInfo->MaxHeight = MUI_MAXMAX;
1858 D(bug("List %p minheight=%d, line maxh=%d\n",
1859 obj, msg->MinMaxInfo->MinHeight, data->entry_maxheight));
1861 return TRUE;
1864 /****i* List.mui/MUIM_Layout *************************************************
1866 * NAME
1867 * MUIM_Layout
1869 ******************************************************************************
1873 IPTR List__MUIM_Layout(struct IClass *cl, Object *obj,
1874 struct MUIP_Layout *msg)
1876 struct MUI_ListData *data = INST_DATA(cl, obj);
1877 ULONG rc = DoSuperMethodA(cl, obj, (Msg) msg);
1878 LONG new_entries_first = data->entries_first;
1880 /* Calc the numbers of entries visible */
1881 CalcVertVisible(cl, obj);
1883 /* Ensure active entry is visible if requested */
1884 if (data->entries_active + 1 >=
1885 (data->entries_first + data->entries_visible)
1886 && (data->flags & LIST_AUTOVISIBLE) != 0)
1887 new_entries_first =
1888 data->entries_active - data->entries_visible + 1;
1890 /* Ensure there are no unnecessary empty lines */
1891 if ((new_entries_first + data->entries_visible >=
1892 data->entries_num)
1893 && (data->entries_visible <= data->entries_num))
1894 new_entries_first = data->entries_num - data->entries_visible;
1896 /* Always show the start of the list if it isn't long enough to fill the
1897 view */
1898 if (data->entries_num <= data->entries_visible)
1899 new_entries_first = 0;
1901 if (new_entries_first < 0)
1902 new_entries_first = 0;
1904 set(obj, new_entries_first != data->entries_first ?
1905 MUIA_List_First : TAG_IGNORE, new_entries_first);
1907 /* So the notify happens */
1908 set(obj, MUIA_List_VertProp_Visible, data->entries_visible);
1910 return rc;
1914 /**************************************************************************
1915 MUIM_Show
1916 **************************************************************************/
1917 IPTR List__MUIM_Show(struct IClass *cl, Object *obj,
1918 struct MUIP_Show *msg)
1920 struct MUI_ListData *data = INST_DATA(cl, obj);
1921 ULONG rc = DoSuperMethodA(cl, obj, (Msg) msg);
1923 zune_imspec_show(data->list_cursor, obj);
1924 zune_imspec_show(data->list_select, obj);
1925 zune_imspec_show(data->list_selcur, obj);
1926 return rc;
1930 /**************************************************************************
1931 MUIM_Hide
1932 **************************************************************************/
1933 IPTR List__MUIM_Hide(struct IClass *cl, Object *obj,
1934 struct MUIP_Hide *msg)
1936 struct MUI_ListData *data = INST_DATA(cl, obj);
1938 zune_imspec_hide(data->list_cursor);
1939 zune_imspec_hide(data->list_select);
1940 zune_imspec_hide(data->list_selcur);
1942 return DoSuperMethodA(cl, obj, (Msg) msg);
1946 /**************************************************************************
1947 Draw an entry at entry_pos at the given row. To draw the title, set pos to
1948 ENTRY_TITLE
1949 **************************************************************************/
1950 static VOID List_DrawEntry(struct IClass *cl, Object *obj, int entry_pos,
1951 int y)
1953 struct MUI_ListData *data = INST_DATA(cl, obj);
1954 int col, x1, x2;
1956 /* To be sure we don't draw anything if there is no title */
1957 if (entry_pos == ENTRY_TITLE && !data->title)
1958 return;
1960 DisplayEntry(cl, obj, entry_pos);
1961 x1 = _mleft(data->area);
1963 for (col = 0; col < data->columns; col++)
1965 ZText *text;
1966 x2 = x1 + data->ci[col].entries_width;
1968 if ((text =
1969 zune_text_new(data->preparses[col], data->strings[col],
1970 ZTEXT_ARG_NONE, 0)))
1972 /* Could be made simpler, as we don't really need the bounds */
1973 zune_text_get_bounds(text, obj);
1974 /* Note, this was MPEN_SHADOW before */
1975 SetAPen(_rp(obj), muiRenderInfo(obj)->mri_Pens[MPEN_TEXT]);
1976 zune_text_draw(text, obj, x1, x2, y); /* totally wrong! */
1977 zune_text_destroy(text);
1979 x1 = x2 + data->ci[col].delta + (data->ci[col].bar ? BAR_WIDTH : 0);
1983 /**************************************************************************
1984 MUIM_Draw
1985 **************************************************************************/
1986 IPTR List__MUIM_Draw(struct IClass *cl, Object *obj, struct MUIP_Draw *msg)
1988 struct MUI_ListData *data = INST_DATA(cl, obj);
1989 int entry_pos, y;
1990 APTR clip;
1991 int start, end;
1992 BOOL scroll_caused_damage = FALSE;
1993 struct MUI_ImageSpec_intern *highlight;
1994 IPTR ret = (IPTR)0;
1996 D(bug("[Zune:List] %s()\n", __func__);)
1998 if (data->flags & LIST_QUIET)
1999 return ret;
2002 bug("[Zune:List] %s: Rendering...\n", __func__);
2003 bug("[Zune:List] %s: update = %d\n", __func__, data->update);
2006 ret = DoSuperMethodA(cl, obj, (Msg) msg);
2008 if (data->area_replaced)
2009 return ret;
2011 /* Calculate the title height */
2012 if (data->title)
2014 data->title_height = data->entries[ENTRY_TITLE]->height + 2;
2016 else
2018 data->title_height = 0;
2021 /* Calc the numbers of entries visible */
2022 CalcVertVisible(cl, obj);
2024 if ((msg->flags & MADF_DRAWUPDATE) == 0 || data->update == UPDATEMODE_ALL)
2026 DoMethod(obj, MUIM_DrawBackground, _mleft(data->area),
2027 _mtop(data->area), _mwidth(data->area), _mheight(data->area),
2028 0, data->entries_first * data->entry_maxheight, 0);
2031 clip = MUI_AddClipping(muiRenderInfo(obj), _mleft(data->area),
2032 _mtop(data->area), _mwidth(data->area), _mheight(data->area));
2034 if ((msg->flags & MADF_DRAWUPDATE) == 0 || data->update == UPDATEMODE_ALL)
2036 y = _mtop(data->area);
2037 /* Draw Title
2039 if (data->title_height && data->title)
2041 List_DrawEntry(cl, obj, ENTRY_TITLE, y);
2042 y += data->entries[ENTRY_TITLE]->height;
2043 SetAPen(_rp(obj), _pens(obj)[MPEN_SHADOW]);
2044 Move(_rp(obj), _mleft(data->area), y);
2045 Draw(_rp(obj), _mright(data->area), y);
2046 SetAPen(_rp(obj), _pens(obj)[MPEN_SHINE]);
2047 y++;
2048 Move(_rp(obj), _mleft(data->area), y);
2049 Draw(_rp(obj), _mright(data->area), y);
2053 y = data->entries_top_pixel;
2055 start = data->entries_first;
2056 end = data->entries_first + data->entries_visible;
2058 if ((msg->flags & MADF_DRAWUPDATE) && data->update == (UPDATEMODE_ENTRY|UPDATEMODE_ALL))
2060 int diffy = data->entries_first - data->update_pos;
2061 int top, bottom;
2062 if (abs(diffy) < data->entries_visible)
2064 scroll_caused_damage =
2065 (_rp(obj)->Layer->Flags & LAYERREFRESH) ? FALSE : TRUE;
2067 ScrollRaster(_rp(obj), 0, diffy * data->entry_maxheight,
2068 _mleft(data->area), y,
2069 _mright(data->area),
2070 y + data->entry_maxheight * data->entries_visible);
2072 scroll_caused_damage =
2073 scroll_caused_damage
2074 && (_rp(obj)->Layer->Flags & LAYERREFRESH);
2076 if (diffy > 0)
2078 start = end - diffy;
2079 y += data->entry_maxheight * (data->entries_visible -
2080 diffy);
2082 else
2083 end = start - diffy;
2086 top = y;
2087 bottom = y + (end - start) * data->entry_maxheight;
2089 DoMethod(obj, MUIM_DrawBackground, _mleft(data->area), top,
2090 _mwidth(data->area), bottom - top + 1, 0,
2091 top - _mtop(data->area) + data->entries_first
2092 * data->entry_maxheight, 0);
2095 for (entry_pos = start;
2096 entry_pos < end && entry_pos < data->entries_num; entry_pos++)
2098 struct ListEntry *entry = data->entries[entry_pos];
2100 if (!(msg->flags & MADF_DRAWUPDATE) ||
2101 ((msg->flags & MADF_DRAWUPDATE) && data->update == UPDATEMODE_ALL) ||
2102 ((msg->flags & MADF_DRAWUPDATE) && data->update == (UPDATEMODE_ENTRY|UPDATEMODE_ALL)) ||
2103 ((msg->flags & MADF_DRAWUPDATE) && data->update == UPDATEMODE_ENTRY
2104 && data->update_pos == entry_pos) ||
2105 ((msg->flags & MADF_DRAWUPDATE) && data->update == UPDATEMODE_NEEDED
2106 && (entry->flags & ENTRY_RENDER)))
2108 /* Choose appropriate highlight image */
2110 if (entry_pos == data->entries_active
2111 && (entry->flags & ENTRY_SELECTED) && !data->read_only)
2112 highlight = data->list_selcur;
2113 else if (entry_pos == data->entries_active && !data->read_only)
2114 highlight = data->list_cursor;
2115 else if (entry->flags & ENTRY_SELECTED)
2116 highlight = data->list_select;
2117 else
2118 highlight = NULL;
2120 /* Draw highlight or background */
2122 if (highlight != NULL)
2124 zune_imspec_draw(highlight, muiRenderInfo(obj),
2125 _mleft(data->area), y, _mwidth(data->area),
2126 data->entry_maxheight,
2127 0, y - data->entries_top_pixel, 0);
2129 else if (((msg->flags & MADF_DRAWUPDATE) && data->update == UPDATEMODE_ENTRY
2130 && data->update_pos == entry_pos) ||
2131 ((msg->flags & MADF_DRAWUPDATE) && data->update == UPDATEMODE_NEEDED
2132 && (entry->flags & ENTRY_RENDER)))
2134 DoMethod(obj, MUIM_DrawBackground, _mleft(data->area), y,
2135 _mwidth(data->area), data->entry_maxheight, 0,
2136 y - _mtop(data->area) +
2137 data->entries_first * data->entry_maxheight, 0);
2140 List_DrawEntry(cl, obj, entry_pos, y);
2141 entry->flags &= ~ENTRY_RENDER;
2143 y += data->entry_maxheight;
2146 MUI_RemoveClipping(muiRenderInfo(obj), clip);
2148 data->update = 0;
2150 if (scroll_caused_damage)
2152 if (MUI_BeginRefresh(muiRenderInfo(obj), 0))
2154 /* Theoretically it might happen that more damage is caused
2155 after ScrollRaster. By something else, like window movement
2156 in front of our window. Therefore refresh root object of
2157 window, not just this object */
2159 Object *o = NULL;
2161 get(_win(obj), MUIA_Window_RootObject, &o);
2162 MUI_Redraw(o, MADF_DRAWOBJECT);
2164 MUI_EndRefresh(muiRenderInfo(obj), 0);
2168 ULONG x1 = _mleft(data->area);
2169 ULONG col;
2170 y = _mtop(data->area);
2172 if (data->title_height && data->title)
2174 for (col = 0; col < data->columns; col++)
2176 ULONG halfdelta = data->ci[col].delta / 2;
2177 x1 += data->ci[col].entries_width + halfdelta;
2179 if (x1 + (data->ci[col].bar ? BAR_WIDTH : 0) > _mright(data->area))
2180 break;
2182 if (data->ci[col].bar)
2184 SetAPen(_rp(obj), _pens(obj)[MPEN_SHINE]);
2185 Move(_rp(obj), x1, y);
2186 Draw(_rp(obj), x1,
2187 y + data->entries[ENTRY_TITLE]->height - 1);
2188 SetAPen(_rp(obj), _pens(obj)[MPEN_SHADOW]);
2189 Move(_rp(obj), x1 + 1, y);
2190 Draw(_rp(obj), x1 + 1,
2191 y + data->entries[ENTRY_TITLE]->height - 1);
2193 x1 += BAR_WIDTH;
2195 x1 += data->ci[col].delta - halfdelta;
2197 y += data->entries[ENTRY_TITLE]->height + 1;
2200 x1 = _mleft(data->area);
2202 for (col = 0; col < data->columns; col++)
2204 ULONG halfdelta = data->ci[col].delta / 2;
2205 x1 += data->ci[col].entries_width + halfdelta;
2207 if (x1 + (data->ci[col].bar ? BAR_WIDTH : 0) > _mright(data->area))
2208 break;
2210 if (data->ci[col].bar)
2212 SetAPen(_rp(obj), _pens(obj)[MPEN_SHINE]);
2213 Move(_rp(obj), x1, y);
2214 Draw(_rp(obj), x1, _mbottom(data->area));
2215 SetAPen(_rp(obj), _pens(obj)[MPEN_SHADOW]);
2216 Move(_rp(obj), x1 + 1, y);
2217 Draw(_rp(obj), x1 + 1, _mbottom(data->area));
2219 x1 += BAR_WIDTH;
2222 x1 += data->ci[col].delta - halfdelta;
2225 data->flags &= ~LIST_CHANGED;
2227 return 0;
2230 /****** List.mui/MUIM_List_Clear *********************************************
2232 * NAME
2233 * MUIM_List_Clear (V4)
2235 * SYNOPSIS
2236 * DoMethod(obj, MUIM_List_Clear);
2238 * FUNCTION
2239 * Removes all entries from the list.
2241 ******************************************************************************
2245 IPTR List__MUIM_Clear(struct IClass *cl, Object *obj,
2246 struct MUIP_List_Clear *msg)
2248 struct MUI_ListData *data = INST_DATA(cl, obj);
2250 while (data->confirm_entries_num)
2252 struct ListEntry *lentry =
2253 data->entries[--data->confirm_entries_num];
2254 DoMethod(obj, MUIM_List_Destruct, (IPTR) lentry->data,
2255 (IPTR) data->pool);
2256 FreeListEntry(data, lentry);
2258 data->flags |= LIST_CHANGED;
2260 /* Should never fail when shrinking */
2261 SetListSize(data, 0);
2263 if (data->confirm_entries_num != data->entries_num)
2265 SetAttrs(obj, MUIA_List_Entries, 0, MUIA_List_First, 0,
2266 /* Notify only when no entry was active */
2267 data->entries_active !=
2268 MUIV_List_Active_Off ? MUIA_List_Active : TAG_DONE,
2269 MUIV_List_Active_Off, TAG_DONE);
2271 data->update = UPDATEMODE_ALL;
2272 MUI_Redraw(obj, MADF_DRAWUPDATE);
2275 return 0;
2278 /****** List.mui/MUIM_List_Exchange ******************************************
2280 * NAME
2281 * MUIM_List_Exchange (V4)
2283 * SYNOPSIS
2284 * DoMethod(obj, MUIM_List_Exchange, LONG pos1, LONG pos2);
2286 * FUNCTION
2287 * Exchange two entries' positions.
2289 * INPUTS
2290 * pos1 - the current index of the first entry that should be moved, or
2291 * one of these special values:
2292 * MUIV_List_Exchange_Active: the active entry.
2293 * MUIV_List_Exchange_Top: the first entry.
2294 * MUIV_List_Exchange_Bottom: the last entry.
2295 * pos2 - the index of the entry that the first entry should be exchanged
2296 * with, or one of these special values:
2297 * MUIV_List_Exchange_Active: the active entry.
2298 * MUIV_List_Exchange_Top: the first entry.
2299 * MUIV_List_Exchange_Bottom: the last entry.
2300 * MUIV_List_Exchange_Next: the next entry after pos1.
2301 * MUIV_List_Exchange_Previous: the previous entry before pos1.
2303 * NOTES
2304 * This method will do nothing if either index is greater than the last
2305 * index in the list, or if MUIV_List_Exchange_Next or
2306 * MUIV_List_Exchange_Previous imply an index outside the list.
2308 * SEE ALSO
2309 * MUIM_List_Move
2311 ******************************************************************************
2315 IPTR List__MUIM_Exchange(struct IClass *cl, Object *obj,
2316 struct MUIP_List_Exchange *msg)
2318 struct MUI_ListData *data = INST_DATA(cl, obj);
2319 LONG pos1, pos2;
2321 switch (msg->pos1)
2323 case MUIV_List_Exchange_Top:
2324 pos1 = 0;
2325 break;
2326 case MUIV_List_Exchange_Active:
2327 pos1 = data->entries_active;
2328 break;
2329 case MUIV_List_Exchange_Bottom:
2330 pos1 = data->entries_num - 1;
2331 break;
2332 default:
2333 pos1 = msg->pos1;
2336 switch (msg->pos2)
2338 case MUIV_List_Exchange_Top:
2339 pos2 = 0;
2340 break;
2341 case MUIV_List_Exchange_Active:
2342 pos2 = data->entries_active;
2343 break;
2344 case MUIV_List_Exchange_Bottom:
2345 pos2 = data->entries_num - 1;
2346 break;
2347 case MUIV_List_Exchange_Next:
2348 pos2 = pos1 + 1;
2349 break;
2350 case MUIV_List_Exchange_Previous:
2351 pos2 = pos1 - 1;
2352 break;
2353 default:
2354 pos2 = msg->pos2;
2357 if (pos1 >= 0 && pos1 < data->entries_num && pos2 >= 0
2358 && pos2 < data->entries_num && pos1 != pos2)
2360 struct ListEntry *save = data->entries[pos1];
2361 data->entries[pos1] = data->entries[pos2];
2362 data->entries[pos2] = save;
2364 data->update = UPDATEMODE_ENTRY;
2365 data->update_pos = pos1;
2366 MUI_Redraw(obj, MADF_DRAWUPDATE);
2368 data->update = UPDATEMODE_ENTRY;
2369 data->update_pos = pos2;
2370 MUI_Redraw(obj, MADF_DRAWUPDATE);
2372 return TRUE;
2374 else
2376 return FALSE;
2380 /**************************************************************************
2381 MUIM_List_Redraw
2382 **************************************************************************/
2383 IPTR List__MUIM_Redraw(struct IClass *cl, Object *obj,
2384 struct MUIP_List_Redraw *msg)
2386 struct MUI_ListData *data = INST_DATA(cl, obj);
2388 D(bug("[Zune:List] %s()\n", __func__);)
2390 if (msg->pos == MUIV_List_Redraw_All)
2392 CalcWidths(cl, obj);
2393 data->update = UPDATEMODE_ALL;
2394 if (!(data->flags & LIST_QUIET))
2396 MUI_Redraw(obj, MADF_DRAWUPDATE);
2398 else
2399 data->flags |= LIST_CHANGED;
2401 else
2403 LONG pos = -1;
2404 if (msg->pos == MUIV_List_Redraw_Active)
2405 pos = data->entries_active;
2406 else if (msg->pos == MUIV_List_Redraw_Entry)
2408 LONG i;
2409 for (i = 0; i < data->entries_num; i++)
2410 if (data->entries[i]->data == msg->entry)
2412 pos = i;
2413 break;
2416 else
2417 pos = msg->pos;
2419 if (pos != -1)
2421 data->entries[pos]->flags |= ENTRY_RENDER;
2422 if (!(data->flags & LIST_QUIET))
2424 if (CalcDimsOfEntry(cl, obj, pos))
2425 data->update = UPDATEMODE_ALL;
2426 else
2428 data->update = UPDATEMODE_ENTRY;
2429 data->update_pos = pos;
2432 MUI_Redraw(obj, MADF_DRAWUPDATE);
2434 else
2436 if (CalcDimsOfEntry(cl, obj, pos))
2437 data->update = UPDATEMODE_ALL;
2438 else if (!(data->update & UPDATEMODE_ALL))
2439 data->update = UPDATEMODE_NEEDED;
2440 data->flags |= LIST_CHANGED;
2445 return 0;
2448 /****** List.mui/MUIM_List_Remove ********************************************
2450 * NAME
2451 * MUIM_List_Remove (V4)
2453 * SYNOPSIS
2454 * DoMethod(obj, MUIM_List_Remove, LONG pos);
2456 * FUNCTION
2457 * Removes entries from the list. If a destruct hook has been
2458 * installed, it will be called for the removed entry.
2460 * INPUTS
2461 * pos - the index of the entry to be removed. The following
2462 * special values can also be used:
2463 * MUIV_List_Remove_First: remove the first entry.
2464 * MUIV_List_Remove_Last: remove the last entry.
2465 * MUIV_List_Remove_Active: remove the active entry.
2466 * MUIV_List_Remove_Selected: remove all selected entries
2467 * (or the active entry if there are no selected entries).
2469 * NOTES
2470 * When the active entry is removed, the next entry becomes active
2471 * (if there is no entry below the active entry, the previous entry
2472 * becomes active instead).
2474 * SEE ALSO
2475 * MUIM_List_Insertsingle, MUIM_List_Insert, MUIA_List_DestructHook.
2477 ******************************************************************************
2479 * It was not possible to use MUIM_List_NextSelected here because that method
2480 * may skip entries if entries are removed during an iteration.
2484 IPTR List__MUIM_Remove(struct IClass *cl, Object *obj,
2485 struct MUIP_List_Remove *msg)
2487 struct MUI_ListData *data = INST_DATA(cl, obj);
2488 LONG pos;
2489 LONG new_act;
2490 UWORD i;
2491 BOOL found, done = FALSE;
2492 struct ListEntry *lentry;
2493 Tag active_tag = TAG_DONE;
2495 if (!data->entries_num)
2496 return 0;
2498 switch (msg->pos)
2500 case MUIV_List_Remove_First:
2501 pos = 0;
2502 break;
2504 case MUIV_List_Remove_Active:
2505 pos = data->entries_active;
2506 break;
2508 case MUIV_List_Remove_Last:
2509 pos = data->entries_num - 1;
2510 break;
2512 case MUIV_List_Remove_Selected:
2513 pos = 0;
2514 break;
2516 default:
2517 pos = msg->pos;
2518 break;
2521 if (pos < 0 || pos >= data->entries_num)
2522 return 0;
2524 new_act = data->entries_active;
2526 while (!done)
2528 if (msg->pos == MUIV_List_Remove_Selected)
2530 /* Find the next selected entry */
2531 for (found = FALSE, i = pos;
2532 i < data->confirm_entries_num && !found; i++)
2534 if (data->entries[i]->flags & ENTRY_SELECTED)
2536 pos = i;
2537 found = TRUE;
2541 if (!found)
2543 done = TRUE;
2545 /* If there were no selected entries, remove the active one */
2546 if (data->confirm_entries_num == data->entries_num
2547 && data->entries_active != MUIV_List_Active_Off)
2549 pos = data->entries_active;
2550 found = TRUE;
2554 else
2556 done = TRUE;
2557 found = TRUE;
2560 if (found)
2562 lentry = data->entries[pos];
2563 DoMethod(obj, MUIM_List_Destruct, (IPTR) lentry->data,
2564 (IPTR) data->pool);
2565 RemoveListEntries(data, pos, 1);
2566 data->confirm_entries_num--;
2568 if (pos < new_act)
2570 new_act--;
2571 active_tag = MUIA_List_Active;
2573 else if (pos == new_act)
2574 active_tag = MUIA_List_Active;
2578 /* Update entries count prior to range check */
2579 SetAttrs(obj, MUIA_List_Entries, data->confirm_entries_num, TAG_DONE);
2581 /* Ensure that the active element is in a valid range (it might become
2582 * MUIV_List_Active_Off (-1), but that's OK) */
2583 if (new_act >= data->entries_num)
2584 new_act = data->entries_num - 1;
2586 SetAttrs(obj,
2587 active_tag, new_act, /* Inform only if necessary (for notify) */
2588 TAG_DONE);
2590 data->flags |= LIST_CHANGED;
2591 data->update = UPDATEMODE_ALL;
2592 if (!(data->flags & LIST_QUIET))
2593 MUI_Redraw(obj, MADF_DRAWUPDATE);
2595 return 0;
2598 /****** List.mui/MUIM_List_Select ********************************************
2600 * NAME
2601 * MUIM_List_Select (V4)
2603 * SYNOPSIS
2604 * DoMethod(obj, MUIM_List_Select, LONG pos, LONG seltype, LONG *state);
2606 * FUNCTION
2607 * Selects or deselects entries in the list and/or enquires about their
2608 * current selection state. If a multiselection test hook has been
2609 * installed via MUIA_List_MultiTestHook, it will be called to validate
2610 * the requested selection.
2612 * This method may also be used to count the number of selected entries
2613 * (see below).
2615 * INPUTS
2616 * pos - the index of the entry to be selected. The following
2617 * special values can also be used:
2618 * MUIV_List_Select_Active: Select the active entry.
2619 * MUIV_List_Select_All: Select all entries.
2620 * seltype - the new selection state; one of the following:
2621 * MUIV_List_Select_Off: Select the entry.
2622 * MUIV_List_Select_On: Deselect the entry.
2623 * MUIV_List_Select_Toggle: Switch to the alternate selection state.
2624 * MUIV_List_Select_Ask: Do not change the selection state, just
2625 * retrieve the current state. If 'pos' is MUIV_List_Select_All,
2626 * the number of selected entries will be retrieved instead (V9).
2627 * state - a pointer in which to fill in the previous selection state
2628 * (either MUIV_List_Select_On or MUIV_List_Select_Off) or the
2629 * number of selected entries. May be NULL.
2631 * NOTES
2632 * If pos is MUIV_List_Select_All and seltype is not
2633 * MUIV_List_Select_Ask, the value filled in 'state' is undefined.
2635 * SEE ALSO
2636 * MUIA_List_Active, MUIA_List_MultiTestHook.
2638 ******************************************************************************
2642 IPTR List__MUIM_Select(struct IClass *cl, Object *obj,
2643 struct MUIP_List_Select *msg)
2645 struct MUI_ListData *data = INST_DATA(cl, obj);
2646 LONG pos, i, count, selcount = 0, state = 0;
2647 BOOL multi_allowed = TRUE, new_multi_allowed, new_select_state = FALSE;
2649 /* Establish the range of entries affected */
2650 switch (msg->pos)
2652 case MUIV_List_Select_Active:
2653 pos = data->entries_active;
2654 if (pos == MUIV_List_Active_Off)
2655 count = 0;
2656 else
2657 count = 1;
2658 break;
2660 case MUIV_List_Select_All:
2661 pos = 0;
2662 count = data->entries_num;
2663 break;
2665 default:
2666 pos = msg->pos;
2667 count = 1;
2668 if (pos < 0 || pos >= data->entries_num)
2669 return 0;
2670 break;
2673 if (msg->seltype != MUIV_List_Select_Ask && data->multi_test_hook != NULL)
2675 /* Count selected entries and disallow selection of additional
2676 entries if there is a currently selected entry that is not
2677 multi-selectable */
2678 for (i = 0; i < data->entries_num && multi_allowed; i++)
2680 if (data->entries[i]->flags & ENTRY_SELECTED)
2682 selcount++;
2683 if (data->multi_test_hook != NULL && selcount == 1)
2684 multi_allowed = CallHookPkt(data->multi_test_hook, NULL,
2685 data->entries[i]->data);
2690 /* Change or check state of each entry in the range */
2691 for (i = pos; i < pos + count; i++)
2693 state = data->entries[i]->flags & ENTRY_SELECTED;
2694 switch (msg->seltype)
2696 case MUIV_List_Select_Off:
2697 new_select_state = FALSE;
2698 break;
2700 case MUIV_List_Select_On:
2701 new_select_state = TRUE;
2702 break;
2704 case MUIV_List_Select_Toggle:
2705 new_select_state = !state;
2706 break;
2708 default:
2709 if (data->entries[i]->flags & ENTRY_SELECTED)
2710 selcount++;
2711 break;
2714 if (msg->seltype != MUIV_List_Select_Ask)
2716 if (new_select_state && !state)
2718 /* Check if there is potential to select an additional entry */
2719 if (multi_allowed || selcount == 0)
2721 /* Check if the entry to be selected is multi-selectable */
2722 if (data->multi_test_hook != NULL)
2723 new_multi_allowed = CallHookPkt(data->multi_test_hook,
2724 NULL, data->entries[i]->data);
2725 else
2726 new_multi_allowed = TRUE;
2728 /* Check if the entry to be selected can be selected at
2729 the same time as the already selected entries */
2730 if (new_multi_allowed || selcount == 0)
2732 /* Select the entry and update the selection count
2733 and flag */
2734 data->entries[i]->flags |= ENTRY_SELECTED;
2735 selcount++;
2737 multi_allowed = new_multi_allowed;
2741 else if (!new_select_state && state)
2743 data->entries[i]->flags &= ~ENTRY_SELECTED;
2744 selcount--;
2749 /* Report old state or number of selected entries */
2750 if (msg->info)
2752 if (msg->pos == MUIV_List_Select_All
2753 && msg->seltype == MUIV_List_Select_Ask)
2754 *msg->info = selcount;
2755 else
2756 *msg->info = state;
2759 /* Redraw unless it was just an enquiry */
2760 if (msg->seltype != MUIV_List_Select_Ask)
2762 if (count > 1)
2763 data->update = UPDATEMODE_ALL;
2764 else
2766 data->update = UPDATEMODE_ENTRY;
2767 data->update_pos = pos;
2769 MUI_Redraw(obj, MADF_DRAWUPDATE);
2772 return 0;
2775 /****** List.mui/MUIM_List_Insert ********************************************
2777 * NAME
2778 * MUIM_List_Insert (V4)
2780 * SYNOPSIS
2781 * DoMethod(obj, MUIM_List_Insert, APTR *entries, LONG count, LONG pos);
2783 * FUNCTION
2784 * Adds multiple entries to the list. If a construct hook has been
2785 * installed, the results of passing the entries to this hook will be
2786 * inserted.
2788 * INPUTS
2789 * entries - an array of entries to be inserted.
2790 * count - the number of entries to insert. A special value of -1 may be
2791 * used, indicating that the array of entries is NULL-terminated.
2792 * pos - the index at which to insert the new entries. The following
2793 * special values can also be used:
2794 * MUIV_List_Insert_Top: insert at index 0.
2795 * MUIV_List_Insert_Bottom: insert after all existing entries.
2796 * MUIV_List_Insert_Active: insert at the index of the active entry
2797 * (or at index 0 if there is no active entry).
2798 * MUIV_List_Insert_Sorted: keep the list sorted.
2800 * SEE ALSO
2801 * MUIM_List_InsertSingle, MUIM_List_Remove, MUIA_List_ConstructHook.
2803 ******************************************************************************
2807 IPTR List__MUIM_Insert(struct IClass *cl, Object *obj,
2808 struct MUIP_List_Insert *msg)
2810 struct MUI_ListData *data = INST_DATA(cl, obj);
2811 LONG pos, count, sort, active;
2812 BOOL adjusted = FALSE;
2814 count = msg->count;
2815 sort = 0;
2817 if (count == -1)
2819 /* Count the number of entries */
2820 for (count = 0; msg->entries[count] != NULL; count++)
2824 if (count <= 0)
2825 return ~0;
2827 switch (msg->pos)
2829 case MUIV_List_Insert_Top:
2830 pos = 0;
2831 break;
2833 case MUIV_List_Insert_Active:
2834 if (data->entries_active != -1)
2835 pos = data->entries_active;
2836 else
2837 pos = 0;
2838 break;
2840 case MUIV_List_Insert_Sorted:
2841 pos = data->entries_num;
2842 sort = 1; /* we sort'em later */
2843 break;
2845 case MUIV_List_Insert_Bottom:
2846 pos = data->entries_num;
2847 break;
2849 default:
2850 if (msg->pos > data->entries_num)
2851 pos = data->entries_num;
2852 else if (msg->pos < 0)
2853 pos = 0;
2854 else
2855 pos = msg->pos;
2856 break;
2858 data->insert_position = pos;
2860 if (!(SetListSize(data, data->entries_num + count)))
2861 return ~0;
2863 LONG until = pos + count;
2864 APTR *toinsert = msg->entries;
2866 if (!(PrepareInsertListEntries(data, pos, count)))
2867 return ~0;
2869 while (pos < until)
2871 struct ListEntry *lentry;
2873 if (!(lentry = AllocListEntry(data)))
2875 /* Panic, but we must be in a consistent state, so remove
2876 * the space where the following list entries should have gone
2878 RemoveListEntries(data, pos, until - pos);
2879 return ~0;
2882 /* now call the construct method which returns us a pointer which
2883 we need to store */
2884 lentry->data = (APTR) DoMethod(obj, MUIM_List_Construct,
2885 (IPTR) * toinsert, (IPTR) data->pool);
2886 if (!lentry->data)
2888 FreeListEntry(data, lentry);
2889 RemoveListEntries(data, pos, until - pos);
2891 /* TODO: Also check for visible stuff like below */
2892 if (data->entries_num != data->confirm_entries_num)
2893 set(obj, MUIA_List_Entries, data->confirm_entries_num);
2894 return ~0;
2897 lentry->flags |= ENTRY_RENDER;
2898 data->entries[pos] = lentry;
2899 data->confirm_entries_num++;
2901 data->flags |= LIST_CHANGED;
2903 if (_flags(obj) & MADF_SETUP)
2905 /* We have to calculate the width and height of the newly
2906 * inserted entry. This has to be done after inserting the
2907 * element into the list */
2908 if (CalcDimsOfEntry(cl, obj, pos))
2909 adjusted = TRUE;
2912 toinsert++;
2913 pos++;
2915 pos--;
2917 /* Recalculate the number of visible entries */
2918 if (_flags(obj) & MADF_SETUP)
2919 CalcVertVisible(cl, obj);
2921 if (data->entries_num != data->confirm_entries_num)
2923 SetAttrs(obj,
2924 MUIA_List_Entries, data->confirm_entries_num,
2925 MUIA_List_Visible, data->entries_visible, TAG_DONE);
2928 /* If the array is already sorted, we could do a simple insert
2929 * sort and would be much faster than with qsort.
2930 * If an array is not yet sorted, does a MUIV_List_Insert_Sorted
2931 * sort the whole array?
2933 * I think, we better sort the whole array:
2935 if (sort)
2937 /* TODO: which pos to return here !? */
2938 DoMethod(obj, MUIM_List_Sort);
2940 if ((adjusted) && (data->flags & LIST_QUIET))
2941 data->update = UPDATEMODE_ALL;
2943 else
2945 data->update = UPDATEMODE_ALL;
2946 if (!(data->flags & LIST_QUIET))
2947 MUI_Redraw(obj, MADF_DRAWUPDATE);
2949 superset(cl, obj, MUIA_List_InsertPosition, data->insert_position);
2951 /* Update index of active entry */
2952 if (data->entries_active >= data->insert_position)
2954 active = data->entries_active + count;
2955 SET(obj, MUIA_List_Active, active);
2958 return (ULONG) pos;
2961 /****** List.mui/MUIM_List_InsertSingle **************************************
2963 * NAME
2964 * MUIM_List_InsertSingle (V7)
2966 * SYNOPSIS
2967 * DoMethod(obj, MUIM_List_InsertSingle, APTR entry, LONG pos);
2969 * FUNCTION
2970 * Adds a single entry to the list. If a construct hook has been
2971 * installed, the result of passing the entry to this hook will be
2972 * inserted.
2974 * INPUTS
2975 * entry - the entry to be inserted.
2976 * pos - the index at which to insert the new entry. The following
2977 * special values can also be used:
2978 * MUIV_List_Insert_Top: insert at index 0.
2979 * MUIV_List_Insert_Bottom: insert after all existing entries.
2980 * MUIV_List_Insert_Active: insert at the index of the active entry
2981 * (or at index 0 if there is no active entry).
2982 * MUIV_List_Insert_Sorted: keep the list sorted.
2984 * SEE ALSO
2985 * MUIM_List_Insert, MUIM_List_Remove, MUIA_List_ConstructHook.
2987 ******************************************************************************
2991 IPTR List__MUIM_InsertSingle(struct IClass *cl, Object *obj,
2992 struct MUIP_List_InsertSingle *msg)
2994 return DoMethod(obj, MUIM_List_Insert, (IPTR) & msg->entry, 1,
2995 msg->pos);
2998 /****** List.mui/MUIM_List_GetEntry ******************************************
3000 * NAME
3001 * MUIM_List_GetEntry (V4)
3003 * SYNOPSIS
3004 * DoMethod(obj, MUIM_List_GetEntry, LONG pos, APTR *entry);
3006 * FUNCTION
3007 * Retrieves an entry from the list. If the requested entry position is
3008 * invalid, the entry will be NULL.
3010 * INPUTS
3011 * pos - the index of the entry to get, or the special value
3012 * MUIV_List_GetEntry_Active to get the active entry.
3013 * entry - a pointer to a variable in which to store a pointer to the
3014 * entry data.
3016 * SEE ALSO
3017 * MUIM_List_Insert, MUIM_List_InsertSingle, MUIM_List_Remove.
3019 ******************************************************************************
3023 IPTR List__MUIM_GetEntry(struct IClass *cl, Object *obj,
3024 struct MUIP_List_GetEntry *msg)
3026 struct MUI_ListData *data = INST_DATA(cl, obj);
3027 int pos = msg->pos;
3029 if (pos == MUIV_List_GetEntry_Active)
3030 pos = data->entries_active;
3032 if (pos < 0 || pos >= data->entries_num)
3034 *msg->entry = NULL;
3035 return 0;
3037 *msg->entry = data->entries[pos]->data;
3038 return (IPTR) *msg->entry;
3041 /**************************************************************************
3042 MUIM_List_Construct
3043 **************************************************************************/
3044 IPTR List__MUIM_Construct(struct IClass *cl, Object *obj,
3045 struct MUIP_List_Construct *msg)
3047 struct MUI_ListData *data = INST_DATA(cl, obj);
3049 if (NULL == data->construct_hook)
3050 return (IPTR) msg->entry;
3051 if ((IPTR) data->construct_hook == MUIV_List_ConstructHook_String)
3053 int len = msg->entry ? strlen((STRPTR) msg->entry) : 0;
3054 ULONG *mem = AllocPooled(msg->pool, len + 5);
3056 if (NULL == mem)
3057 return 0;
3058 mem[0] = len + 5;
3059 if (msg->entry != NULL)
3060 strcpy((STRPTR) (mem + 1), (STRPTR) msg->entry);
3061 else
3062 *(STRPTR) (mem + 1) = 0;
3063 return (IPTR) (mem + 1);
3065 return CallHookPkt(data->construct_hook, msg->pool, msg->entry);
3068 /**************************************************************************
3069 MUIM_List_Destruct
3070 **************************************************************************/
3071 IPTR List__MUIM_Destruct(struct IClass *cl, Object *obj,
3072 struct MUIP_List_Destruct *msg)
3074 struct MUI_ListData *data = INST_DATA(cl, obj);
3076 if (NULL == data->destruct_hook)
3077 return 0;
3079 if ((IPTR) data->destruct_hook == MUIV_List_DestructHook_String)
3081 ULONG *mem = ((ULONG *) msg->entry) - 1;
3082 FreePooled(msg->pool, mem, mem[0]);
3084 else
3086 CallHookPkt(data->destruct_hook, msg->pool, msg->entry);
3088 return 0;
3091 /****** List.mui/MUIM_List_Compare *******************************************
3093 * NAME
3094 * MUIM_List_Compare (V20)
3096 * SYNOPSIS
3097 * DoMethod(obj, MUIM_List_Compare, APTR entry1, APTR entry2,
3098 * LONG sort_type1, LONG sort_type2);
3100 * FUNCTION
3101 * Compare two list entries according to the current comparison hook
3102 * (MUIA_List_CompareHook).
3104 * INPUTS
3105 * entry1 - the first entry data.
3106 * entry2 - the second entry data.
3107 * sort_type1 - undocumented.
3108 * sort_type2 - undocumented.
3110 * SEE ALSO
3111 * MUIA_List_CompareHook, MUIM_List_Sort.
3113 ******************************************************************************
3117 IPTR List__MUIM_Compare(struct IClass *cl, Object *obj,
3118 struct MUIP_List_Compare *msg)
3120 struct MUI_ListData *data = INST_DATA(cl, obj);
3122 return CallHookPkt(data->compare_hook, msg->entry2, msg->entry1);
3125 /**************************************************************************
3126 MUIM_List_Display
3127 **************************************************************************/
3128 IPTR List__MUIM_Display(struct IClass *cl, Object *obj,
3129 struct MUIP_List_Display *msg)
3131 struct MUI_ListData *data = INST_DATA(cl, obj);
3133 if (NULL == data->display_hook)
3135 if (msg->entry)
3136 *msg->array = msg->entry;
3137 else
3138 *msg->array = 0;
3139 return 1;
3142 *((ULONG *) (msg->array - 1)) = msg->entry_pos;
3143 return CallHookPkt(data->display_hook, msg->array, msg->entry);
3146 /**************************************************************************
3147 MUIM_List_SelectChange
3148 **************************************************************************/
3149 IPTR List__MUIM_SelectChange(struct IClass *cl, Object *obj,
3150 struct MUIP_List_SelectChange *msg)
3152 return 1;
3155 /****** List.mui/MUIM_List_CreateImage ***************************************
3157 * NAME
3158 * MUIM_List_CreateImage (V11)
3160 * SYNOPSIS
3161 * DoMethod(obj, MUIM_List_CreateImage, Object *area, ULONG flags);
3163 * FUNCTION
3164 * Creates an image to be inserted within list entries. An instance of
3165 * any Area subclass is passed in and a blackbox value is returned that
3166 * can be displayed by embedding its hexadecimal representation within
3167 * any of the list's display strings (provided either statically or by
3168 * the list's display hook. The format string to be used is
3169 * "\330[%08lx]".
3171 * It is recommended that lists that embed images are instances of a
3172 * custom subclass so that this method can be called in the list's
3173 * MUIM_Setup method, and MUIM_List_DeleteImage can be called in the
3174 * list's MUIM_Cleanup method. However, this is not necessary as long as
3175 * MUIM_List_CreateImage is called after the list has had its MUIM_Setup
3176 * called, and MUIM_List_DeleteImage is called after the list has had its
3177 * MUIM_Cleanup method called.
3179 * INPUTS
3180 * area - the area object that is used to generate the image.
3181 * flags - must be zero.
3183 * RESULT
3184 * A blackbox reference to the list image, or NULL on an error.
3186 * NOTES
3187 * If this method returns NULL, no special action needs to be taken, as
3188 * embedding this value in a list string will simply cause no image to
3189 * appear.
3191 * The object passed in becomes a child of the list, so it cannot be used
3192 * elsewhere simultaneously.
3194 * SEE ALSO
3195 * MUIM_List_DeleteImage, MUIA_List_DisplayHook.
3197 ******************************************************************************
3199 * Connects an Area subclass object to the list, much like an object gets
3200 * connected to a window. List calls Setup and AskMinMax on that object,
3201 * keeps a reference to it (that reference will be returned).
3202 * Text engine will dereference that pointer and draw the object with its
3203 * default size.
3207 IPTR List__MUIM_CreateImage(struct IClass *cl, Object *obj,
3208 struct MUIP_List_CreateImage *msg)
3210 struct MUI_ListData *data = INST_DATA(cl, obj);
3211 struct ListImage *li;
3213 if (!msg->obj)
3214 return 0;
3216 /* List must be already setup in Setup of your subclass */
3217 if (!(_flags(obj) & MADF_SETUP))
3218 return 0;
3219 li = AllocPooled(data->pool, sizeof(struct ListImage));
3220 if (!li)
3221 return 0;
3222 li->obj = msg->obj;
3224 AddTail((struct List *)&data->images, (struct Node *)li);
3225 DoMethod(li->obj, MUIM_ConnectParent, (IPTR) obj);
3226 DoSetupMethod(li->obj, muiRenderInfo(obj));
3229 return (IPTR) li;
3232 /****** List.mui/MUIM_List_DeleteImage ***************************************
3234 * NAME
3235 * MUIM_List_DeleteImage (V11)
3237 * SYNOPSIS
3238 * DoMethod(obj, MUIM_List_DeleteImage, APTR image);
3240 * FUNCTION
3241 * Deletes an image created by MUIM_List_CreateImage.
3243 * INPUTS
3244 * image - a blackbox reference to the list image, or NULL.
3246 * SEE ALSO
3247 * MUIM_List_CreateImage, MUIA_List_DisplayHook.
3249 ******************************************************************************
3253 IPTR List__MUIM_DeleteImage(struct IClass *cl, Object *obj,
3254 struct MUIP_List_DeleteImage *msg)
3256 struct MUI_ListData *data = INST_DATA(cl, obj);
3257 struct ListImage *li = (struct ListImage *)msg->listimg;
3259 if (li)
3261 DoMethod(li->obj, MUIM_Cleanup);
3262 DoMethod(li->obj, MUIM_DisconnectParent);
3263 Remove((struct Node *)li);
3264 FreePooled(data->pool, li, sizeof(struct ListImage));
3267 return 0;
3270 /****** List.mui/MUIM_List_Jump **********************************************
3272 * NAME
3273 * MUIM_List_Jump (V4)
3275 * SYNOPSIS
3276 * DoMethod(obj, MUIM_List_Jump, LONG pos);
3278 * FUNCTION
3279 * Scrolls the list so that a particular entry is visible.
3281 * INPUTS
3282 * pos - index of entry that should become visible, or one of these
3283 * special values:
3284 * MUIV_List_Jump_Active: show the active entry.
3285 * MUIV_List_Jump_Top: show the first entry.
3286 * MUIV_List_Jump_Bottom: show the last entry.
3287 * MUIV_List_Jump_Up: show the previous hidden entry.
3288 * MUIV_List_Jump_Down: show the next hidden entry.
3290 ******************************************************************************
3294 IPTR List__MUIM_Jump(struct IClass *cl, Object *obj,
3295 struct MUIP_List_Jump *msg)
3297 struct MUI_ListData *data = INST_DATA(cl, obj);
3298 LONG pos = msg->pos;
3300 switch (pos)
3302 case MUIV_List_Jump_Top:
3303 pos = 0;
3304 break;
3306 case MUIV_List_Jump_Active:
3307 pos = data->entries_active;
3308 break;
3310 case MUIV_List_Jump_Bottom:
3311 pos = data->entries_num - 1;
3312 break;
3314 case MUIV_List_Jump_Down:
3315 pos = data->entries_first + data->entries_visible;
3316 break;
3318 case MUIV_List_Jump_Up:
3319 pos = data->entries_first - 1;
3320 break;
3323 if (pos >= data->entries_num)
3325 pos = data->entries_num - 1;
3327 if (pos < 0)
3328 pos = 0;
3330 if (pos < data->entries_first)
3332 set(obj, MUIA_List_First, pos);
3334 else if (pos >= data->entries_first + data->entries_visible)
3336 pos -= (data->entries_visible - 1);
3337 if (pos < 0)
3338 pos = 0;
3339 if (pos != data->entries_first)
3341 set(obj, MUIA_List_First, pos);
3345 return TRUE;
3348 /****** List.mui/MUIM_List_Sort **********************************************
3350 * NAME
3351 * MUIM_List_Sort (V4)
3353 * SYNOPSIS
3354 * DoMethod(obj, MUIM_List_Sort);
3356 * FUNCTION
3357 * Sort the list's entries according to the current comparison hook
3358 * (MUIA_List_CompareHook).
3360 * NOTES
3361 * The active index does not change, so the active entry may do so.
3363 * SEE ALSO
3364 * MUIA_List_CompareHook, MUIM_List_Compare.
3366 ******************************************************************************
3370 IPTR List__MUIM_Sort(struct IClass *cl, Object *obj,
3371 struct MUIP_List_Sort *msg)
3373 struct MUI_ListData *data = INST_DATA(cl, obj);
3375 int i, j, max;
3376 struct MUIP_List_Compare cmpmsg =
3377 { MUIM_List_Compare, NULL, NULL, 0, 0 };
3378 BOOL changed = FALSE;
3380 D(bug("[Zune:List] %s()\n", __func__);)
3382 if (data->entries_num > 1)
3385 Simple sort algorithm. Feel free to improve it.
3387 for (i = 0; i < data->entries_num - 1; i++)
3389 max = i;
3390 for (j = i + 1; j < data->entries_num; j++)
3392 cmpmsg.entry1 = data->entries[max]->data;
3393 cmpmsg.entry2 = data->entries[j]->data;
3394 if ((LONG) DoMethodA(obj, (Msg) & cmpmsg) > 0)
3396 max = j;
3399 if (i != max)
3401 APTR tmp = data->entries[i];
3402 data->entries[i] = data->entries[max];
3403 data->entries[i]->flags |= ENTRY_RENDER;
3404 data->entries[max] = tmp;
3405 data->entries[max]->flags |= ENTRY_RENDER;
3406 if (data->entries_active == i)
3407 data->entries_active = max;
3408 else if (data->entries_active == max)
3409 data->entries_active = i;
3410 changed = TRUE;
3415 if (changed)
3417 data->flags |= LIST_CHANGED;
3418 if (!(data->update & UPDATEMODE_ALL))
3419 data->update = UPDATEMODE_NEEDED;
3420 if (!(data->flags & LIST_QUIET))
3421 MUI_Redraw(obj, MADF_DRAWUPDATE);
3424 return 0;
3427 /****** List.mui/MUIM_List_Move **********************************************
3429 * NAME
3430 * MUIM_List_Move (V9)
3432 * SYNOPSIS
3433 * DoMethod(obj, MUIM_List_Move, LONG from, LONG to);
3435 * FUNCTION
3436 * Move a list entry to a new position.
3438 * INPUTS
3439 * from - the current index of the entry that should be moved, or one of
3440 * these special values:
3441 * MUIV_List_Move_Active: the active entry.
3442 * MUIV_List_Move_Top: the first entry.
3443 * MUIV_List_Move_Bottom: the last entry.
3444 * to - the index of the entry's new position, or one of
3445 * these special values:
3446 * MUIV_List_Move_Active: the active entry.
3447 * MUIV_List_Move_Top: the first entry.
3448 * MUIV_List_Move_Bottom: the last entry.
3450 * NOTES
3451 * The active index does not change, so the active entry may do so.
3453 * SEE ALSO
3454 * MUIM_List_Exchange
3456 ******************************************************************************
3460 IPTR List__MUIM_Move(struct IClass *cl, Object *obj,
3461 struct MUIP_List_Move *msg)
3463 struct MUI_ListData *data = INST_DATA(cl, obj);
3465 LONG from, to;
3466 int i;
3468 /* Normalise special 'from' values */
3469 switch (msg->from)
3471 case MUIV_List_Move_Top:
3472 from = 0;
3473 break;
3474 case MUIV_List_Move_Active:
3475 from = data->entries_active;
3476 break;
3477 case MUIV_List_Move_Bottom:
3478 from = data->entries_num - 1;
3479 break;
3480 default:
3481 from = msg->from;
3484 /* Normalise special 'to' values */
3485 switch (msg->to)
3487 case MUIV_List_Move_Top:
3488 to = 0;
3489 break;
3490 case MUIV_List_Move_Active:
3491 to = data->entries_active;
3492 break;
3493 case MUIV_List_Move_Bottom:
3494 to = data->entries_num - 1;
3495 break;
3496 case MUIV_List_Move_Next:
3497 to = from + 1;
3498 break;
3499 case MUIV_List_Move_Previous:
3500 to = from - 1;
3501 break;
3502 default:
3503 to = msg->to;
3506 /* Check that values are within valid bounds */
3507 if (from > data->entries_num - 1 || from < 0
3508 || to > data->entries_num - 1 || to < 0 || from == to)
3509 return (IPTR) FALSE;
3511 /* Shift all entries in the range between the 'from' and 'to' positions */
3512 if (from < to)
3514 struct ListEntry *backup = data->entries[from];
3515 for (i = from; i < to; i++)
3516 data->entries[i] = data->entries[i + 1];
3517 data->entries[to] = backup;
3519 else
3521 struct ListEntry *backup = data->entries[from];
3522 for (i = from; i > to; i--)
3523 data->entries[i] = data->entries[i - 1];
3524 data->entries[to] = backup;
3527 #if 0 /* Not done in MUI 3 */
3528 /* Update index of active entry */
3529 if (from == data->entries_active)
3530 data->entries_active = to;
3531 else if (data->entries_active > from && data->entries_active < to)
3532 data->entries_active--;
3533 else if (data->entries_active < from && data->entries_active >= to)
3534 data->entries_active++;
3535 #endif
3537 /* Reflect list changes visually */
3538 data->flags |= LIST_CHANGED;
3539 data->update = UPDATEMODE_ALL;
3540 if (!(data->flags & LIST_QUIET))
3541 MUI_Redraw(obj, MADF_DRAWUPDATE);
3543 return TRUE;
3546 /****** List.mui/MUIM_List_NextSelected **************************************
3548 * NAME
3549 * MUIM_List_NextSelected (V6)
3551 * SYNOPSIS
3552 * DoMethod(obj, MUIM_List_NextSelected, LONG *pos);
3554 * FUNCTION
3555 * Allows iteration through a list's selected entries by providing the
3556 * index of the next selected entry after the specified index.
3558 * INPUTS
3559 * pos - the address of a variable containing the index of the previous
3560 * selected entry. The variable must be initialised to the special
3561 * value MUIV_List_NextSelected_Start to find the first selected
3562 * entry. When this method returns, the variable will contain the
3563 * index of the next selected entry, or MUIV_List_NextSelected_End if
3564 * there are no more.
3566 * NOTES
3567 * If there are no selected entries but there is an active entry, the
3568 * index of the active entry will be stored (when
3569 * MUIV_List_NextSelected_Start is specified).
3571 * Some selected entries may be skipped if any entries are removed
3572 * between calls to this method during an iteration of a list.
3574 * MUIV_List_NextSelected_Start and MUIV_List_NextSelected_End may have
3575 * the same numeric value.
3577 * SEE ALSO
3578 * MUIM_List_Select, MUIM_List_Remove.
3580 ******************************************************************************
3584 IPTR List__MUIM_NextSelected(struct IClass *cl, Object *obj,
3585 struct MUIP_List_NextSelected *msg)
3587 struct MUI_ListData *data = INST_DATA(cl, obj);
3588 LONG pos, i;
3589 BOOL found = FALSE;
3591 /* Get the first entry to check */
3592 pos = *msg->pos;
3593 if (pos == MUIV_List_NextSelected_Start)
3594 pos = 0;
3595 else
3596 pos++;
3598 /* Find the next selected entry */
3599 for (i = pos; i < data->entries_num && !found; i++)
3601 if (data->entries[i]->flags & ENTRY_SELECTED)
3603 pos = i;
3604 found = TRUE;
3608 /* Return index of selected or active entry, or indicate there are no
3609 more */
3610 if (!found)
3612 if (*msg->pos == MUIV_List_NextSelected_Start
3613 && data->entries_active != MUIV_List_Active_Off)
3614 pos = data->entries_active;
3615 else
3616 pos = MUIV_List_NextSelected_End;
3618 *msg->pos = pos;
3620 return TRUE;
3623 /**************************************************************************
3624 MUIM_List_TestPos
3625 **************************************************************************/
3626 IPTR List__MUIM_TestPos(struct IClass *cl, Object *obj,
3627 struct MUIP_List_TestPos *msg)
3629 struct MUI_ListData *data = INST_DATA(cl, obj);
3630 struct MUI_List_TestPos_Result *result = msg->res;
3631 LONG col = -1, row = -1;
3632 UWORD flags = 0, i;
3633 LONG mx;
3634 LONG ey;
3635 LONG entries_visible;
3637 if (data->entries_visible <= data->entries_num)
3638 entries_visible = data->entries_visible;
3639 else
3640 entries_visible = data->entries_num;
3642 if ((msg->x == MUI_MAXMAX) && (msg->y == MUI_MAXMAX))
3644 mx = data->mouse_x;
3645 ey = data->mouse_y;
3647 else
3649 mx = msg->x;
3650 ey = msg->y;
3653 if ((mx != MUI_MAXMAX) && (ey != MUI_MAXMAX))
3655 mx -= _left(data->area);
3656 /* y coordinates transformed to the entries */
3657 ey -= data->entries_top_pixel;
3659 /* Now check if it was clicked on a title or on entries */
3660 if (ey < 0)
3661 flags |= MUI_LPR_ABOVE;
3662 else if (ey >= entries_visible * data->entry_maxheight)
3663 flags |= MUI_LPR_BELOW;
3664 else
3666 /* Identify row */
3667 row = ey / data->entry_maxheight + data->entries_first;
3668 result->yoffset =
3669 ey % data->entry_maxheight - data->entry_maxheight / 2;
3672 if (mx < 0)
3673 flags |= MUI_LPR_LEFT;
3674 else if (mx >= _width(data->area))
3675 flags |= MUI_LPR_RIGHT;
3676 else
3678 /* Identify column */
3679 if (data->entries_num > 0 && data->columns > 0)
3681 LONG width_sum = 0;
3682 col = data->columns - 1;
3683 for (i = 0; i < data->columns; i++)
3685 result->xoffset = mx - width_sum;
3686 width_sum +=
3687 data->ci[i].entries_width +
3688 data->ci[i].delta +
3689 (data->ci[i].bar ? BAR_WIDTH : 0);
3690 D(bug("[List/MUIM_TestPos] i %d "
3691 "width %d width_sum %d mx %d\n",
3692 i, data->ci[i].entries_width, width_sum, mx));
3693 if (mx < width_sum)
3695 col = i;
3696 D(bug("[List/MUIM_TestPos] Column hit %d\n", col));
3697 break;
3704 result->entry = row;
3705 result->column = col;
3706 result->flags = flags;
3708 return TRUE;
3711 /****i* List.mui/MUIM_DragQuery **********************************************
3713 * NAME
3714 * MUIM_DragQuery
3716 ******************************************************************************
3720 IPTR List__MUIM_DragQuery(struct IClass *cl, Object *obj,
3721 struct MUIP_DragQuery *msg)
3723 struct MUI_ListData *data = INST_DATA(cl, obj);
3725 if (msg->obj == obj && (data->flags & LIST_DRAGSORTABLE))
3726 return MUIV_DragQuery_Accept;
3727 else
3728 return MUIV_DragQuery_Refuse;
3732 /****i* List.mui/MUIM_DragFinish *********************************************
3734 * NAME
3735 * MUIM_DragFinish
3737 ******************************************************************************
3741 IPTR List__MUIM_DragFinish(struct IClass *cl, Object *obj,
3742 struct MUIP_DragFinish *msg)
3744 struct MUI_ListData *data = INST_DATA(cl, obj);
3746 data->drop_mark_y = -1;
3748 return DoSuperMethodA(cl, obj, (Msg) msg);
3751 /****i* List.mui/MUIM_DragReport *********************************************
3753 * NAME
3754 * MUIM_DragReport
3756 ******************************************************************************
3760 IPTR List__MUIM_DragReport(struct IClass *cl, Object *obj,
3761 struct MUIP_DragReport *msg)
3763 struct MUI_ListData *data = INST_DATA(cl, obj);
3764 struct MUI_List_TestPos_Result pos;
3765 struct RastPort *rp = _rp(obj);
3766 LONG n, y;
3767 UWORD old_pattern;
3769 /* Choose new drop mark position */
3771 DoMethod(obj, MUIM_List_TestPos, msg->x, msg->y, (IPTR) &pos);
3772 if (pos.entry != -1)
3774 n = pos.entry;
3775 if (pos.yoffset > 0)
3776 n++;
3778 else if ((pos.flags & MUI_LPR_ABOVE) != 0)
3779 n = data->entries_first;
3780 else
3782 n = MIN(data->entries_visible, data->entries_num)
3783 - data->entries_first;
3786 /* Clear old drop mark */
3788 if ((data->flags & LIST_SHOWDROPMARKS) != 0)
3790 y = data->entries_top_pixel + (n - data->entries_first)
3791 * data->entry_maxheight;
3792 if (y != data->drop_mark_y)
3794 DoMethod(obj, MUIM_DrawBackground, _mleft(data->area),
3795 data->drop_mark_y, _mwidth(data->area), 1, 0, 0, 0);
3797 /* Draw new drop mark and store its position */
3799 SetABPenDrMd(rp, _pens(obj)[MPEN_SHINE], _pens(obj)[MPEN_SHADOW],
3800 JAM2);
3801 old_pattern = rp->LinePtrn;
3802 SetDrPt(rp, 0xF0F0);
3803 Move(rp, _mleft(data->area), y);
3804 Draw(rp, _mright(data->area), y);
3805 SetDrPt(rp, old_pattern);
3806 data->drop_mark_y = y;
3810 return TRUE;
3814 /****i* List.mui/MUIM_DragDrop ***********************************************
3816 * NAME
3817 * MUIM_DragDrop
3819 ******************************************************************************
3823 IPTR List__MUIM_DragDrop(struct IClass *cl, Object *obj,
3824 struct MUIP_DragDrop *msg)
3826 struct MUI_ListData *data = INST_DATA(cl, obj);
3827 struct MUI_List_TestPos_Result pos;
3828 LONG n;
3830 /* Find drop position */
3832 DoMethod(obj, MUIM_List_TestPos, msg->x, msg->y, (IPTR) &pos);
3833 if (pos.entry != -1)
3835 /* Change drop position when coords move past centre of entry, not
3836 * entry boundary */
3838 n = pos.entry;
3839 if (pos.yoffset > 0)
3840 n++;
3842 else if ((pos.flags & MUI_LPR_ABOVE) != 0)
3843 n = 0;
3844 else
3845 n = data->entries_num;
3847 data->drop_mark = n;
3849 if (data->flags & LIST_DRAGSORTABLE)
3851 /* Ensure that dropped entry will be positioned between the two
3852 * entries that are above and below the drop mark, rather than
3853 * strictly at the numeric index shown */
3855 if (n > data->entries_active)
3856 n--;
3858 DoMethod(obj, MUIM_List_Move, MUIV_List_Move_Active, n);
3859 SET(obj, MUIA_List_Active, n);
3862 return TRUE;
3866 /****i* List.mui/MUIM_CreateDragImage ****************************************
3868 * NAME
3869 * MUIM_CreateDragImage
3871 ******************************************************************************
3875 static IPTR List__MUIM_CreateDragImage(struct IClass *cl, Object *obj,
3876 struct MUIP_CreateDragImage *msg)
3878 struct MUI_ListData *data = INST_DATA(cl, obj);
3879 BOOL success = TRUE;
3880 struct MUI_List_TestPos_Result pos;
3881 WORD width, height, left, top;
3882 struct MUI_DragImage *img = NULL;
3883 const struct ZuneFrameGfx *zframe;
3884 LONG depth;
3886 /* If entries aren't draggable, allow the list as a whole to be */
3887 if (data->drag_type == MUIV_Listview_DragType_None)
3888 return DoSuperMethodA(cl, obj, msg);
3890 /* Get info on dragged entry */
3891 DoMethod(obj, MUIM_List_TestPos, _left(data->area) - msg->touchx,
3892 _top(data->area) - msg->touchy, (IPTR) &pos);
3893 if (pos.entry == -1)
3894 success = FALSE;
3896 if (success)
3898 /* Get boundaries of entry */
3899 width = _mwidth(data->area);
3900 height = data->entry_maxheight;
3901 left = _mleft(data->area);
3902 top = _top(data->area) - msg->touchy
3903 - (pos.yoffset + data->entry_maxheight / 2);
3905 /* Allocate drag image structure */
3906 img = (struct MUI_DragImage *)
3907 AllocVec(sizeof(struct MUI_DragImage), MEMF_CLEAR);
3908 if (img == NULL)
3909 success = FALSE;
3912 if (success)
3914 /* Get drag frame */
3915 zframe = zune_zframe_get(obj,
3916 &muiGlobalInfo(obj)->mgi_Prefs->frames[MUIV_Frame_Drag]);
3918 /* Allocate drag image buffer */
3919 img->width = width + zframe->ileft + zframe->iright;
3920 img->height = height + zframe->itop + zframe->ibottom;
3921 depth = GetBitMapAttr(_screen(obj)->RastPort.BitMap, BMA_DEPTH);
3922 img->bm = AllocBitMap(img->width, img->height, depth, BMF_MINPLANES,
3923 _screen(obj)->RastPort.BitMap);
3925 if (img->bm != NULL)
3927 /* Render entry */
3928 struct RastPort temprp;
3929 InitRastPort(&temprp);
3930 temprp.BitMap = img->bm;
3931 ClipBlit(_rp(obj), left, top, &temprp,
3932 zframe->ileft, zframe->itop, width, height,
3933 0xc0);
3935 /* Render frame */
3936 struct RastPort *rp_save = muiRenderInfo(obj)->mri_RastPort;
3937 muiRenderInfo(obj)->mri_RastPort = &temprp;
3938 zframe->draw(zframe->customframe, muiRenderInfo(obj), 0, 0,
3939 img->width, img->height, 0, 0, img->width, img->height);
3940 muiRenderInfo(obj)->mri_RastPort = rp_save;
3943 /* Ensure drag point matches where user clicked */
3944 img->touchx = msg->touchx - zframe->ileft + _addleft(obj);
3945 img->touchy = -(pos.yoffset + data->entry_maxheight / 2)
3946 - zframe->itop;
3947 img->flags = 0;
3950 return (IPTR) img;
3953 static void DoWheelMove(struct IClass *cl, Object *obj, LONG wheely)
3955 LONG new, first, entries, visible;
3957 new = first = XGET(obj, MUIA_List_First);
3958 entries = XGET(obj, MUIA_List_Entries);
3959 visible = XGET(obj, MUIA_List_Visible);
3961 new += wheely;
3963 if (new > entries - visible)
3965 new = entries - visible;
3968 if (new < 0)
3970 new = 0;
3973 if (new != first)
3975 set(obj, MUIA_List_First, new);
3979 /**************************************************************************
3980 MUIM_HandleEvent
3981 **************************************************************************/
3982 IPTR List__MUIM_HandleEvent(struct IClass *cl, Object *obj,
3983 struct MUIP_HandleEvent *msg)
3985 struct MUI_ListData *data = INST_DATA(cl, obj);
3986 struct MUI_List_TestPos_Result pos;
3987 LONG seltype = MUIV_List_Select_On, old_active, new_active, visible,
3988 first, last, i;
3989 IPTR result = 0;
3990 BOOL select = FALSE, clear = FALSE, range_select = FALSE, changing;
3991 WORD delta;
3992 typeof(msg->muikey) muikey = msg->muikey;
3994 new_active = old_active = XGET(obj, MUIA_List_Active);
3995 visible = XGET(obj, MUIA_List_Visible);
3997 if (muikey != MUIKEY_NONE)
3999 result = MUI_EventHandlerRC_Eat;
4001 /* Make keys behave differently in read-only mode */
4002 if (data->read_only)
4004 switch (muikey)
4006 case MUIKEY_TOP:
4007 muikey = MUIKEY_LINESTART;
4008 break;
4010 case MUIKEY_BOTTOM:
4011 muikey = MUIKEY_LINEEND;
4012 break;
4014 case MUIKEY_UP:
4015 muikey = MUIKEY_LEFT;
4016 break;
4018 case MUIKEY_DOWN:
4019 case MUIKEY_PRESS:
4020 muikey = MUIKEY_RIGHT;
4021 break;
4025 switch (muikey)
4027 case MUIKEY_TOGGLE:
4028 if (data->multiselect != MUIV_Listview_MultiSelect_None
4029 && !data->read_only)
4031 select = TRUE;
4032 data->click_column = data->def_click_column;
4033 new_active = MUIV_List_Active_Down;
4035 else
4037 DoMethod(obj, MUIM_List_Jump, 0);
4038 muikey = MUIKEY_NONE;
4040 break;
4042 case MUIKEY_TOP:
4043 new_active = MUIV_List_Active_Top;
4044 break;
4046 case MUIKEY_BOTTOM:
4047 new_active = MUIV_List_Active_Bottom;
4048 break;
4050 case MUIKEY_LEFT:
4051 case MUIKEY_WORDLEFT:
4052 DoMethod(obj, MUIM_List_Jump, MUIV_List_Jump_Up);
4053 break;
4055 case MUIKEY_RIGHT:
4056 case MUIKEY_WORDRIGHT:
4057 DoMethod(obj, MUIM_List_Jump, MUIV_List_Jump_Down);
4058 break;
4060 case MUIKEY_LINESTART:
4061 DoMethod(obj, MUIM_List_Jump, MUIV_List_Jump_Top);
4062 break;
4064 case MUIKEY_LINEEND:
4065 DoMethod(obj, MUIM_List_Jump, MUIV_List_Jump_Bottom);
4066 break;
4068 case MUIKEY_UP:
4069 new_active = MUIV_List_Active_Up;
4070 break;
4072 case MUIKEY_DOWN:
4073 new_active = MUIV_List_Active_Down;
4074 break;
4076 case MUIKEY_PRESS:
4077 data->click_column = data->def_click_column;
4078 superset(cl, obj, MUIA_Listview_ClickColumn,
4079 data->click_column);
4080 set(obj, MUIA_Listview_DoubleClick, TRUE);
4081 break;
4083 case MUIKEY_PAGEUP:
4084 if (data->read_only)
4085 DoWheelMove(cl, obj, -visible);
4086 else
4087 new_active = MUIV_List_Active_PageUp;
4088 break;
4090 case MUIKEY_PAGEDOWN:
4091 if (data->read_only)
4092 DoWheelMove(cl, obj, visible);
4093 else
4094 new_active = MUIV_List_Active_PageDown;
4095 break;
4097 default:
4098 result = 0;
4101 else if (msg->imsg)
4103 DoMethod(obj, MUIM_List_TestPos, msg->imsg->MouseX, msg->imsg->MouseY,
4104 (IPTR) &pos);
4106 switch (msg->imsg->Class)
4108 case IDCMP_MOUSEBUTTONS:
4109 if (msg->imsg->Code == SELECTDOWN)
4111 if (_isinobject(data->area, msg->imsg->MouseX,
4112 msg->imsg->MouseY))
4114 data->mouse_click = MOUSE_CLICK_ENTRY;
4116 if (!data->read_only && pos.entry != -1)
4118 new_active = pos.entry;
4120 clear = (data->multiselect
4121 == MUIV_Listview_MultiSelect_Shifted
4122 && (msg->imsg->Qualifier
4123 & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)) == 0);
4124 seltype = clear ? MUIV_List_Select_On
4125 : MUIV_List_Select_Toggle;
4126 select = data->multiselect
4127 != MUIV_Listview_MultiSelect_None;
4129 /* Handle MUIA_Listview_ClickColumn */
4130 data->click_column = pos.column;
4131 superset(cl, obj, MUIA_Listview_ClickColumn,
4132 data->click_column);
4134 /* Handle double clicking */
4135 if (data->last_active == pos.entry
4136 && DoubleClick(data->last_secs, data->last_mics,
4137 msg->imsg->Seconds, msg->imsg->Micros))
4139 set(obj, MUIA_Listview_DoubleClick, TRUE);
4140 data->last_active = -1;
4141 data->last_secs = data->last_mics = 0;
4143 else
4145 data->last_active = pos.entry;
4146 data->last_secs = msg->imsg->Seconds;
4147 data->last_mics = msg->imsg->Micros;
4150 /* Look out for mouse movement, timer and
4151 inactive-window events while mouse button is
4152 down */
4153 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
4154 (IPTR) &data->ehn);
4155 data->ehn.ehn_Events |= (IDCMP_MOUSEMOVE
4156 | IDCMP_INTUITICKS |IDCMP_INACTIVEWINDOW);
4157 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
4158 (IPTR) &data->ehn);
4162 else
4164 if (msg->imsg->Code == SELECTUP && data->mouse_click)
4166 /* cache click position ... */
4167 data->mouse_x = msg->imsg->MouseX;
4168 data->mouse_y = msg->imsg->MouseY;
4170 /* ... and activate the object */
4171 set(_win(obj), MUIA_Window_ActiveObject, (IPTR)obj);
4172 data->mouse_click = 0;
4175 /* Restore normal event mask */
4176 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
4177 (IPTR) &data->ehn);
4178 data->ehn.ehn_Events &= ~(IDCMP_MOUSEMOVE | IDCMP_INTUITICKS
4179 | IDCMP_INACTIVEWINDOW);
4180 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
4181 (IPTR) &data->ehn);
4183 break;
4185 case IDCMP_MOUSEMOVE:
4186 case IDCMP_INTUITICKS:
4187 if (pos.flags & MUI_LPR_ABOVE)
4188 new_active = MUIV_List_Active_Up;
4189 else if (pos.flags & MUI_LPR_BELOW)
4190 new_active = MUIV_List_Active_Down;
4191 else
4192 new_active = pos.entry;
4194 select = new_active != old_active
4195 && data->multiselect != MUIV_Listview_MultiSelect_None;
4196 if (select)
4198 seltype = data->seltype;
4199 range_select = new_active >= 0;
4202 break;
4204 case IDCMP_INACTIVEWINDOW:
4205 /* Stop listening for events we only listen to when mouse button is
4206 down: we will not be informed of the button being released */
4207 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
4208 (IPTR) &data->ehn);
4209 data->ehn.ehn_Events &=
4210 ~(IDCMP_MOUSEMOVE | IDCMP_INTUITICKS | IDCMP_INACTIVEWINDOW);
4211 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
4212 (IPTR) &data->ehn);
4213 break;
4215 case IDCMP_RAWKEY:
4216 /* Scroll wheel */
4217 if (data->vert && _isinobject(data->vert, msg->imsg->MouseX,
4218 msg->imsg->MouseY))
4219 delta = 1;
4220 else if (_isinobject(data->area, msg->imsg->MouseX,
4221 msg->imsg->MouseY))
4222 delta = 4;
4223 else
4224 delta = 0;
4226 if (delta != 0)
4228 switch (msg->imsg->Code)
4230 case RAWKEY_NM_WHEEL_UP:
4231 DoWheelMove(cl, obj, -delta);
4232 break;
4234 case RAWKEY_NM_WHEEL_DOWN:
4235 DoWheelMove(cl, obj, delta);
4236 break;
4238 result = MUI_EventHandlerRC_Eat;
4240 break;
4244 /* Decide in advance if any selections may change */
4245 changing = clear || muikey == MUIKEY_TOGGLE || select;
4247 /* Change selected and active entries */
4248 if (clear)
4250 DoMethod(obj, MUIM_List_Select, MUIV_List_Select_All,
4251 MUIV_List_Select_Off, NULL);
4254 if (muikey == MUIKEY_TOGGLE)
4256 DoMethod(obj, MUIM_List_Select, MUIV_List_Select_Active,
4257 MUIV_List_Select_Toggle, NULL);
4258 select = FALSE;
4261 if (new_active != old_active)
4262 set(obj, MUIA_List_Active, new_active);
4264 if (select)
4266 if (range_select)
4268 if (old_active < new_active)
4269 first = old_active + 1, last = new_active;
4270 else
4271 first = new_active, last = old_active - 1;
4272 for (i = first; i <= last; i++)
4273 DoMethod(obj, MUIM_List_Select, i, seltype, NULL);
4275 else
4277 DoMethod(obj, MUIM_List_Select, MUIV_List_Select_Active,
4278 seltype, NULL);
4279 DoMethod(obj, MUIM_List_Select, MUIV_List_Select_Active,
4280 MUIV_List_Select_Ask, &data->seltype);
4284 if (changing)
4285 superset(cl, obj, MUIA_Listview_SelectChange, TRUE);
4287 return result;
4290 /**************************************************************************
4291 Dispatcher
4292 **************************************************************************/
4293 BOOPSI_DISPATCHER(IPTR, List_Dispatcher, cl, obj, msg)
4295 switch (msg->MethodID)
4297 case OM_NEW:
4298 return List__OM_NEW(cl, obj, (struct opSet *)msg);
4299 case OM_DISPOSE:
4300 return List__OM_DISPOSE(cl, obj, msg);
4301 case OM_SET:
4302 return List__OM_SET(cl, obj, (struct opSet *)msg);
4303 case OM_GET:
4304 return List__OM_GET(cl, obj, (struct opGet *)msg);
4306 case MUIM_Setup:
4307 return List__MUIM_Setup(cl, obj, (struct MUIP_Setup *)msg);
4308 case MUIM_Cleanup:
4309 return List__MUIM_Cleanup(cl, obj, (struct MUIP_Cleanup *)msg);
4310 case MUIM_HandleEvent:
4311 return List__MUIM_HandleEvent(cl, obj, (struct MUIP_HandleEvent *)msg);
4312 case MUIM_AskMinMax:
4313 return List__MUIM_AskMinMax(cl, obj, (struct MUIP_AskMinMax *)msg);
4314 case MUIM_Show:
4315 return List__MUIM_Show(cl, obj, (struct MUIP_Show *)msg);
4316 case MUIM_Hide:
4317 return List__MUIM_Hide(cl, obj, (struct MUIP_Hide *)msg);
4318 case MUIM_Draw:
4319 return List__MUIM_Draw(cl, obj, (struct MUIP_Draw *)msg);
4320 case MUIM_Layout:
4321 return List__MUIM_Layout(cl, obj, (struct MUIP_Layout *)msg);
4322 case MUIM_List_Clear:
4323 return List__MUIM_Clear(cl, obj, (struct MUIP_List_Clear *)msg);
4324 case MUIM_List_Sort:
4325 return List__MUIM_Sort(cl, obj, (struct MUIP_List_Sort *)msg);
4326 case MUIM_List_Exchange:
4327 return List__MUIM_Exchange(cl, obj,
4328 (struct MUIP_List_Exchange *)msg);
4329 case MUIM_List_Insert:
4330 return List__MUIM_Insert(cl, obj, (APTR) msg);
4331 case MUIM_List_InsertSingle:
4332 return List__MUIM_InsertSingle(cl, obj, (APTR) msg);
4333 case MUIM_List_GetEntry:
4334 return List__MUIM_GetEntry(cl, obj, (APTR) msg);
4335 case MUIM_List_Redraw:
4336 return List__MUIM_Redraw(cl, obj, (APTR) msg);
4337 case MUIM_List_Remove:
4338 return List__MUIM_Remove(cl, obj, (APTR) msg);
4339 case MUIM_List_Select:
4340 return List__MUIM_Select(cl, obj, (APTR) msg);
4341 case MUIM_List_Construct:
4342 return List__MUIM_Construct(cl, obj, (APTR) msg);
4343 case MUIM_List_Destruct:
4344 return List__MUIM_Destruct(cl, obj, (APTR) msg);
4345 case MUIM_List_Compare:
4346 return List__MUIM_Compare(cl, obj, (APTR) msg);
4347 case MUIM_List_Display:
4348 return List__MUIM_Display(cl, obj, (APTR) msg);
4349 case MUIM_List_SelectChange:
4350 return List__MUIM_SelectChange(cl, obj, (APTR) msg);
4351 case MUIM_List_CreateImage:
4352 return List__MUIM_CreateImage(cl, obj, (APTR) msg);
4353 case MUIM_List_DeleteImage:
4354 return List__MUIM_DeleteImage(cl, obj, (APTR) msg);
4355 case MUIM_List_Jump:
4356 return List__MUIM_Jump(cl, obj, (APTR) msg);
4357 case MUIM_List_Move:
4358 return List__MUIM_Move(cl, obj, (struct MUIP_List_Move *)msg);
4359 case MUIM_List_NextSelected:
4360 return List__MUIM_NextSelected(cl, obj,
4361 (struct MUIP_List_NextSelected *)msg);
4362 case MUIM_List_TestPos:
4363 return List__MUIM_TestPos(cl, obj, (APTR) msg);
4364 case MUIM_DragQuery:
4365 return List__MUIM_DragQuery(cl, obj, (APTR) msg);
4366 case MUIM_DragFinish:
4367 return List__MUIM_DragFinish(cl, obj, (APTR) msg);
4368 case MUIM_DragReport:
4369 return List__MUIM_DragReport(cl, obj, (APTR) msg);
4370 case MUIM_DragDrop:
4371 return List__MUIM_DragDrop(cl, obj, (APTR) msg);
4372 case MUIM_CreateDragImage:
4373 return List__MUIM_CreateDragImage(cl, obj, (APTR) msg);
4376 return DoSuperMethodA(cl, obj, msg);
4378 BOOPSI_DISPATCHER_END
4381 * Class descriptor.
4383 const struct __MUIBuiltinClass _MUI_List_desc =
4385 MUIC_List,
4386 MUIC_Group,
4387 sizeof(struct MUI_ListData),
4388 (void *) List_Dispatcher