muimaster.library: remove commented out code from List constructor
[AROS.git] / workbench / libs / muimaster / classes / list.c
blob4a5c0f201eef7e73f6ee2accfa5ae0b2142a5069
1 /*
2 Copyright © 2002-2015, The AROS Development Team. All rights reserved.
3 $Id$
4 */
6 #include <string.h>
7 #include <stdlib.h>
9 #include <exec/memory.h>
10 #include <graphics/gfx.h>
11 #include <graphics/gfxmacros.h>
12 #include <graphics/view.h>
13 #include <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 enum
43 ARG_DELTA,
44 ARG_PREPARSE,
45 ARG_WEIGHT,
46 ARG_MINWIDTH,
47 ARG_MAXWIDTH,
48 ARG_COL,
49 ARG_BAR,
50 ARG_CNT
54 struct ListEntry
56 APTR data;
57 LONG *widths; /* Widths of the columns */
58 LONG width; /* Line width */
59 LONG height; /* Line height */
60 WORD flags; /* see below */
63 #define ENTRY_SELECTED (1<<0)
66 struct ColumnInfo
68 int colno; /* Column number */
69 int user_width; /* user set width; -1 if entry width */
70 int min_width; /* min width percentage */
71 int max_width; /* min width percentage */
72 int weight;
73 int delta; /* ignored for the first and last column, defaults to 4 */
74 int bar;
75 STRPTR preparse;
76 int entries_width; /* width of the entries (maximum of all widths) */
79 struct MUI_ImageSpec_intern;
81 struct MUI_ListData
83 /* bool attrs */
84 ULONG flags;
86 APTR intern_pool; /* The internal pool which the class has allocated */
87 LONG intern_puddle_size;
88 LONG intern_thresh_size;
89 APTR pool; /* the pool which is used to allocate list entries */
91 struct Hook *construct_hook;
92 struct Hook *compare_hook;
93 struct Hook *destruct_hook;
94 struct Hook *display_hook;
95 struct Hook *multi_test_hook;
97 struct Hook default_compare_hook;
99 /* List management, currently we use a simple flat array, which is not
100 * good if many entries are inserted/deleted */
101 LONG entries_num; /* Number of Entries in the list */
102 LONG entries_allocated;
103 struct ListEntry **entries;
105 LONG entries_first; /* first visible entry */
106 LONG entries_visible; /* number of visible entries,
107 * determined at MUIM_Layout */
108 LONG entries_active;
109 LONG insert_position; /* pos of the last insertion */
111 LONG entry_maxheight; /* Maximum height of an entry */
112 ULONG entry_minheight; /* from MUIA_List_MinLineHeight */
114 LONG entries_totalheight;
115 LONG entries_maxwidth;
117 LONG vertprop_entries;
118 LONG vertprop_visible;
119 LONG vertprop_first;
121 LONG confirm_entries_num; /* These are the correct entries num, used
122 * so you cannot set MUIA_List_Entries to
123 * wrong values */
125 LONG entries_top_pixel; /* Where the entries start */
127 /* Column managment, is allocated by ParseListFormat() and freed
128 * by CleanListFormat() */
129 STRPTR format;
130 LONG columns; /* Number of columns the list has */
131 struct ColumnInfo *ci;
132 STRPTR *preparses;
133 STRPTR *strings; /* the strings for the display function, one
134 * more than needed (for the entry position) */
136 /* Titlestuff */
137 int title_height; /* The complete height of the title */
138 STRPTR title; /* On single column lists this is the title,
139 * otherwise 1. NULL for no title(s) */
141 /* Cursor images */
142 struct MUI_ImageSpec_intern *list_cursor;
143 struct MUI_ImageSpec_intern *list_select;
144 struct MUI_ImageSpec_intern *list_selcur;
146 /* Render optimization */
147 int update; /* 1 - update everything, 2 - redraw entry at update_pos,
148 * 3 - scroll to current entries_first (old value is in
149 * update_pos) */
150 int update_pos;
152 LONG drop_mark_y;
154 /* list images */
155 struct MinList images;
157 /* user prefs */
158 ListviewRefresh prefs_refresh;
159 UWORD prefs_linespacing;
160 BOOL prefs_smoothed;
161 UWORD prefs_smoothval;
163 /* render space handling */
164 Object *area;
165 BOOL area_replaced;
166 BOOL area_connected;
168 /***************************/
169 /* Former Listview members */
170 /***************************/
172 Object *vert;
173 BOOL vert_connected;
174 IPTR scroller_pos;
175 BOOL read_only;
176 IPTR multiselect;
178 /* clicked column */
179 LONG click_column;
180 LONG def_click_column;
182 LONG mouse_click; /* see below if mouse is held down */
184 /* double click */
185 ULONG last_secs;
186 ULONG last_mics;
187 ULONG last_active;
189 struct MUI_EventHandlerNode ehn;
191 /* user prefs */
192 ListviewMulti prefs_multi;
194 BOOL select_change;
195 BOOL doubleclick;
197 struct Hook hook;
200 #define MOUSE_CLICK_ENTRY 1 /* on entry clicked */
201 #define MOUSE_CLICK_TITLE 2 /* on title clicked */
203 #define LIST_ADJUSTWIDTH (1<<0)
204 #define LIST_ADJUSTHEIGHT (1<<1)
205 #define LIST_AUTOVISIBLE (1<<2)
206 #define LIST_DRAGSORTABLE (1<<3)
207 #define LIST_SHOWDROPMARKS (1<<4)
208 #define LIST_QUIET (1<<5)
211 /****** List.mui/MUIA_List_CompareHook ***************************************
213 * NAME
214 * MUIA_List_CompareHook -- (V4) [IS.], struct Hook *
216 * FUNCTION
217 * The provided hook indicates the sort ordering of two list entries.
218 * The hook receives list-entry data pointers as its second and third
219 * arguments. The hook should return a negative value if the first entry
220 * should be placed before the second entry, a positive value if the
221 * first entry should be placed after the second entry, and zero if the
222 * entries are equal.
224 * In addition to being used internally for sorting operations, this hook
225 * will be called when MUIM_List_Compare is externally invoked.
227 * If this attribute is not specified or is set to NULL, all list entries
228 * must be strings.
230 ******************************************************************************
234 /****** List.mui/MUIA_List_MultiTestHook *************************************
236 * NAME
237 * MUIA_List_MultiTestHook -- (V4) [IS.], struct Hook *
239 * FUNCTION
240 * The provided hook indicates whether a particular list entry
241 * may be multiselected. The hook receives the list-entry data pointer as
242 * its third argument, and returns a Boolean value. If this attribute is
243 * not specified or is set to NULL, all list entries are considered
244 * multi-selectable.
246 * Whenever an entry is about to be selected, this hook is called if
247 * there are other entries already selected. If the hook returns TRUE,
248 * the entry may be multi-selected; if the hook returns FALSE, the entry
249 * remains unselected.
251 * Additionally, if a non-multi-selectable entry has been selected (as
252 * the only selected entry in the list), any attempt to select an
253 * additional entry will fail.
255 ******************************************************************************
259 /**************************************************************************
260 Allocate a single list entry, does not initialize it (except the pointer)
261 **************************************************************************/
262 static struct ListEntry *AllocListEntry(struct MUI_ListData *data)
264 ULONG *mem;
265 struct ListEntry *le;
266 int size = sizeof(struct ListEntry) + sizeof(LONG) * data->columns + 4;
267 /* sizeinfo */
268 LONG j;
270 mem = AllocPooled(data->pool, size);
271 if (!mem)
272 return NULL;
273 D(bug("List AllocListEntry %p, %ld bytes\n", mem, size));
275 mem[0] = size; /* Save the size */
276 le = (struct ListEntry *)(mem + 1);
277 le->widths = (LONG *) (le + 1);
279 /* Initialize fields */
280 le->height = 0;
281 le->width = 0;
282 le->flags = 0;
283 for (j = 0; j < data->columns; j++)
284 le->widths[j] = 0;
286 return le;
289 /**************************************************************************
290 Deallocate a single list entry, does not deinitialize it
291 **************************************************************************/
292 static void FreeListEntry(struct MUI_ListData *data,
293 struct ListEntry *entry)
295 ULONG *mem = ((ULONG *) entry) - 1;
296 D(bug("FreeListEntry %p size=%ld\n", mem, mem[0]));
297 FreePooled(data->pool, mem, mem[0]);
300 /**************************************************************************
301 Ensures that there can be at least the given amount of entries within
302 the list. Returns 0 if not. It also allocates the space for the title.
303 It can be accessed with data->entries[ENTRY_TITLE]
304 **************************************************************************/
305 static int SetListSize(struct MUI_ListData *data, LONG size)
307 struct ListEntry **new_entries;
308 int new_entries_allocated;
310 if (size + 1 <= data->entries_allocated)
311 return 1;
313 new_entries_allocated = data->entries_allocated * 2 + 4;
314 if (new_entries_allocated < size + 1)
315 new_entries_allocated = size + 1 + 10; /* 10 is just random */
317 D(bug("List %p : SetListSize allocating %ld bytes\n", data,
318 new_entries_allocated * sizeof(struct ListEntry *)));
319 new_entries =
320 AllocVec(new_entries_allocated * sizeof(struct ListEntry *), 0);
321 if (NULL == new_entries)
322 return 0;
323 if (data->entries)
325 CopyMem(data->entries - 1, new_entries,
326 (data->entries_num + 1) * sizeof(struct ListEntry *));
327 FreeVec(data->entries - 1);
329 data->entries = new_entries + 1;
330 data->entries_allocated = new_entries_allocated;
331 return 1;
334 /**************************************************************************
335 Prepares the insertion of count entries at pos.
336 This function doesn't care if there is enough space in the datastructure.
337 SetListSize() must be used first.
338 With current implementation, this call will never fail
339 **************************************************************************/
340 static int PrepareInsertListEntries(struct MUI_ListData *data, int pos,
341 int count)
343 memmove(&data->entries[pos + count], &data->entries[pos],
344 (data->entries_num - pos) * sizeof(struct ListEntry *));
345 return 1;
348 /**************************************************************************
349 Removes count (already deinitalized) list entries starting az pos.
350 **************************************************************************/
351 static void RemoveListEntries(struct MUI_ListData *data, int pos, int count)
353 // FIXME: segfault if entries_num = pos = count = 1
354 memmove(&data->entries[pos], &data->entries[pos + count],
355 (data->entries_num - (pos + count)) * sizeof(struct ListEntry *));
358 /**************************************************************************
359 Frees all memory allocated by ParseListFormat()
360 **************************************************************************/
361 static void FreeListFormat(struct MUI_ListData *data)
363 int i;
365 if (data->ci)
367 for (i = 0; i < data->columns; i++)
369 FreeVec(data->ci[i].preparse);
370 data->ci[i].preparse = NULL;
372 FreeVec(data->ci);
373 data->ci = NULL;
375 FreeVec(data->preparses);
376 data->preparses = NULL;
377 if (data->strings)
379 FreeVec(data->strings - 1);
380 data->strings = NULL;
382 data->columns = 0;
385 /**************************************************************************
386 Parses the given format string (also frees a previously parsed format).
387 Return 0 on failure.
388 **************************************************************************/
389 static int ParseListFormat(struct MUI_ListData *data, STRPTR format)
391 int new_columns, i;
392 STRPTR ptr;
393 STRPTR format_sep;
394 char c;
396 IPTR args[ARG_CNT];
397 struct RDArgs *rdargs;
399 if (!format)
400 format = (STRPTR) "";
402 ptr = format;
404 FreeListFormat(data);
406 new_columns = 1;
408 /* Count the number of columns first */
409 while ((c = *ptr++))
410 if (c == ',')
411 new_columns++;
413 if (!(data->preparses =
414 AllocVec((new_columns + 10) * sizeof(STRPTR), 0)))
415 return 0;
417 if (!(data->strings = AllocVec((new_columns + 1 + 10)
418 * sizeof(STRPTR), 0))) /* hold enough space also for the entry pos,
419 * used by orginal MUI and also some
420 * security space */
421 return 0;
423 if (!(data->ci = AllocVec(new_columns * sizeof(struct ColumnInfo), 0)))
424 return 0;
426 // set defaults
427 for (i = 0; i < new_columns; i++)
429 data->ci[i].colno = -1; // -1 means: use unassigned column
430 data->ci[i].weight = 100;
431 data->ci[i].delta = 4;
432 data->ci[i].min_width = -1;
433 data->ci[i].max_width = -1;
434 data->ci[i].user_width = -1;
435 data->ci[i].bar = FALSE;
436 data->ci[i].preparse = NULL;
439 if ((format_sep = StrDup(format)) != 0)
441 for (i = 0; format_sep[i] != '\0'; i++)
443 if (format_sep[i] == ',')
444 format_sep[i] = '\0';
447 if ((rdargs = AllocDosObject(DOS_RDARGS, NULL)) != 0)
449 ptr = format_sep;
450 i = 0;
453 rdargs->RDA_Source.CS_Buffer = ptr;
454 rdargs->RDA_Source.CS_Length = strlen(ptr);
455 rdargs->RDA_Source.CS_CurChr = 0;
456 rdargs->RDA_DAList = 0;
457 rdargs->RDA_Buffer = NULL;
458 rdargs->RDA_BufSiz = 0;
459 rdargs->RDA_ExtHelp = NULL;
460 rdargs->RDA_Flags = 0;
462 memset(args, 0, sizeof args);
463 if (ReadArgs(FORMAT_TEMPLATE, args, rdargs))
465 if (args[ARG_COL])
466 data->ci[i].colno = *(LONG *) args[ARG_COL];
467 if (args[ARG_WEIGHT])
468 data->ci[i].weight = *(LONG *) args[ARG_WEIGHT];
469 if (args[ARG_DELTA])
470 data->ci[i].delta = *(LONG *) args[ARG_DELTA];
471 if (args[ARG_MINWIDTH])
472 data->ci[i].min_width =
473 *(LONG *) args[ARG_MINWIDTH];
474 if (args[ARG_MAXWIDTH])
475 data->ci[i].max_width =
476 *(LONG *) args[ARG_MAXWIDTH];
477 data->ci[i].bar = args[ARG_BAR];
478 if (args[ARG_PREPARSE])
479 data->ci[i].preparse =
480 StrDup((STRPTR) args[ARG_PREPARSE]);
482 FreeArgs(rdargs);
484 ptr += strlen(ptr) + 1;
485 i++;
487 while (i < new_columns);
488 FreeDosObject(DOS_RDARGS, rdargs);
490 FreeVec(format_sep);
493 for (i = 0; i < new_columns; i++)
495 D(bug("colno %d weight %d delta %d preparse %s\n",
496 data->ci[i].colno, data->ci[i].weight, data->ci[i].delta,
497 data->ci[i].preparse));
500 data->columns = new_columns;
501 data->strings++; /* Skip entry pos */
503 return 1;
506 /**************************************************************************
507 Call the MUIM_List_Display for the given entry. It fills out
508 data->string and data->preparses
509 **************************************************************************/
510 static void DisplayEntry(struct IClass *cl, Object *obj, int entry_pos)
512 struct MUI_ListData *data = INST_DATA(cl, obj);
513 APTR entry_data;
514 int col;
516 for (col = 0; col < data->columns; col++)
517 data->preparses[col] = data->ci[col].preparse;
519 if (entry_pos == ENTRY_TITLE)
521 if ((data->columns == 1) && (data->title != (STRPTR) 1))
523 *data->strings = data->title;
524 return;
526 entry_data = NULL; /* it's a title request */
528 else
529 entry_data = data->entries[entry_pos]->data;
531 /* Get the display formation */
532 DoMethod(obj, MUIM_List_Display, (IPTR) entry_data,
533 (IPTR) data->strings, entry_pos, (IPTR) data->preparses);
536 /**************************************************************************
537 Determine the dims of a single entry and adapt the columninfo according
538 to it. pos might be ENTRY_TITLE. Returns 0 if pos entry needs to
539 be redrawn after this operation, 1 if all entries need to be redrawn.
540 **************************************************************************/
541 static int CalcDimsOfEntry(struct IClass *cl, Object *obj, int pos)
543 struct MUI_ListData *data = INST_DATA(cl, obj);
544 struct ListEntry *entry = data->entries[pos];
545 int j;
546 int ret = 0;
548 if (!entry)
549 return ret;
551 if (!(_flags(obj) & MADF_SETUP))
552 return ret;
554 DisplayEntry(cl, obj, pos);
556 /* Set height to at least minheight */
557 if (data->entries[pos]->height < data->entry_minheight)
558 data->entries[pos]->height = data->entry_minheight;
560 for (j = 0; j < data->columns; j++)
562 ZText *text =
563 zune_text_new(data->preparses[j], data->strings[j],
564 ZTEXT_ARG_NONE, 0);
565 if (text != NULL)
567 zune_text_get_bounds(text, obj);
569 if (text->height > data->entries[pos]->height)
571 data->entries[pos]->height = text->height;
572 /* entry height changed, redraw all entries later */
573 ret = 1;
575 data->entries[pos]->widths[j] = text->width;
577 if (text->width > data->ci[j].entries_width)
579 /* This columns width is bigger than the other in the same
580 * columns, so we store this value
582 data->ci[j].entries_width = text->width;
583 /* column width changed, redraw all entries later */
584 ret = 1;
587 zune_text_destroy(text);
590 if (data->entries[pos]->height > data->entry_maxheight)
592 data->entry_maxheight = data->entries[pos]->height;
593 /* maximum entry height changed, redraw all entries later */
594 ret = 1;
597 return ret;
600 /**************************************************************************
601 Determine the widths of the entries
602 **************************************************************************/
603 static void CalcWidths(struct IClass *cl, Object *obj)
605 int i, j;
606 struct MUI_ListData *data = INST_DATA(cl, obj);
608 if (!(_flags(obj) & MADF_SETUP))
609 return;
611 for (j = 0; j < data->columns; j++)
612 data->ci[j].entries_width = 0;
614 data->entry_maxheight = 0;
615 data->entries_totalheight = 0;
616 data->entries_maxwidth = 0;
618 for (i = (data->title ? ENTRY_TITLE : 0); i < data->entries_num; i++)
620 CalcDimsOfEntry(cl, obj, i);
621 data->entries_totalheight += data->entries[i]->height;
624 for (j = 0; j < data->columns; j++)
625 data->entries_maxwidth += data->ci[j].entries_width;
627 if (!data->entry_maxheight)
628 data->entry_maxheight = 1;
631 /**************************************************************************
632 Calculates the number of visible entry lines. Returns 1 if it has
633 changed
634 **************************************************************************/
635 static int CalcVertVisible(struct IClass *cl, Object *obj)
637 struct MUI_ListData *data = INST_DATA(cl, obj);
638 int old_entries_visible = data->entries_visible;
639 int old_entries_top_pixel = data->entries_top_pixel;
641 data->entries_visible = (_mheight(data->area) - data->title_height)
642 / (data->entry_maxheight /* + data->prefs_linespacing */ );
644 /* Distribute extra vertical space evenly between top and bottom of
645 * list */
647 data->entries_top_pixel = _mtop(data->area) + data->title_height
648 + (_mheight(data->area) - data->title_height
650 data->entries_visible *
651 (data->entry_maxheight /* + data->prefs_linespacing */ )) / 2;
653 return (old_entries_visible != data->entries_visible)
654 || (old_entries_top_pixel != data->entries_top_pixel);
657 /**************************************************************************
658 Default hook to compare two list entries. Works for strings only.
659 **************************************************************************/
660 AROS_UFH3S(int, default_compare_func,
661 AROS_UFHA(struct Hook *, h, A0),
662 AROS_UFHA(char *, s2, A2),
663 AROS_UFHA(char *, s1, A1))
665 AROS_USERFUNC_INIT
667 return Stricmp(s1, s2);
669 AROS_USERFUNC_EXIT
672 #define PROP_VERT_FIRST 1
674 static ULONG List_Function(struct Hook *hook, Object * obj, void **msg)
676 struct MUI_ListData *data = (struct MUI_ListData *)hook->h_Data;
677 SIPTR type = (SIPTR) msg[0];
678 SIPTR val = (SIPTR) msg[1];
680 switch (type)
682 case PROP_VERT_FIRST:
683 get(data->vert, MUIA_Prop_First, &val);
684 nnset(obj, MUIA_List_VertProp_First, val);
685 break;
687 return 0;
690 /* At entry to this function, data->area is always set, but data->vert may or may not be set */
691 static void List_HandleScrollerPos(struct IClass *cl, Object *obj)
693 struct MUI_ListData *data = INST_DATA(cl, obj);
694 BOOL vert_not_used = FALSE;
696 /* Disallow any changes after setup, this function should basically a creation-time only */
697 if (_flags(obj) & MADF_SETUP)
698 return;
700 /* Remove both objects */
701 if (data->area_connected)
702 DoMethod(obj, OM_REMMEMBER, data->area);
703 if (data->vert_connected)
704 DoMethod(obj, OM_REMMEMBER, data->vert);
706 /* Add list and/or scroller */
707 switch (data->scroller_pos)
709 case MUIV_Listview_ScrollerPos_None:
710 vert_not_used = TRUE;
711 DoMethod(obj, OM_ADDMEMBER, data->area);
712 break;
713 case MUIV_Listview_ScrollerPos_Left:
714 if (!data->vert) data->vert = ScrollbarObject, MUIA_Group_Horiz, FALSE, End;
715 DoMethod(obj, OM_ADDMEMBER, data->vert);
716 DoMethod(obj, OM_ADDMEMBER, data->area);
717 break;
718 default:
719 if (!data->vert) data->vert = ScrollbarObject, MUIA_Group_Horiz, FALSE, End;
720 DoMethod(obj, OM_ADDMEMBER, data->area);
721 DoMethod(obj, OM_ADDMEMBER, data->vert);
722 break;
725 data->area_connected = TRUE;
727 /* Handle case where it was decided that vert will not be used */
728 if (vert_not_used)
730 if (data->vert)
732 if (data->vert_connected)
734 DoMethod(obj, MUIM_KillNotifyObj, MUIA_List_VertProp_First, (IPTR) data->vert);
735 DoMethod(obj, MUIM_KillNotifyObj, MUIA_List_VertProp_Visible, (IPTR) data->vert);
736 DoMethod(obj, MUIM_KillNotifyObj, MUIA_List_VertProp_Entries, (IPTR) data->vert);
737 data->vert_connected = FALSE;
740 MUI_DisposeObject(data->vert);
741 data->vert = NULL;
745 /* If at this point data->vert is not null, it means vert is to be connected */
746 if (data->vert && !data->vert_connected)
748 LONG entries = 0, first = 0, visible = 0;
750 get(obj, MUIA_List_VertProp_First, &first);
751 get(obj, MUIA_List_VertProp_Visible, &visible);
752 get(obj, MUIA_List_VertProp_Entries, &entries);
754 SetAttrs(data->vert,
755 MUIA_Prop_First, first,
756 MUIA_Prop_Visible, visible, MUIA_Prop_Entries, entries, TAG_DONE);
759 DoMethod(data->vert, MUIM_Notify, MUIA_Prop_First, MUIV_EveryTime, (IPTR) obj,
760 4, MUIM_CallHook, (IPTR) &data->hook, PROP_VERT_FIRST,
761 MUIV_TriggerValue);
763 /* Pass prop object as DestObj (based on code in NList) */
764 DoMethod(obj, MUIM_Notify, MUIA_List_VertProp_First, MUIV_EveryTime,
765 (IPTR) data->vert, 3, MUIM_NoNotifySet, MUIA_Prop_First, MUIV_TriggerValue);
766 DoMethod(obj, MUIM_Notify, MUIA_List_VertProp_Visible, MUIV_EveryTime,
767 (IPTR) data->vert, 3, MUIM_NoNotifySet, MUIA_Prop_Visible, MUIV_TriggerValue);
768 DoMethod(obj, MUIM_Notify, MUIA_List_VertProp_Entries, MUIV_EveryTime,
769 (IPTR) data->vert, 3, MUIM_NoNotifySet, MUIA_Prop_Entries, MUIV_TriggerValue);
771 data->vert_connected = TRUE;
776 /**************************************************************************
777 OM_NEW
778 **************************************************************************/
779 IPTR List__OM_NEW(struct IClass *cl, Object *obj, struct opSet *msg)
781 struct MUI_ListData *data;
782 struct TagItem *tag;
783 struct TagItem *tags;
784 APTR *array = NULL;
785 LONG new_entries_active = MUIV_List_Active_Off;
786 struct TagItem rectattrs[2] = {{TAG_IGNORE, TAG_IGNORE }, {TAG_DONE, TAG_DONE}};
787 Object *area;
789 /* search for MUIA_Frame as it has to be passed to rectangle object */
790 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
792 if (tag->ti_Tag == MUIA_Frame)
794 rectattrs[0].ti_Tag = MUIA_Frame;
795 rectattrs[0].ti_Data = tag->ti_Data;
796 tag->ti_Tag = TAG_IGNORE;
797 break;
801 obj = (Object *) DoSuperNewTags(cl, obj, NULL,
802 MUIA_Group_Horiz, TRUE,
803 MUIA_InnerLeft, 0,
804 MUIA_InnerRight, 0,
805 MUIA_Group_Spacing, 0,
806 MUIA_Font, MUIV_Font_List,
807 MUIA_ShowSelState, FALSE,
808 MUIA_InputMode, MUIV_InputMode_RelVerify,
809 MUIA_Background, MUII_ListBack,
810 TAG_MORE, (IPTR) msg->ops_AttrList,
811 TAG_DONE);
813 if (!obj)
814 return FALSE;
816 data = INST_DATA(cl, obj);
818 data->columns = 1;
819 data->entries_active = MUIV_List_Active_Off;
820 data->intern_puddle_size = 2008;
821 data->intern_thresh_size = 1024;
822 data->default_compare_hook.h_Entry = (HOOKFUNC) default_compare_func;
823 data->default_compare_hook.h_SubEntry = 0;
824 data->compare_hook = &(data->default_compare_hook);
825 data->flags = LIST_SHOWDROPMARKS;
826 data->area_replaced = FALSE;
827 data->area_connected = FALSE;
828 data->vert_connected = FALSE;
830 data->last_active = -1;
832 data->ehn.ehn_Events = IDCMP_MOUSEBUTTONS | IDCMP_RAWKEY;
833 data->ehn.ehn_Priority = 0;
834 data->ehn.ehn_Flags = 0;
835 data->ehn.ehn_Object = obj;
836 data->ehn.ehn_Class = cl;
838 data->hook.h_Entry = HookEntry;
839 data->hook.h_SubEntry = (HOOKFUNC) List_Function;
840 data->hook.h_Data = data;
842 area = (Object *)GetTagData(MUIA_List_ListArea, (IPTR) 0, msg->ops_AttrList);
844 if (!area)
845 area = RectangleObject, MUIA_FillArea, FALSE, TAG_MORE, (IPTR) rectattrs, End;
846 else
847 data->area_replaced = TRUE;
848 data->area = area;
850 /* parse initial taglist */
851 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
853 switch (tag->ti_Tag)
855 case MUIA_List_Active:
856 new_entries_active = tag->ti_Data;
857 break;
859 case MUIA_List_Pool:
860 data->pool = (APTR) tag->ti_Data;
861 break;
863 case MUIA_List_PoolPuddleSize:
864 data->intern_puddle_size = tag->ti_Data;
865 break;
867 case MUIA_List_PoolThreshSize:
868 data->intern_thresh_size = tag->ti_Data;
869 break;
871 case MUIA_List_CompareHook:
872 data->compare_hook = (struct Hook *)tag->ti_Data;
873 if (data->compare_hook == NULL)
874 data->compare_hook = &data->default_compare_hook;
875 break;
877 case MUIA_List_ConstructHook:
878 data->construct_hook = (struct Hook *)tag->ti_Data;
879 break;
881 case MUIA_List_DestructHook:
882 data->destruct_hook = (struct Hook *)tag->ti_Data;
883 break;
885 case MUIA_List_DisplayHook:
886 data->display_hook = (struct Hook *)tag->ti_Data;
887 break;
889 case MUIA_List_MultiTestHook:
890 data->multi_test_hook = (struct Hook *)tag->ti_Data;
891 break;
893 case MUIA_List_SourceArray:
894 array = (APTR *) tag->ti_Data;
895 break;
897 case MUIA_List_Format:
898 data->format = StrDup((STRPTR) tag->ti_Data);
899 break;
901 case MUIA_List_Title:
902 data->title = (STRPTR) tag->ti_Data;
903 break;
905 case MUIA_List_MinLineHeight:
906 data->entry_minheight = tag->ti_Data;
907 break;
909 case MUIA_List_AdjustHeight:
910 _handle_bool_tag(data->flags, tag->ti_Data, LIST_ADJUSTHEIGHT);
911 break;
913 case MUIA_List_AdjustWidth:
914 _handle_bool_tag(data->flags, tag->ti_Data, LIST_ADJUSTWIDTH);
915 break;
917 case MUIA_List_AutoVisible:
918 _handle_bool_tag(data->flags, tag->ti_Data, LIST_AUTOVISIBLE);
919 break;
921 case MUIA_List_ShowDropMarks:
922 _handle_bool_tag(data->flags, tag->ti_Data, LIST_SHOWDROPMARKS);
923 break;
925 case MUIA_List_DragSortable:
926 _handle_bool_tag(data->flags, tag->ti_Data, LIST_DRAGSORTABLE);
927 set(obj, MUIA_Draggable, tag->ti_Data);
928 break;
930 case MUIA_Listview_ScrollerPos:
931 data->scroller_pos = tag->ti_Data;
932 break;
934 case MUIA_Listview_Input:
935 data->read_only = !tag->ti_Data;
936 break;
938 case MUIA_Listview_MultiSelect:
939 data->multiselect = tag->ti_Data;
940 break;
942 case MUIA_Listview_DoubleClick:
943 data->doubleclick = tag->ti_Data != 0;
944 break;
948 List_HandleScrollerPos(cl, obj);
950 if (!data->pool)
952 /* No memory pool given, so we create our own */
953 data->pool = data->intern_pool =
954 CreatePool(0, data->intern_puddle_size,
955 data->intern_thresh_size);
956 if (!data->pool)
958 CoerceMethod(cl, obj, OM_DISPOSE);
959 return 0;
963 /* parse the list format */
964 if (!(ParseListFormat(data, data->format)))
966 CoerceMethod(cl, obj, OM_DISPOSE);
967 return 0;
970 /* This is neccessary for at least the title */
971 if (!SetListSize(data, 0))
973 CoerceMethod(cl, obj, OM_DISPOSE);
974 return 0;
977 if (data->title)
979 if (!(data->entries[ENTRY_TITLE] = AllocListEntry(data)))
981 CoerceMethod(cl, obj, OM_DISPOSE);
982 return 0;
985 else
986 data->entries[ENTRY_TITLE] = NULL;
988 if (array)
990 int i;
991 /* Count the number of elements */
992 for (i = 0; array[i] != NULL; i++)
994 /* Insert them */
995 DoMethod(obj, MUIM_List_Insert, (IPTR) array, i,
996 MUIV_List_Insert_Top);
999 if ((data->entries_num) && (new_entries_active != MUIV_List_Active_Off))
1001 switch (new_entries_active)
1003 case MUIV_List_Active_Top:
1004 new_entries_active = 0;
1005 break;
1007 case MUIV_List_Active_Bottom:
1008 new_entries_active = data->entries_num - 1;
1009 break;
1012 if (new_entries_active < 0)
1013 new_entries_active = 0;
1014 else if (new_entries_active >= data->entries_num)
1015 new_entries_active = data->entries_num - 1;
1017 data->entries_active = new_entries_active;
1018 /* Selected entry will be moved into visible area */
1021 NewList((struct List *)&data->images);
1023 D(bug("List_New(%lx)\n", obj));
1025 return (IPTR) obj;
1028 /**************************************************************************
1029 OM_DISPOSE
1030 **************************************************************************/
1031 IPTR List__OM_DISPOSE(struct IClass *cl, Object *obj, Msg msg)
1033 struct MUI_ListData *data = INST_DATA(cl, obj);
1035 D(bug("List Dispose\n"));
1037 /* Call destruct method for every entry and free the entries manually
1038 * to avoid notification */
1039 while (data->confirm_entries_num)
1041 struct ListEntry *lentry =
1042 data->entries[--data->confirm_entries_num];
1043 DoMethod(obj, MUIM_List_Destruct, (IPTR) lentry->data,
1044 (IPTR) data->pool);
1045 FreeListEntry(data, lentry);
1048 if (data->intern_pool)
1049 DeletePool(data->intern_pool);
1050 if (data->entries)
1051 FreeVec(data->entries - 1);
1052 /* title is currently before all other elements */
1054 FreeListFormat(data);
1055 FreeVec(data->format);
1057 return DoSuperMethodA(cl, obj, msg);
1061 /**************************************************************************
1062 OM_SET
1063 **************************************************************************/
1064 IPTR List__OM_SET(struct IClass *cl, Object *obj, struct opSet *msg)
1066 struct MUI_ListData *data = INST_DATA(cl, obj);
1067 struct TagItem *tag;
1068 struct TagItem *tags;
1070 /* parse taglist */
1071 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
1073 switch (tag->ti_Tag)
1075 case MUIA_List_CompareHook:
1076 data->compare_hook = (struct Hook *)tag->ti_Data;
1077 if (data->compare_hook == NULL)
1078 data->compare_hook = &data->default_compare_hook;
1079 break;
1081 case MUIA_List_ConstructHook:
1082 data->construct_hook = (struct Hook *)tag->ti_Data;
1083 break;
1085 case MUIA_List_DestructHook:
1086 data->destruct_hook = (struct Hook *)tag->ti_Data;
1087 break;
1089 case MUIA_List_DisplayHook:
1090 data->display_hook = (struct Hook *)tag->ti_Data;
1091 break;
1093 case MUIA_List_MultiTestHook:
1094 data->multi_test_hook = (struct Hook *)tag->ti_Data;
1095 if (data->multi_test_hook != NULL)
1097 /* Clearing current selections is the easiest way to keep
1098 * selections consistent with the new hook */
1099 DoMethod(obj, MUIM_List_Select, MUIV_List_Select_All,
1100 MUIV_List_Select_Off, NULL);
1102 break;
1104 case MUIA_List_Title:
1105 data->title = (STRPTR) tag->ti_Data;
1106 DoMethod(obj, MUIM_List_Redraw, MUIV_List_Redraw_All);
1107 break;
1109 case MUIA_List_VertProp_First:
1110 data->vertprop_first = tag->ti_Data;
1111 if (data->entries_first != tag->ti_Data)
1113 set(obj, MUIA_List_First, tag->ti_Data);
1115 break;
1117 case MUIA_List_Format:
1118 data->format = StrDup((STRPTR) tag->ti_Data);
1119 ParseListFormat(data, data->format);
1120 // FIXME: should we check for errors?
1121 DoMethod(obj, MUIM_List_Redraw, MUIV_List_Redraw_All);
1122 break;
1124 case MUIA_List_VertProp_Entries:
1125 data->vertprop_entries = tag->ti_Data;
1126 break;
1128 case MUIA_List_VertProp_Visible:
1129 data->vertprop_visible = tag->ti_Data;
1130 data->entries_visible = tag->ti_Data;
1131 break;
1133 case MUIA_List_Active:
1135 LONG new_entries_active = tag->ti_Data;
1137 if ((data->entries_num)
1138 && (new_entries_active != MUIV_List_Active_Off))
1140 switch (new_entries_active)
1142 case MUIV_List_Active_Top:
1143 new_entries_active = 0;
1144 break;
1146 case MUIV_List_Active_Bottom:
1147 new_entries_active = data->entries_num - 1;
1148 break;
1150 case MUIV_List_Active_Up:
1151 new_entries_active = data->entries_active - 1;
1152 break;
1154 case MUIV_List_Active_Down:
1155 new_entries_active = data->entries_active + 1;
1156 break;
1158 case MUIV_List_Active_PageUp:
1159 new_entries_active =
1160 data->entries_active - data->entries_visible;
1161 break;
1163 case MUIV_List_Active_PageDown:
1164 new_entries_active =
1165 data->entries_active + data->entries_visible;
1166 break;
1169 if (new_entries_active < 0)
1170 new_entries_active = 0;
1171 else if (new_entries_active >= data->entries_num)
1172 new_entries_active = data->entries_num - 1;
1174 else
1175 new_entries_active = -1;
1177 if (data->entries_active != new_entries_active)
1179 LONG old = data->entries_active;
1180 data->entries_active = new_entries_active;
1182 /* Selectchange stuff */
1183 if (new_entries_active != -1)
1185 DoMethod(obj, MUIM_List_SelectChange,
1186 new_entries_active, MUIV_List_Select_On, 0);
1187 DoMethod(obj, MUIM_List_SelectChange,
1188 new_entries_active, MUIV_List_Select_Active, 0);
1190 else
1191 DoMethod(obj, MUIM_List_SelectChange,
1192 MUIV_List_Active_Off, MUIV_List_Select_Off, 0);
1194 data->update = 2;
1195 data->update_pos = old;
1196 MUI_Redraw(obj, MADF_DRAWUPDATE);
1197 data->update = 2;
1198 data->update_pos = data->entries_active;
1199 MUI_Redraw(obj, MADF_DRAWUPDATE);
1201 /* Make new active entry visible (if there is one and
1202 list is visible) */
1203 if (new_entries_active != -1
1204 && (_flags(obj) & MADF_SETUP))
1206 DoMethod(obj, MUIM_List_Jump,
1207 MUIV_List_Jump_Active);
1211 break;
1213 case MUIA_List_First:
1214 data->update_pos = data->entries_first;
1215 data->update = 3;
1216 data->entries_first = tag->ti_Data;
1218 MUI_Redraw(obj, MADF_DRAWUPDATE);
1219 if ((data->vertprop_first != tag->ti_Data)
1220 && (!(data->flags & LIST_QUIET)))
1222 set(obj, MUIA_List_VertProp_First, tag->ti_Data);
1224 break;
1226 case MUIA_List_Visible: /* Shouldn't be settable? */
1227 if (data->vertprop_visible != tag->ti_Data)
1228 set(obj, MUIA_List_VertProp_Visible, tag->ti_Data);
1229 break;
1231 case MUIA_List_Entries:
1232 if (data->confirm_entries_num == tag->ti_Data)
1234 data->entries_num = tag->ti_Data;
1235 if (!(data->flags & LIST_QUIET))
1237 set(obj, MUIA_List_VertProp_Entries, data->entries_num);
1240 else
1242 D(bug("Bug: confirm_entries != MUIA_List_Entries!\n"));
1244 break;
1246 case MUIA_List_Quiet:
1247 _handle_bool_tag(data->flags, tag->ti_Data, LIST_QUIET);
1248 if (!tag->ti_Data)
1250 DoMethod(obj, MUIM_List_Redraw, MUIV_List_Redraw_All);
1251 if (data->entries_num != XGET(obj, MUIA_List_VertProp_Entries))
1252 set(obj, MUIA_List_VertProp_Entries, data->entries_num);
1253 if (data->entries_first != XGET(obj, MUIA_List_VertProp_First))
1254 set(obj, MUIA_List_VertProp_First, data->entries_first);
1256 break;
1258 case MUIA_List_AutoVisible:
1259 _handle_bool_tag(data->flags, tag->ti_Data, LIST_AUTOVISIBLE);
1260 break;
1262 case MUIA_List_ShowDropMarks:
1263 _handle_bool_tag(data->flags, tag->ti_Data, LIST_SHOWDROPMARKS);
1264 break;
1266 case MUIA_List_DragSortable:
1267 _handle_bool_tag(data->flags, tag->ti_Data, LIST_DRAGSORTABLE);
1268 set(obj, MUIA_Draggable, tag->ti_Data);
1269 break;
1271 case MUIA_Selected:
1272 /* Swallow this so the Area class doesn't redraw us */
1273 tag->ti_Tag = TAG_IGNORE;
1274 break;
1276 case MUIA_Disabled:
1277 if (_flags(obj) & MADF_SETUP)
1279 /* Stop listening for events we only listen to when mouse
1280 button is down: we will not be informed of the button
1281 being released */
1282 DoMethod(_win(obj), MUIM_Window_RemEventHandler, (IPTR) &data->ehn);
1283 data->ehn.ehn_Events &= ~(IDCMP_MOUSEMOVE | IDCMP_INTUITICKS | IDCMP_INACTIVEWINDOW);
1284 DoMethod(_win(obj), MUIM_Window_AddEventHandler, (IPTR) &data->ehn);
1286 break;
1288 case MUIA_Listview_DoubleClick: /* private set */
1289 data->doubleclick = tag->ti_Data != 0;
1290 break;
1292 case MUIA_Listview_SelectChange: /* private set */
1293 data->select_change = tag->ti_Data != 0;
1294 break;
1296 case MUIA_Listview_ScrollerPos: /* private set */
1297 data->scroller_pos = tag->ti_Data;
1298 List_HandleScrollerPos(cl, obj);
1299 break;
1301 case MUIA_Listview_Input: /* private set */
1302 data->read_only = !tag->ti_Data;
1303 break;
1305 case MUIA_Listview_MultiSelect: /* private set */
1306 data->multiselect = tag->ti_Data;
1307 break;
1311 return DoSuperMethodA(cl, obj, (Msg) msg);
1314 /**************************************************************************
1315 OM_GET
1316 **************************************************************************/
1317 IPTR List__OM_GET(struct IClass *cl, Object *obj, struct opGet *msg)
1319 /* small macro to simplify return value storage */
1320 #define STORE *(msg->opg_Storage)
1321 struct MUI_ListData *data = INST_DATA(cl, obj);
1323 switch (msg->opg_AttrID)
1325 case MUIA_List_Entries:
1326 STORE = data->entries_num;
1327 return 1;
1328 case MUIA_List_First:
1329 STORE = data->entries_first;
1330 return 1;
1331 case MUIA_List_Active:
1332 STORE = data->entries_active;
1333 return 1;
1334 case MUIA_List_InsertPosition:
1335 STORE = data->insert_position;
1336 return 1;
1337 case MUIA_List_Title:
1338 STORE = (IPTR) data->title;
1339 return 1;
1340 case MUIA_List_VertProp_Entries:
1341 STORE = data->vertprop_entries;
1342 return 1;
1343 case MUIA_List_VertProp_Visible:
1344 case MUIA_List_Visible:
1345 STORE = data->vertprop_visible;
1346 return 1;
1347 case MUIA_List_VertProp_First:
1348 STORE = data->vertprop_first;
1349 return 1;
1350 case MUIA_List_Format:
1351 STORE = (IPTR) data->format;
1352 return 1;
1353 case MUIA_List_AutoVisible:
1354 STORE = data->flags & LIST_AUTOVISIBLE;
1355 return 1;
1356 case MUIA_List_ShowDropMarks:
1357 STORE = data->flags & LIST_SHOWDROPMARKS;
1358 return 1;
1359 case MUIA_List_DragSortable:
1360 STORE = data->flags & LIST_DRAGSORTABLE;
1361 return 1;
1362 case MUIA_Listview_ClickColumn:
1363 STORE = data->click_column;
1364 return 1;
1365 case MUIA_Listview_DoubleClick:
1366 STORE = data->doubleclick;
1367 return 1;
1368 case MUIA_Listview_SelectChange:
1369 STORE = data->select_change;
1370 return 1;
1371 case MUIA_Listview_List:
1372 STORE = (IPTR)obj;
1373 return 1;
1376 if (DoSuperMethodA(cl, obj, (Msg) msg))
1377 return 1;
1378 return 0;
1379 #undef STORE
1382 /**************************************************************************
1383 MUIM_Setup
1384 **************************************************************************/
1385 IPTR List__MUIM_Setup(struct IClass *cl, Object *obj,
1386 struct MUIP_Setup *msg)
1388 struct MUI_ListData *data = INST_DATA(cl, obj);
1390 if (!DoSuperMethodA(cl, obj, (Msg) msg))
1391 return 0;
1393 data->prefs_refresh = muiGlobalInfo(obj)->mgi_Prefs->list_refresh;
1394 data->prefs_linespacing =
1395 muiGlobalInfo(obj)->mgi_Prefs->list_linespacing;
1396 data->prefs_smoothed = muiGlobalInfo(obj)->mgi_Prefs->list_smoothed;
1397 data->prefs_smoothval = muiGlobalInfo(obj)->mgi_Prefs->list_smoothval;
1399 CalcWidths(cl, obj);
1401 data->list_cursor =
1402 zune_imspec_setup(MUII_ListCursor, muiRenderInfo(obj));
1403 data->list_select =
1404 zune_imspec_setup(MUII_ListSelect, muiRenderInfo(obj));
1405 data->list_selcur =
1406 zune_imspec_setup(MUII_ListSelCur, muiRenderInfo(obj));
1408 data->prefs_multi = muiGlobalInfo(obj)->mgi_Prefs->list_multi;
1409 if (data->multiselect == MUIV_Listview_MultiSelect_Default)
1411 if (data->prefs_multi == LISTVIEW_MULTI_SHIFTED)
1412 data->multiselect = MUIV_Listview_MultiSelect_Shifted;
1413 else
1414 data->multiselect = MUIV_Listview_MultiSelect_Always;
1417 DoMethod(_win(obj), MUIM_Window_AddEventHandler, (IPTR) &data->ehn);
1419 return 1;
1422 /**************************************************************************
1423 MUIM_Cleanup
1424 **************************************************************************/
1425 IPTR List__MUIM_Cleanup(struct IClass *cl, Object *obj,
1426 struct MUIP_Cleanup *msg)
1428 struct MUI_ListData *data = INST_DATA(cl, obj);
1429 struct ListImage *li = List_First(&data->images);
1431 while (li)
1433 struct ListImage *next = Node_Next(li);
1434 DoMethod(obj, MUIM_List_DeleteImage, (IPTR) li);
1435 li = next;
1438 zune_imspec_cleanup(data->list_cursor);
1439 zune_imspec_cleanup(data->list_select);
1440 zune_imspec_cleanup(data->list_selcur);
1442 DoMethod(_win(obj), MUIM_Window_RemEventHandler, (IPTR) &data->ehn);
1443 data->mouse_click = 0;
1445 return DoSuperMethodA(cl, obj, (Msg) msg);
1448 /**************************************************************************
1449 MUIM_AskMinMax
1450 **************************************************************************/
1451 IPTR List__MUIM_AskMinMax(struct IClass *cl, Object *obj,
1452 struct MUIP_AskMinMax *msg)
1454 struct MUI_ListData *data = INST_DATA(cl, obj);
1456 DoSuperMethodA(cl, obj, (Msg) msg);
1459 if ((data->flags & LIST_ADJUSTWIDTH) && (data->entries_num > 0))
1461 msg->MinMaxInfo->MinWidth += data->entries_maxwidth;
1462 msg->MinMaxInfo->DefWidth += data->entries_maxwidth;
1463 msg->MinMaxInfo->MaxWidth += data->entries_maxwidth;
1465 else
1467 msg->MinMaxInfo->MinWidth += 40;
1468 msg->MinMaxInfo->DefWidth += 100;
1469 msg->MinMaxInfo->MaxWidth = MUI_MAXMAX;
1472 if (data->entries_num > 0)
1474 if (data->flags & LIST_ADJUSTHEIGHT)
1476 msg->MinMaxInfo->MinHeight += data->entries_totalheight;
1477 msg->MinMaxInfo->DefHeight += data->entries_totalheight;
1478 msg->MinMaxInfo->MaxHeight += data->entries_totalheight;
1480 else
1482 ULONG h = data->entry_maxheight + data->prefs_linespacing;
1483 msg->MinMaxInfo->MinHeight += 2 * h + data->prefs_linespacing;
1484 msg->MinMaxInfo->DefHeight += 8 * h + data->prefs_linespacing;
1485 msg->MinMaxInfo->MaxHeight = MUI_MAXMAX;
1488 else
1490 msg->MinMaxInfo->MinHeight += 36;
1491 msg->MinMaxInfo->DefHeight += 96;
1492 msg->MinMaxInfo->MaxHeight = MUI_MAXMAX;
1494 D(bug("List %p minheigh=%d, line maxh=%d\n",
1495 obj, msg->MinMaxInfo->MinHeight, data->entry_maxheight));
1496 return TRUE;
1499 /****i* List.mui/MUIM_Layout *************************************************
1501 * NAME
1502 * MUIM_Layout
1504 ******************************************************************************
1508 IPTR List__MUIM_Layout(struct IClass *cl, Object *obj,
1509 struct MUIP_Layout *msg)
1511 struct MUI_ListData *data = INST_DATA(cl, obj);
1512 ULONG rc = DoSuperMethodA(cl, obj, (Msg) msg);
1513 LONG new_entries_first = data->entries_first;
1515 /* Calc the numbers of entries visible */
1516 CalcVertVisible(cl, obj);
1518 /* Ensure active entry is visible if requested */
1519 if (data->entries_active + 1 >=
1520 (data->entries_first + data->entries_visible)
1521 && (data->flags & LIST_AUTOVISIBLE) != 0)
1522 new_entries_first =
1523 data->entries_active - data->entries_visible + 1;
1525 /* Ensure there are no unnecessary empty lines */
1526 if ((new_entries_first + data->entries_visible >=
1527 data->entries_num)
1528 && (data->entries_visible <= data->entries_num))
1529 new_entries_first = data->entries_num - data->entries_visible;
1531 /* Always show the start of the list if it isn't long enough to fill the
1532 view */
1533 if (data->entries_num <= data->entries_visible)
1534 new_entries_first = 0;
1536 if (new_entries_first < 0)
1537 new_entries_first = 0;
1539 set(obj, new_entries_first != data->entries_first ?
1540 MUIA_List_First : TAG_IGNORE, new_entries_first);
1542 /* So the notify happens */
1543 set(obj, MUIA_List_VertProp_Visible, data->entries_visible);
1545 return rc;
1549 /**************************************************************************
1550 MUIM_Show
1551 **************************************************************************/
1552 IPTR List__MUIM_Show(struct IClass *cl, Object *obj,
1553 struct MUIP_Show *msg)
1555 struct MUI_ListData *data = INST_DATA(cl, obj);
1556 ULONG rc = DoSuperMethodA(cl, obj, (Msg) msg);
1558 zune_imspec_show(data->list_cursor, obj);
1559 zune_imspec_show(data->list_select, obj);
1560 zune_imspec_show(data->list_selcur, obj);
1561 return rc;
1565 /**************************************************************************
1566 MUIM_Hide
1567 **************************************************************************/
1568 IPTR List__MUIM_Hide(struct IClass *cl, Object *obj,
1569 struct MUIP_Hide *msg)
1571 struct MUI_ListData *data = INST_DATA(cl, obj);
1573 zune_imspec_hide(data->list_cursor);
1574 zune_imspec_hide(data->list_select);
1575 zune_imspec_hide(data->list_selcur);
1577 return DoSuperMethodA(cl, obj, (Msg) msg);
1581 /**************************************************************************
1582 Draw an entry at entry_pos at the given row. To draw the title, set pos to
1583 ENTRY_TITLE
1584 **************************************************************************/
1585 static VOID List_DrawEntry(struct IClass *cl, Object *obj, int entry_pos,
1586 int y)
1588 struct MUI_ListData *data = INST_DATA(cl, obj);
1589 int col, x1, x2;
1591 /* To be sure we don't draw anything if there is no title */
1592 if (entry_pos == ENTRY_TITLE && !data->title)
1593 return;
1595 DisplayEntry(cl, obj, entry_pos);
1596 x1 = _mleft(data->area);
1598 for (col = 0; col < data->columns; col++)
1600 ZText *text;
1601 x2 = x1 + data->ci[col].entries_width;
1603 if ((text =
1604 zune_text_new(data->preparses[col], data->strings[col],
1605 ZTEXT_ARG_NONE, 0)))
1607 /* Could be made simpler, as we don't really need the bounds */
1608 zune_text_get_bounds(text, obj);
1609 /* Note, this was MPEN_SHADOW before */
1610 SetAPen(_rp(obj), muiRenderInfo(obj)->mri_Pens[MPEN_TEXT]);
1611 zune_text_draw(text, obj, x1, x2, y); /* totally wrong! */
1612 zune_text_destroy(text);
1614 x1 = x2 + data->ci[col].delta + (data->ci[col].bar ? BAR_WIDTH : 0);
1618 /**************************************************************************
1619 MUIM_Draw
1620 **************************************************************************/
1621 IPTR List__MUIM_Draw(struct IClass *cl, Object *obj, struct MUIP_Draw *msg)
1623 struct MUI_ListData *data = INST_DATA(cl, obj);
1624 int entry_pos, y;
1625 APTR clip;
1626 int start, end;
1627 BOOL scroll_caused_damage = FALSE;
1628 struct MUI_ImageSpec_intern *highlight;
1629 IPTR ret = (IPTR)0;
1631 if (data->flags & LIST_QUIET)
1632 return 0;
1634 ret = DoSuperMethodA(cl, obj, (Msg) msg);
1636 if (data->area_replaced)
1637 return ret;
1639 /* Calculate the title height */
1640 if (data->title)
1642 data->title_height = data->entries[ENTRY_TITLE]->height + 2;
1644 else
1646 data->title_height = 0;
1649 /* Calc the numbers of entries visible */
1650 CalcVertVisible(cl, obj);
1652 if ((msg->flags & MADF_DRAWUPDATE) == 0 || data->update == 1)
1654 DoMethod(obj, MUIM_DrawBackground, _mleft(data->area), _mtop(data->area),
1655 _mwidth(data->area), _mheight(data->area),
1656 0, data->entries_first * data->entry_maxheight, 0);
1659 clip = MUI_AddClipping(muiRenderInfo(obj), _mleft(data->area), _mtop(data->area),
1660 _mwidth(data->area), _mheight(data->area));
1662 if ((msg->flags & MADF_DRAWUPDATE) == 0 || data->update == 1)
1664 y = _mtop(data->area);
1665 /* Draw Title
1667 if (data->title_height && data->title)
1669 List_DrawEntry(cl, obj, ENTRY_TITLE, y);
1670 y += data->entries[ENTRY_TITLE]->height;
1671 SetAPen(_rp(obj), _pens(obj)[MPEN_SHADOW]);
1672 Move(_rp(obj), _mleft(data->area), y);
1673 Draw(_rp(obj), _mright(data->area), y);
1674 SetAPen(_rp(obj), _pens(obj)[MPEN_SHINE]);
1675 y++;
1676 Move(_rp(obj), _mleft(data->area), y);
1677 Draw(_rp(obj), _mright(data->area), y);
1681 y = data->entries_top_pixel;
1683 start = data->entries_first;
1684 end = data->entries_first + data->entries_visible;
1686 if ((msg->flags & MADF_DRAWUPDATE) && data->update == 3)
1688 int diffy = data->entries_first - data->update_pos;
1689 int top, bottom;
1690 if (abs(diffy) < data->entries_visible)
1692 scroll_caused_damage =
1693 (_rp(obj)->Layer->Flags & LAYERREFRESH) ? FALSE : TRUE;
1695 ScrollRaster(_rp(obj), 0, diffy * data->entry_maxheight,
1696 _mleft(data->area), y,
1697 _mright(data->area),
1698 y + data->entry_maxheight * data->entries_visible);
1700 scroll_caused_damage =
1701 scroll_caused_damage
1702 && (_rp(obj)->Layer->Flags & LAYERREFRESH);
1704 if (diffy > 0)
1706 start = end - diffy;
1707 y += data->entry_maxheight * (data->entries_visible -
1708 diffy);
1710 else
1711 end = start - diffy;
1714 top = y;
1715 bottom = y + (end - start) * data->entry_maxheight;
1717 DoMethod(obj, MUIM_DrawBackground, _mleft(data->area), top,
1718 _mwidth(data->area), bottom - top + 1,
1720 top - _mtop(data->area) + data->entries_first * data->entry_maxheight,
1724 for (entry_pos = start;
1725 entry_pos < end && entry_pos < data->entries_num; entry_pos++)
1727 struct ListEntry *entry = data->entries[entry_pos];
1729 if (!(msg->flags & MADF_DRAWUPDATE) ||
1730 ((msg->flags & MADF_DRAWUPDATE) && data->update == 1) ||
1731 ((msg->flags & MADF_DRAWUPDATE) && data->update == 3) ||
1732 ((msg->flags & MADF_DRAWUPDATE) && data->update == 2
1733 && data->update_pos == entry_pos))
1735 /* Choose appropriate highlight image */
1737 if (entry_pos == data->entries_active
1738 && entry->flags & ENTRY_SELECTED)
1739 highlight = data->list_selcur;
1740 else if (entry_pos == data->entries_active)
1741 highlight = data->list_cursor;
1742 else if (entry->flags & ENTRY_SELECTED)
1743 highlight = data->list_select;
1744 else
1745 highlight = NULL;
1747 /* Draw highlight or background */
1749 if (highlight != NULL)
1751 zune_imspec_draw(highlight, muiRenderInfo(obj),
1752 _mleft(data->area), y, _mwidth(data->area), data->entry_maxheight,
1753 0, y - data->entries_top_pixel, 0);
1755 else if ((msg->flags & MADF_DRAWUPDATE) && data->update == 2
1756 && data->update_pos == entry_pos)
1758 DoMethod(obj, MUIM_DrawBackground, _mleft(data->area), y,
1759 _mwidth(data->area), data->entry_maxheight, 0,
1760 y - _mtop(data->area) +
1761 data->entries_first * data->entry_maxheight, 0);
1764 List_DrawEntry(cl, obj, entry_pos, y);
1766 y += data->entry_maxheight;
1769 MUI_RemoveClipping(muiRenderInfo(obj), clip);
1771 data->update = 0;
1773 if (scroll_caused_damage)
1775 if (MUI_BeginRefresh(muiRenderInfo(obj), 0))
1777 /* Theoretically it might happen that more damage is caused
1778 after ScrollRaster. By something else, like window movement
1779 in front of our window. Therefore refresh root object of
1780 window, not just this object */
1782 Object *o = NULL;
1784 get(_win(obj), MUIA_Window_RootObject, &o);
1785 MUI_Redraw(o, MADF_DRAWOBJECT);
1787 MUI_EndRefresh(muiRenderInfo(obj), 0);
1791 ULONG x1 = _mleft(data->area);
1792 ULONG col;
1793 y = _mtop(data->area);
1795 if (data->title_height && data->title)
1797 for (col = 0; col < data->columns; col++)
1799 ULONG halfdelta = data->ci[col].delta / 2;
1800 x1 += data->ci[col].entries_width + halfdelta;
1802 if (x1 + (data->ci[col].bar ? BAR_WIDTH : 0) > _mright(data->area))
1803 break;
1805 if (data->ci[col].bar)
1807 SetAPen(_rp(obj), _pens(obj)[MPEN_SHINE]);
1808 Move(_rp(obj), x1, y);
1809 Draw(_rp(obj), x1,
1810 y + data->entries[ENTRY_TITLE]->height - 1);
1811 SetAPen(_rp(obj), _pens(obj)[MPEN_SHADOW]);
1812 Move(_rp(obj), x1 + 1, y);
1813 Draw(_rp(obj), x1 + 1,
1814 y + data->entries[ENTRY_TITLE]->height - 1);
1816 x1 += BAR_WIDTH;
1818 x1 += data->ci[col].delta - halfdelta;
1820 y += data->entries[ENTRY_TITLE]->height + 1;
1823 x1 = _mleft(data->area);
1825 for (col = 0; col < data->columns; col++)
1827 ULONG halfdelta = data->ci[col].delta / 2;
1828 x1 += data->ci[col].entries_width + halfdelta;
1830 if (x1 + (data->ci[col].bar ? BAR_WIDTH : 0) > _mright(data->area))
1831 break;
1833 if (data->ci[col].bar)
1835 SetAPen(_rp(obj), _pens(obj)[MPEN_SHINE]);
1836 Move(_rp(obj), x1, y);
1837 Draw(_rp(obj), x1, _mbottom(data->area));
1838 SetAPen(_rp(obj), _pens(obj)[MPEN_SHADOW]);
1839 Move(_rp(obj), x1 + 1, y);
1840 Draw(_rp(obj), x1 + 1, _mbottom(data->area));
1842 x1 += BAR_WIDTH;
1845 x1 += data->ci[col].delta - halfdelta;
1848 return 0;
1851 /****** List.mui/MUIM_List_Clear *********************************************
1853 * NAME
1854 * MUIM_List_Clear (V4)
1856 * SYNOPSIS
1857 * DoMethod(obj, MUIM_List_Clear);
1859 * FUNCTION
1860 * Removes all entries from the list.
1862 ******************************************************************************
1866 IPTR List__MUIM_Clear(struct IClass *cl, Object *obj,
1867 struct MUIP_List_Clear *msg)
1869 struct MUI_ListData *data = INST_DATA(cl, obj);
1871 while (data->confirm_entries_num)
1873 struct ListEntry *lentry =
1874 data->entries[--data->confirm_entries_num];
1875 DoMethod(obj, MUIM_List_Destruct, (IPTR) lentry->data,
1876 (IPTR) data->pool);
1877 FreeListEntry(data, lentry);
1879 /* Should never fail when shrinking */
1880 SetListSize(data, 0);
1883 if (data->confirm_entries_num != data->entries_num)
1885 SetAttrs(obj, MUIA_List_Entries, 0, MUIA_List_First, 0,
1886 /* Notify only when no entry was active */
1887 data->entries_active !=
1888 MUIV_List_Active_Off ? MUIA_List_Active : TAG_DONE,
1889 MUIV_List_Active_Off, TAG_DONE);
1891 data->update = 1;
1892 MUI_Redraw(obj, MADF_DRAWUPDATE);
1895 return 0;
1898 /**************************************************************************
1899 MUIM_List_Exchange
1900 **************************************************************************/
1901 IPTR List__MUIM_Exchange(struct IClass *cl, Object *obj,
1902 struct MUIP_List_Exchange *msg)
1904 struct MUI_ListData *data = INST_DATA(cl, obj);
1905 LONG pos1, pos2;
1907 switch (msg->pos1)
1909 case MUIV_List_Exchange_Top:
1910 pos1 = 0;
1911 break;
1912 case MUIV_List_Exchange_Active:
1913 pos1 = data->entries_active;
1914 break;
1915 case MUIV_List_Exchange_Bottom:
1916 pos1 = data->entries_num - 1;
1917 break;
1918 default:
1919 pos1 = msg->pos1;
1922 switch (msg->pos2)
1924 case MUIV_List_Exchange_Top:
1925 pos2 = 0;
1926 break;
1927 case MUIV_List_Exchange_Active:
1928 pos2 = data->entries_active;
1929 break;
1930 case MUIV_List_Exchange_Bottom:
1931 pos2 = data->entries_num - 1;
1932 break;
1933 case MUIV_List_Exchange_Next:
1934 pos2 = pos1 + 1;
1935 break;
1936 case MUIV_List_Exchange_Previous:
1937 pos2 = pos1 - 1;
1938 break;
1939 default:
1940 pos2 = msg->pos2;
1943 if (pos1 >= 0 && pos1 < data->entries_num && pos2 >= 0
1944 && pos2 < data->entries_num && pos1 != pos2)
1946 struct ListEntry *save = data->entries[pos1];
1947 data->entries[pos1] = data->entries[pos2];
1948 data->entries[pos2] = save;
1950 data->update = 2;
1951 data->update_pos = pos1;
1952 MUI_Redraw(obj, MADF_DRAWUPDATE);
1954 data->update = 2;
1955 data->update_pos = pos2;
1956 MUI_Redraw(obj, MADF_DRAWUPDATE);
1958 return TRUE;
1960 else
1962 return FALSE;
1966 /**************************************************************************
1967 MUIM_List_Redraw
1968 **************************************************************************/
1969 IPTR List__MUIM_Redraw(struct IClass *cl, Object *obj,
1970 struct MUIP_List_Redraw *msg)
1972 struct MUI_ListData *data = INST_DATA(cl, obj);
1974 if (!(data->flags & LIST_QUIET))
1976 if (msg->pos == MUIV_List_Redraw_All)
1978 data->update = 1;
1979 CalcWidths(cl, obj);
1980 MUI_Redraw(obj, MADF_DRAWUPDATE);
1982 else
1984 LONG pos = -1;
1985 if (msg->pos == MUIV_List_Redraw_Active)
1986 pos = data->entries_active;
1987 else if (msg->pos == MUIV_List_Redraw_Entry)
1989 LONG i;
1990 for (i = 0; i < data->entries_num; i++)
1991 if (data->entries[i]->data == msg->entry)
1993 pos = i;
1994 break;
1997 else
1998 pos = msg->pos;
2000 if (pos != -1)
2002 if (CalcDimsOfEntry(cl, obj, pos))
2003 data->update = 1;
2004 else
2006 data->update = 2;
2007 data->update_pos = pos;
2009 MUI_Redraw(obj, MADF_DRAWUPDATE);
2013 return 0;
2016 /**************************************************************************
2017 MUIM_List_Remove
2018 **************************************************************************/
2019 IPTR List__MUIM_Remove(struct IClass *cl, Object *obj,
2020 struct MUIP_List_Remove *msg)
2022 struct MUI_ListData *data = INST_DATA(cl, obj);
2023 LONG pos, cur;
2024 LONG new_act;
2025 struct ListEntry *lentry;
2026 //int rem_count = 1;
2028 if (!data->entries_num)
2029 return 0;
2031 switch (msg->pos)
2033 case MUIV_List_Remove_First:
2034 pos = 0;
2035 break;
2037 case MUIV_List_Remove_Active:
2038 pos = data->entries_active;
2039 break;
2041 case MUIV_List_Remove_Last:
2042 pos = data->entries_num - 1;
2043 break;
2045 case MUIV_List_Remove_Selected:
2046 /* TODO: needs special handling */
2047 pos = data->entries_active;
2048 break;
2050 default:
2051 pos = msg->pos;
2052 break;
2055 if (pos < 0 || pos >= data->entries_num)
2056 return 0;
2058 new_act = data->entries_active;
2060 if (pos == new_act && new_act == data->entries_num - 1)
2061 new_act--; /* might become MUIV_List_Active_Off */
2063 lentry = data->entries[pos];
2064 DoMethod(obj, MUIM_List_Destruct, (IPTR) lentry->data,
2065 (IPTR) data->pool);
2067 cur = pos + 1;
2069 RemoveListEntries(data, pos, cur - pos);
2070 data->confirm_entries_num -= cur - pos;
2072 /* ensure that the active element is in a valid range */
2073 if (new_act >= data->entries_num)
2074 new_act = data->entries_num - 1;
2076 SetAttrs(obj, MUIA_List_Entries, data->confirm_entries_num,
2077 (new_act >= pos) || (new_act != data->entries_active) ?
2078 MUIA_List_Active : TAG_DONE,
2079 new_act, /* Inform only if neccessary (for notify) */
2080 TAG_DONE);
2082 data->update = 1;
2083 MUI_Redraw(obj, MADF_DRAWUPDATE);
2085 return 0;
2088 /**************************************************************************
2089 MUIM_List_Select
2090 **************************************************************************/
2091 IPTR List__MUIM_Select(struct IClass *cl, Object *obj,
2092 struct MUIP_List_Select *msg)
2094 struct MUI_ListData *data = INST_DATA(cl, obj);
2095 LONG pos, i, count, selcount=0, state=0;
2096 BOOL multi_allowed = TRUE, new_select_state = FALSE;
2098 /* Establish the range of entries affected */
2099 switch (msg->pos)
2101 case MUIV_List_Select_Active:
2102 pos = data->entries_active;
2103 if (pos == MUIV_List_Active_Off)
2104 count = 0;
2105 else
2106 count = 1;
2107 break;
2109 case MUIV_List_Select_All:
2110 pos = 0;
2111 count = data->entries_num;
2112 break;
2114 default:
2115 pos = msg->pos;
2116 count = 1;
2117 if (pos < 0 || pos >= data->entries_num)
2118 return 0;
2119 break;
2122 if (msg->seltype != MUIV_List_Select_Ask && data->multi_test_hook != NULL)
2124 /* Disallow selection of an additional entry if there is a currently
2125 selected entry that is not multi-selectable (in such case there
2126 will only be one entry currently selected, so no need to iterate) */
2127 i = MUIV_List_NextSelected_Start;
2128 DoMethod(obj, MUIM_List_NextSelected, (IPTR) &i);
2129 if (i != MUIV_List_NextSelected_End)
2130 selcount++;
2131 if (data->multi_test_hook != NULL && selcount != 0)
2132 multi_allowed = CallHookPkt(data->multi_test_hook, NULL,
2133 data->entries[i]->data);
2136 /* Change or check state of each entry in the range */
2137 for (i = pos; i < pos + count; i++)
2139 state = data->entries[i]->flags & ENTRY_SELECTED;
2140 switch (msg->seltype)
2142 case MUIV_List_Select_Off:
2143 new_select_state = FALSE;
2144 break;
2146 case MUIV_List_Select_On:
2147 new_select_state = TRUE;
2148 break;
2150 case MUIV_List_Select_Toggle:
2151 new_select_state = !state;
2152 break;
2154 default:
2155 if (data->entries[i]->flags & ENTRY_SELECTED)
2156 selcount++;
2157 break;
2160 if (msg->seltype != MUIV_List_Select_Ask)
2162 /* Disallow selection if entry is not multi-selectable and
2163 * there are already selected entries */
2164 if (data->multi_test_hook != NULL && new_select_state)
2165 new_select_state = multi_allowed && (selcount == 0 ||
2166 CallHookPkt(data->multi_test_hook, NULL,
2167 data->entries[i]->data));
2169 if (new_select_state)
2170 data->entries[i]->flags |= ENTRY_SELECTED;
2171 else
2172 data->entries[i]->flags &= ~ENTRY_SELECTED;
2176 /* Report old state or number of selected entries */
2177 if (msg->info)
2179 if (msg->pos == MUIV_List_Select_All
2180 && msg->seltype == MUIV_List_Select_Ask)
2181 *msg->info = selcount;
2182 else
2183 *msg->info = state;
2186 /* Redraw unless it was just an enquiry */
2187 if (msg->seltype != MUIV_List_Select_Ask)
2189 if (count > 1)
2190 data->update = 1;
2191 else
2193 data->update = 2;
2194 data->update_pos = pos;
2196 MUI_Redraw(obj, MADF_DRAWUPDATE);
2199 return 0;
2202 /**************************************************************************
2203 MUIM_List_Insert
2204 **************************************************************************/
2206 IPTR List__MUIM_Insert(struct IClass *cl, Object *obj,
2207 struct MUIP_List_Insert *msg)
2209 struct MUI_ListData *data = INST_DATA(cl, obj);
2210 LONG pos, count, sort;
2212 count = msg->count;
2213 sort = 0;
2215 if (count == -1)
2217 /* Count the number of entries */
2218 for (count = 0; msg->entries[count] != NULL; count++)
2222 if (count <= 0)
2223 return ~0;
2225 switch (msg->pos)
2227 case MUIV_List_Insert_Top:
2228 pos = 0;
2229 break;
2231 case MUIV_List_Insert_Active:
2232 if (data->entries_active != -1)
2233 pos = data->entries_active;
2234 else
2235 pos = 0;
2236 break;
2238 case MUIV_List_Insert_Sorted:
2239 pos = data->entries_num;
2240 sort = 1; /* we sort'em later */
2241 break;
2243 case MUIV_List_Insert_Bottom:
2244 pos = data->entries_num;
2245 break;
2247 default:
2248 if (msg->pos > data->entries_num)
2249 pos = data->entries_num;
2250 else if (msg->pos < 0)
2251 pos = 0;
2252 else
2253 pos = msg->pos;
2254 break;
2257 if (!(SetListSize(data, data->entries_num + count)))
2258 return ~0;
2260 LONG until = pos + count;
2261 APTR *toinsert = msg->entries;
2263 if (!(PrepareInsertListEntries(data, pos, count)))
2264 return ~0;
2266 while (pos < until)
2268 struct ListEntry *lentry;
2270 if (!(lentry = AllocListEntry(data)))
2272 /* Panic, but we must be in a consistent state, so remove
2273 * the space where the following list entries should have gone
2275 RemoveListEntries(data, pos, until - pos);
2276 return ~0;
2279 /* now call the construct method which returns us a pointer which
2280 we need to store */
2281 lentry->data = (APTR) DoMethod(obj, MUIM_List_Construct,
2282 (IPTR) * toinsert, (IPTR) data->pool);
2283 if (!lentry->data)
2285 FreeListEntry(data, lentry);
2286 RemoveListEntries(data, pos, until - pos);
2288 /* TODO: Also check for visible stuff like below */
2289 if (data->entries_num != data->confirm_entries_num)
2290 set(obj, MUIA_List_Entries, data->confirm_entries_num);
2291 return ~0;
2294 data->entries[pos] = lentry;
2295 data->confirm_entries_num++;
2297 if (_flags(obj) & MADF_SETUP)
2299 /* We have to calculate the width and height of the newly
2300 * inserted entry. This has to be done after inserting the
2301 * element into the list */
2302 CalcDimsOfEntry(cl, obj, pos);
2305 toinsert++;
2306 pos++;
2309 /* Recalculate the number of visible entries */
2310 if (_flags(obj) & MADF_SETUP)
2311 CalcVertVisible(cl, obj);
2313 if (data->entries_num != data->confirm_entries_num)
2315 SetAttrs(obj,
2316 MUIA_List_Entries, data->confirm_entries_num,
2317 MUIA_List_Visible, data->entries_visible, TAG_DONE);
2320 /* If the array is already sorted, we could do a simple insert
2321 * sort and would be much faster than with qsort.
2322 * If an array is not yet sorted, does a MUIV_List_Insert_Sorted
2323 * sort the whole array?
2325 * I think, we better sort the whole array:
2327 if (sort)
2329 DoMethod(obj, MUIM_List_Sort);
2330 /* TODO: which pos to return here !? */
2331 /* MUIM_List_Sort already called MUI_Redraw */
2333 else
2335 data->update = 1;
2336 MUI_Redraw(obj, MADF_DRAWUPDATE);
2338 data->insert_position = pos;
2340 return (ULONG) pos;
2343 /**************************************************************************
2344 MUIM_List_InsertSingle
2345 **************************************************************************/
2346 IPTR List__MUIM_InsertSingle(struct IClass *cl, Object *obj,
2347 struct MUIP_List_InsertSingle *msg)
2349 return DoMethod(obj, MUIM_List_Insert, (IPTR) & msg->entry, 1,
2350 msg->pos);
2353 /**************************************************************************
2354 MUIM_List_GetEntry
2355 **************************************************************************/
2356 IPTR List__MUIM_GetEntry(struct IClass *cl, Object *obj,
2357 struct MUIP_List_GetEntry *msg)
2359 struct MUI_ListData *data = INST_DATA(cl, obj);
2360 int pos = msg->pos;
2362 if (pos == MUIV_List_GetEntry_Active)
2363 pos = data->entries_active;
2365 if (pos < 0 || pos >= data->entries_num)
2367 *msg->entry = NULL;
2368 return 0;
2370 *msg->entry = data->entries[pos]->data;
2371 return (IPTR) *msg->entry;
2374 /**************************************************************************
2375 MUIM_List_Construct
2376 **************************************************************************/
2377 IPTR List__MUIM_Construct(struct IClass *cl, Object *obj,
2378 struct MUIP_List_Construct *msg)
2380 struct MUI_ListData *data = INST_DATA(cl, obj);
2382 if (NULL == data->construct_hook)
2383 return (IPTR) msg->entry;
2384 if ((IPTR) data->construct_hook == MUIV_List_ConstructHook_String)
2386 int len = msg->entry ? strlen((STRPTR) msg->entry) : 0;
2387 ULONG *mem = AllocPooled(msg->pool, len + 5);
2389 if (NULL == mem)
2390 return 0;
2391 mem[0] = len + 5;
2392 if (msg->entry != NULL)
2393 strcpy((STRPTR) (mem + 1), (STRPTR) msg->entry);
2394 else
2395 *(STRPTR) (mem + 1) = 0;
2396 return (IPTR) (mem + 1);
2398 return CallHookPkt(data->construct_hook, msg->pool, msg->entry);
2401 /**************************************************************************
2402 MUIM_List_Destruct
2403 **************************************************************************/
2404 IPTR List__MUIM_Destruct(struct IClass *cl, Object *obj,
2405 struct MUIP_List_Destruct *msg)
2407 struct MUI_ListData *data = INST_DATA(cl, obj);
2409 if (NULL == data->destruct_hook)
2410 return 0;
2412 if ((IPTR) data->destruct_hook == MUIV_List_DestructHook_String)
2414 ULONG *mem = ((ULONG *) msg->entry) - 1;
2415 FreePooled(msg->pool, mem, mem[0]);
2417 else
2419 CallHookPkt(data->destruct_hook, msg->pool, msg->entry);
2421 return 0;
2424 /****** List.mui/MUIM_List_Compare *******************************************
2426 * NAME
2427 * MUIM_List_Compare (V20)
2429 * SYNOPSIS
2430 * DoMethod(obj, MUIM_List_Compare, APTR entry1, APTR entry2,
2431 * LONG sort_type1, LONG sort_type2);
2433 * FUNCTION
2434 * Compare two list entries according to the current comparison hook
2435 * (MUIA_List_CompareHook).
2437 * INPUTS
2438 * entry1 - the first entry data.
2439 * entry2 - the second entry data.
2440 * sort_type1 - undocumented.
2441 * sort_type2 - undocumented.
2443 * SEE ALSO
2444 * MUIA_List_CompareHook, MUIM_List_Sort.
2446 ******************************************************************************
2450 IPTR List__MUIM_Compare(struct IClass *cl, Object *obj,
2451 struct MUIP_List_Compare *msg)
2453 struct MUI_ListData *data = INST_DATA(cl, obj);
2455 return CallHookPkt(data->compare_hook, msg->entry2, msg->entry1);
2458 /**************************************************************************
2459 MUIM_List_Display
2460 **************************************************************************/
2461 IPTR List__MUIM_Display(struct IClass *cl, Object *obj,
2462 struct MUIP_List_Display *msg)
2464 struct MUI_ListData *data = INST_DATA(cl, obj);
2466 if (NULL == data->display_hook)
2468 if (msg->entry)
2469 *msg->array = msg->entry;
2470 else
2471 *msg->array = 0;
2472 return 1;
2475 *((ULONG *) (msg->array - 1)) = msg->entry_pos;
2476 return CallHookPkt(data->display_hook, msg->array, msg->entry);
2479 /**************************************************************************
2480 MUIM_List_SelectChange
2481 **************************************************************************/
2482 IPTR List__MUIM_SelectChange(struct IClass *cl, Object *obj,
2483 struct MUIP_List_SelectChange *msg)
2485 return 1;
2488 /**************************************************************************
2489 MUIM_List_CreateImage
2490 Called by a List subclass in its Setup method.
2491 Connects an Area subclass object to the list, much like an object gets
2492 connected to a window. List calls Setup and AskMinMax on that object,
2493 keeps a reference to it (that reference will be returned).
2494 Text engine will dereference that pointer and draw the object with its
2495 default size.
2496 **************************************************************************/
2497 IPTR List__MUIM_CreateImage(struct IClass *cl, Object *obj,
2498 struct MUIP_List_CreateImage *msg)
2500 struct MUI_ListData *data = INST_DATA(cl, obj);
2501 struct ListImage *li;
2503 if (!msg->obj)
2504 return 0;
2506 /* List must be already setup in Setup of your subclass */
2507 if (!(_flags(obj) & MADF_SETUP))
2508 return 0;
2509 li = AllocPooled(data->pool, sizeof(struct ListImage));
2510 if (!li)
2511 return 0;
2512 li->obj = msg->obj;
2514 AddTail((struct List *)&data->images, (struct Node *)li);
2515 DoMethod(li->obj, MUIM_ConnectParent, (IPTR) obj);
2516 DoSetupMethod(li->obj, muiRenderInfo(obj));
2519 return (IPTR) li;
2522 /**************************************************************************
2523 MUIM_List_DeleteImage
2524 **************************************************************************/
2525 IPTR List__MUIM_DeleteImage(struct IClass *cl, Object *obj,
2526 struct MUIP_List_DeleteImage *msg)
2528 struct MUI_ListData *data = INST_DATA(cl, obj);
2529 struct ListImage *li = (struct ListImage *)msg->listimg;
2531 if (li)
2533 DoMethod(li->obj, MUIM_Cleanup);
2534 DoMethod(li->obj, MUIM_DisconnectParent);
2535 Remove((struct Node *)li);
2536 FreePooled(data->pool, li, sizeof(struct ListImage));
2539 return 0;
2542 /****** List.mui/MUIM_List_Jump **********************************************
2544 * NAME
2545 * MUIM_List_Jump (V4)
2547 * SYNOPSIS
2548 * DoMethod(obj, MUIM_List_Jump, LONG pos);
2550 * FUNCTION
2551 * Scrolls the list so that a particular entry is visible.
2553 * INPUTS
2554 * pos - index of entry that should become visible, or one of these
2555 * special values:
2556 * MUIV_List_Jump_Active: show the active entry.
2557 * MUIV_List_Jump_Top: show the first entry.
2558 * MUIV_List_Jump_Bottom: show the last entry.
2559 * MUIV_List_Jump_Up: show the previous hidden entry.
2560 * MUIV_List_Jump_Down: show the next hidden entry.
2562 ******************************************************************************
2566 IPTR List__MUIM_Jump(struct IClass *cl, Object *obj,
2567 struct MUIP_List_Jump *msg)
2569 struct MUI_ListData *data = INST_DATA(cl, obj);
2570 LONG pos = msg->pos;
2572 switch (pos)
2574 case MUIV_List_Jump_Top:
2575 pos = 0;
2576 break;
2578 case MUIV_List_Jump_Active:
2579 pos = data->entries_active;
2580 break;
2582 case MUIV_List_Jump_Bottom:
2583 pos = data->entries_num - 1;
2584 break;
2586 case MUIV_List_Jump_Down:
2587 pos = data->entries_first + data->entries_visible;
2588 break;
2590 case MUIV_List_Jump_Up:
2591 pos = data->entries_first - 1;
2592 break;
2595 if (pos >= data->entries_num)
2597 pos = data->entries_num - 1;
2599 if (pos < 0)
2600 pos = 0;
2602 if (pos < data->entries_first)
2604 set(obj, MUIA_List_First, pos);
2606 else if (pos >= data->entries_first + data->entries_visible)
2608 pos -= (data->entries_visible - 1);
2609 if (pos < 0)
2610 pos = 0;
2611 if (pos != data->entries_first)
2613 set(obj, MUIA_List_First, pos);
2617 return TRUE;
2620 /****** List.mui/MUIM_List_Sort **********************************************
2622 * NAME
2623 * MUIM_List_Sort (V4)
2625 * SYNOPSIS
2626 * DoMethod(obj, MUIM_List_Sort);
2628 * FUNCTION
2629 * Sort the list's entries according to the current comparison hook
2630 * (MUIA_List_CompareHook).
2632 * SEE ALSO
2633 * MUIA_List_CompareHook, MUIM_List_Compare.
2635 ******************************************************************************
2639 IPTR List__MUIM_Sort(struct IClass *cl, Object *obj,
2640 struct MUIP_List_Sort *msg)
2642 struct MUI_ListData *data = INST_DATA(cl, obj);
2644 int i, j, max;
2645 struct MUIP_List_Compare cmpmsg =
2646 { MUIM_List_Compare, NULL, NULL, 0, 0 };
2648 if (data->entries_num > 1)
2651 Simple sort algorithm. Feel free to improve it.
2653 for (i = 0; i < data->entries_num - 1; i++)
2655 max = i;
2656 for (j = i + 1; j < data->entries_num; j++)
2658 cmpmsg.entry1 = data->entries[max]->data;
2659 cmpmsg.entry2 = data->entries[j]->data;
2660 if ((LONG) DoMethodA(obj, (Msg) & cmpmsg) > 0)
2662 max = j;
2665 if (i != max)
2667 APTR tmp = data->entries[i];
2668 data->entries[i] = data->entries[max];
2669 data->entries[max] = tmp;
2674 data->update = 1;
2675 MUI_Redraw(obj, MADF_DRAWUPDATE);
2677 return 0;
2680 /****** List.mui/MUIM_List_Move **********************************************
2682 * NAME
2683 * MUIM_List_Move (V9)
2685 * SYNOPSIS
2686 * DoMethod(obj, MUIM_List_Move, LONG from, LONG to);
2688 * FUNCTION
2689 * Move a list entry to a new position.
2691 * INPUTS
2692 * from - the current index of the entry that should be moved, or one of
2693 * these special values:
2694 * MUIV_List_Move_Active: the active entry.
2695 * MUIV_List_Move_Top: the first entry.
2696 * MUIV_List_Move_Bottom: the last entry.
2697 * to - the index of the entry's new position, or one of
2698 * these special values:
2699 * MUIV_List_Move_Active: the active entry.
2700 * MUIV_List_Move_Top: the first entry.
2701 * MUIV_List_Move_Bottom: the last entry.
2703 ******************************************************************************
2707 IPTR List__MUIM_Move(struct IClass *cl, Object *obj,
2708 struct MUIP_List_Move *msg)
2710 struct MUI_ListData *data = INST_DATA(cl, obj);
2712 LONG from, to;
2713 int i;
2715 /* Normalise special 'from' values */
2716 switch (msg->from)
2718 case MUIV_List_Move_Top:
2719 from = 0;
2720 break;
2721 case MUIV_List_Move_Active:
2722 from = data->entries_active;
2723 break;
2724 case MUIV_List_Move_Bottom:
2725 from = data->entries_num - 1;
2726 break;
2727 default:
2728 from = msg->from;
2731 /* Normalise special 'to' values */
2732 switch (msg->to)
2734 case MUIV_List_Move_Top:
2735 to = 0;
2736 break;
2737 case MUIV_List_Move_Active:
2738 to = data->entries_active;
2739 break;
2740 case MUIV_List_Move_Bottom:
2741 to = data->entries_num - 1;
2742 break;
2743 case MUIV_List_Move_Next:
2744 to = from + 1;
2745 break;
2746 case MUIV_List_Move_Previous:
2747 to = from - 1;
2748 break;
2749 default:
2750 to = msg->to;
2753 /* Check that values are within valid bounds */
2754 if (from > data->entries_num - 1 || from < 0
2755 || to > data->entries_num - 1 || to < 0 || from == to)
2756 return (IPTR) FALSE;
2758 /* Shift all entries in the range between the 'from' and 'to' positions */
2759 if (from < to)
2761 struct ListEntry *backup = data->entries[from];
2762 for (i = from; i < to; i++)
2763 data->entries[i] = data->entries[i + 1];
2764 data->entries[to] = backup;
2766 else
2768 struct ListEntry *backup = data->entries[from];
2769 for (i = from; i > to; i--)
2770 data->entries[i] = data->entries[i - 1];
2771 data->entries[to] = backup;
2774 /* Update index of active entry */
2775 if (from == data->entries_active)
2776 data->entries_active = to;
2777 else if (data->entries_active > from && data->entries_active < to)
2778 data->entries_active--;
2779 else if (data->entries_active < from && data->entries_active >= to)
2780 data->entries_active++;
2782 /* Reflect list changes visually */
2783 data->update = 1;
2784 MUI_Redraw(obj, MADF_DRAWUPDATE);
2786 return TRUE;
2789 /**************************************************************************
2790 MUIM_List_NextSelected
2791 **************************************************************************/
2792 IPTR List__MUIM_NextSelected(struct IClass *cl, Object *obj,
2793 struct MUIP_List_NextSelected *msg)
2795 struct MUI_ListData *data = INST_DATA(cl, obj);
2796 LONG pos, i;
2797 BOOL found = FALSE;
2799 /* Get the first entry to check */
2800 pos = *msg->pos;
2801 if (pos == MUIV_List_NextSelected_Start)
2802 pos = 0;
2803 else
2804 pos++;
2806 /* Find the next selected entry */
2807 for (i = pos; i < data->entries_num && !found; i++)
2809 if (data->entries[i]->flags & ENTRY_SELECTED)
2811 pos = i;
2812 found = TRUE;
2816 /* Return index of selected entry, or indicate there are no more */
2817 if (!found)
2818 pos = MUIV_List_NextSelected_End;
2819 *msg->pos = pos;
2821 return TRUE;
2824 /**************************************************************************
2825 MUIM_List_TestPos
2826 **************************************************************************/
2827 IPTR List__MUIM_TestPos(struct IClass *cl, Object *obj,
2828 struct MUIP_List_TestPos *msg)
2830 struct MUI_ListData *data = INST_DATA(cl, obj);
2831 struct MUI_List_TestPos_Result *result = msg->res;
2832 LONG col = -1, row = -1;
2833 UWORD flags = 0, i;
2834 LONG mx = msg->x - _left(data->area);
2835 LONG entries_visible;
2837 if (data->entries_visible <= data->entries_num)
2838 entries_visible = data->entries_visible;
2839 else
2840 entries_visible = data->entries_num;
2841 LONG ey = msg->y - data->entries_top_pixel;
2842 /* y coordinates transformed to the entries */
2844 /* Now check if it was clicked on a title or on entries */
2845 if (ey < 0)
2846 flags |= MUI_LPR_ABOVE;
2847 else if (ey >= entries_visible * data->entry_maxheight)
2848 flags |= MUI_LPR_BELOW;
2849 else
2851 /* Identify row */
2852 row = ey / data->entry_maxheight + data->entries_first;
2853 result->yoffset =
2854 ey % data->entry_maxheight - data->entry_maxheight / 2;
2857 if (mx < 0)
2858 flags |= MUI_LPR_LEFT;
2859 else if (mx >= _width(data->area))
2860 flags |= MUI_LPR_RIGHT;
2861 else
2863 /* Identify column */
2864 if (data->entries_num > 0 && data->columns > 0)
2866 LONG width_sum = 0;
2867 col = data->columns - 1;
2868 for (i = 0; i < data->columns; i++)
2870 result->xoffset = mx - width_sum;
2871 width_sum +=
2872 data->ci[i].entries_width +
2873 data->ci[i].delta +
2874 (data->ci[i].bar ? BAR_WIDTH : 0);
2875 D(bug("[List/MUIM_TestPos] i %d "
2876 "width %d width_sum %d mx %d\n",
2877 i, data->ci[i].entries_width, width_sum, mx));
2878 if (mx < width_sum)
2880 col = i;
2881 D(bug("[List/MUIM_TestPos] Column hit %d\n", col));
2882 break;
2888 result->entry = row;
2889 result->column = col;
2890 result->flags = flags;
2892 return TRUE;
2895 /****i* List.mui/MUIM_DragQuery **********************************************
2897 * NAME
2898 * MUIM_DragQuery
2900 ******************************************************************************
2904 IPTR List__MUIM_DragQuery(struct IClass *cl, Object *obj,
2905 struct MUIP_DragQuery *msg)
2907 if (msg->obj == obj)
2908 return MUIV_DragQuery_Accept;
2909 else
2910 return MUIV_DragQuery_Refuse;
2914 /****i* List.mui/MUIM_DragFinish *********************************************
2916 * NAME
2917 * MUIM_DragFinish
2919 ******************************************************************************
2923 IPTR List__MUIM_DragFinish(struct IClass *cl, Object *obj,
2924 struct MUIP_DragFinish *msg)
2926 struct MUI_ListData *data = INST_DATA(cl, obj);
2928 data->drop_mark_y = -1;
2930 return DoSuperMethodA(cl, obj, (Msg) msg);
2933 /****i* List.mui/MUIM_DragReport *********************************************
2935 * NAME
2936 * MUIM_DragReport
2938 ******************************************************************************
2942 IPTR List__MUIM_DragReport(struct IClass *cl, Object *obj,
2943 struct MUIP_DragReport *msg)
2945 struct MUI_ListData *data = INST_DATA(cl, obj);
2946 struct MUI_List_TestPos_Result pos;
2947 struct RastPort *rp = _rp(obj);
2948 LONG n, y;
2949 UWORD old_pattern;
2951 /* Choose new drop mark position */
2953 DoMethod(obj, MUIM_List_TestPos, msg->x, msg->y, (IPTR) &pos);
2954 if (pos.entry != -1)
2956 n = pos.entry;
2957 if (pos.yoffset > 0)
2958 n++;
2960 else if ((pos.flags & MUI_LPR_ABOVE) != 0)
2961 n = data->entries_first;
2962 else
2964 n = MIN(data->entries_visible, data->entries_num)
2965 - data->entries_first;
2968 /* Clear old drop mark */
2970 if ((data->flags & LIST_SHOWDROPMARKS) != 0)
2972 y = data->entries_top_pixel + (n - data->entries_first)
2973 * data->entry_maxheight;
2974 if (y != data->drop_mark_y)
2976 DoMethod(obj, MUIM_DrawBackground, _mleft(data->area), data->drop_mark_y,
2977 _mwidth(data->area), 1, 0, 0, 0);
2979 /* Draw new drop mark and store its position */
2981 SetABPenDrMd(rp, _pens(obj)[MPEN_SHINE], _pens(obj)[MPEN_SHADOW],
2982 JAM2);
2983 old_pattern = rp->LinePtrn;
2984 SetDrPt(rp, 0xF0F0);
2985 Move(rp, _mleft(data->area), y);
2986 Draw(rp, _mright(data->area), y);
2987 SetDrPt(rp, old_pattern);
2988 data->drop_mark_y = y;
2992 return TRUE;
2996 /****i* List.mui/MUIM_DragDrop ***********************************************
2998 * NAME
2999 * MUIM_DragDrop
3001 ******************************************************************************
3005 IPTR List__MUIM_DragDrop(struct IClass *cl, Object *obj,
3006 struct MUIP_DragDrop *msg)
3008 struct MUI_ListData *data = INST_DATA(cl, obj);
3009 struct MUI_List_TestPos_Result pos;
3010 LONG n;
3012 /* Find drop position */
3014 DoMethod(obj, MUIM_List_TestPos, msg->x, msg->y, (IPTR) &pos);
3015 if (pos.entry != -1)
3017 /* Change drop position when coords move past centre of entry, not
3018 * entry boundary */
3020 n = pos.entry;
3021 if (pos.yoffset > 0)
3022 n++;
3024 /* Ensure that dropped entry will be positioned between the two
3025 * entries that are above and below the drop mark, rather than
3026 * strictly at the numeric index shown */
3028 if (n > data->entries_active)
3029 n--;
3031 else if ((pos.flags & MUI_LPR_ABOVE) != 0)
3032 n = MUIV_List_Move_Top;
3033 else
3034 n = MUIV_List_Move_Bottom;
3036 DoMethod(msg->obj, MUIM_List_Move, MUIV_List_Move_Active, n);
3038 return TRUE;
3042 /****i* List.mui/MUIM_CreateDragImage ****************************************
3044 * NAME
3045 * MUIM_CreateDragImage
3047 ******************************************************************************
3051 static IPTR List__MUIM_CreateDragImage(struct IClass *cl, Object *obj,
3052 struct MUIP_CreateDragImage *msg)
3054 struct MUI_ListData *data = INST_DATA(cl, obj);
3055 BOOL success = TRUE;
3056 struct MUI_List_TestPos_Result pos;
3057 WORD width, height, left, top;
3058 struct MUI_DragImage *img = NULL;
3059 const struct ZuneFrameGfx *zframe;
3060 LONG depth;
3062 /* Get info on dragged entry */
3063 DoMethod(obj, MUIM_List_TestPos, _left(data->area) - msg->touchx,
3064 _top(data->area) - msg->touchy, (IPTR) &pos);
3065 if (pos.entry == -1)
3066 success = FALSE;
3068 if (success)
3070 /* Get boundaries of entry */
3071 width = _mwidth(data->area);
3072 height = data->entry_maxheight;
3073 left = _mleft(data->area);
3074 top = _top(data->area) - msg->touchy
3075 - (pos.yoffset + data->entry_maxheight / 2);
3077 /* Allocate drag image structure */
3078 img = (struct MUI_DragImage *)
3079 AllocVec(sizeof(struct MUI_DragImage), MEMF_CLEAR);
3080 if (img == NULL)
3081 success = FALSE;
3084 if (success)
3086 /* Get drag frame */
3087 zframe = zune_zframe_get(obj,
3088 &muiGlobalInfo(obj)->mgi_Prefs->frames[MUIV_Frame_Drag]);
3090 /* Allocate drag image buffer */
3091 img->width = width + zframe->ileft + zframe->iright;
3092 img->height = height + zframe->itop + zframe->ibottom;
3093 depth = GetBitMapAttr(_screen(obj)->RastPort.BitMap, BMA_DEPTH);
3094 img->bm = AllocBitMap(img->width, img->height, depth, BMF_MINPLANES,
3095 _screen(obj)->RastPort.BitMap);
3097 if (img->bm != NULL)
3099 /* Render entry */
3100 struct RastPort temprp;
3101 InitRastPort(&temprp);
3102 temprp.BitMap = img->bm;
3103 ClipBlit(_rp(obj), left, top, &temprp,
3104 zframe->ileft, zframe->itop, width, height,
3105 0xc0);
3107 /* Render frame */
3108 struct RastPort *rp_save = muiRenderInfo(obj)->mri_RastPort;
3109 muiRenderInfo(obj)->mri_RastPort = &temprp;
3110 zframe->draw(zframe->customframe, muiRenderInfo(obj), 0, 0,
3111 img->width, img->height, 0, 0, img->width, img->height);
3112 muiRenderInfo(obj)->mri_RastPort = rp_save;
3115 /* Ensure drag point matches where user clicked */
3116 img->touchx = msg->touchx - zframe->ileft + _addleft(obj);
3117 img->touchy = -(pos.yoffset + data->entry_maxheight / 2)
3118 - zframe->itop;
3119 img->flags = 0;
3122 return (IPTR) img;
3125 static void DoWheelMove(struct IClass *cl, Object *obj, LONG wheely)
3127 LONG new, first, entries, visible;
3129 new = first = XGET(obj, MUIA_List_First);
3130 entries = XGET(obj, MUIA_List_Entries);
3131 visible = XGET(obj, MUIA_List_Visible);
3133 new += wheely;
3135 if (new > entries - visible)
3137 new = entries - visible;
3140 if (new < 0)
3142 new = 0;
3145 if (new != first)
3147 set(obj, MUIA_List_First, new);
3151 /**************************************************************************
3152 MUIM_HandleEvent
3153 **************************************************************************/
3154 IPTR List__MUIM_HandleEvent(struct IClass *cl, Object *obj, struct MUIP_HandleEvent *msg)
3156 struct MUI_ListData *data = INST_DATA(cl, obj);
3157 struct MUI_List_TestPos_Result pos;
3158 LONG seltype, old_active, new_active, visible, first, last, i;
3159 IPTR result = 0;
3160 BOOL select = FALSE, clear = FALSE, range_select = FALSE, changing;
3161 WORD delta;
3162 typeof(msg->muikey) muikey = msg->muikey;
3164 new_active = old_active = XGET(obj, MUIA_List_Active);
3165 visible = XGET(obj, MUIA_List_Visible);
3167 if (muikey != MUIKEY_NONE)
3169 result = MUI_EventHandlerRC_Eat;
3171 /* Make keys behave differently in read-only mode */
3172 if (data->read_only)
3174 switch (muikey)
3176 case MUIKEY_TOP:
3177 muikey = MUIKEY_LINESTART;
3178 break;
3180 case MUIKEY_BOTTOM:
3181 muikey = MUIKEY_LINEEND;
3182 break;
3184 case MUIKEY_UP:
3185 muikey = MUIKEY_LEFT;
3186 break;
3188 case MUIKEY_DOWN:
3189 case MUIKEY_PRESS:
3190 muikey = MUIKEY_RIGHT;
3191 break;
3195 switch (muikey)
3197 case MUIKEY_TOGGLE:
3198 if (data->multiselect != MUIV_Listview_MultiSelect_None
3199 && !data->read_only)
3201 select = TRUE;
3202 data->click_column = data->def_click_column;
3203 new_active = MUIV_List_Active_Down;
3205 else
3207 DoMethod(obj, MUIM_List_Jump, 0);
3208 muikey = MUIKEY_NONE;
3210 break;
3212 case MUIKEY_TOP:
3213 new_active = MUIV_List_Active_Top;
3214 break;
3216 case MUIKEY_BOTTOM:
3217 new_active = MUIV_List_Active_Bottom;
3218 break;
3220 case MUIKEY_LEFT:
3221 case MUIKEY_WORDLEFT:
3222 DoMethod(obj, MUIM_List_Jump, MUIV_List_Jump_Up);
3223 break;
3225 case MUIKEY_RIGHT:
3226 case MUIKEY_WORDRIGHT:
3227 DoMethod(obj, MUIM_List_Jump, MUIV_List_Jump_Down);
3228 break;
3230 case MUIKEY_LINESTART:
3231 DoMethod(obj, MUIM_List_Jump, MUIV_List_Jump_Top);
3232 break;
3234 case MUIKEY_LINEEND:
3235 DoMethod(obj, MUIM_List_Jump, MUIV_List_Jump_Bottom);
3236 break;
3238 case MUIKEY_UP:
3239 new_active = MUIV_List_Active_Up;
3240 break;
3242 case MUIKEY_DOWN:
3243 new_active = MUIV_List_Active_Down;
3244 break;
3246 case MUIKEY_PAGEUP:
3247 if (data->read_only)
3248 DoWheelMove(cl, obj, -visible);
3249 else
3250 new_active = MUIV_List_Active_PageUp;
3251 break;
3253 case MUIKEY_PAGEDOWN:
3254 if (data->read_only)
3255 DoWheelMove(cl, obj, visible);
3256 else
3257 new_active = MUIV_List_Active_PageDown;
3258 break;
3260 default:
3261 result = 0;
3264 else if (msg->imsg)
3266 DoMethod(obj, MUIM_List_TestPos, msg->imsg->MouseX, msg->imsg->MouseY, (IPTR) &pos);
3268 switch (msg->imsg->Class)
3270 case IDCMP_MOUSEBUTTONS:
3271 if (msg->imsg->Code == SELECTDOWN)
3273 if (_isinobject(data->area, msg->imsg->MouseX, msg->imsg->MouseY))
3275 data->mouse_click = MOUSE_CLICK_ENTRY;
3277 if (!data->read_only && pos.entry != -1)
3279 new_active = pos.entry;
3281 clear = (data->multiselect == MUIV_Listview_MultiSelect_Shifted
3282 && (msg->imsg->Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)) == 0);
3283 seltype = clear ? MUIV_List_Select_On: MUIV_List_Select_Toggle;
3284 select = data->multiselect != MUIV_Listview_MultiSelect_None;
3286 /* Handle MUIA_Listview_ClickColumn */
3287 data->click_column = pos.column;
3288 superset(cl, obj, MUIA_Listview_ClickColumn,
3289 data->click_column);
3291 /* Handle double clicking */
3292 if (data->last_active == pos.entry
3293 && DoubleClick(data->last_secs, data->last_mics, msg->imsg->Seconds, msg->imsg->Micros))
3295 set(obj, MUIA_Listview_DoubleClick, TRUE);
3296 data->last_active = -1;
3297 data->last_secs = data->last_mics = 0;
3299 else
3301 data->last_active = pos.entry;
3302 data->last_secs = msg->imsg->Seconds;
3303 data->last_mics = msg->imsg->Micros;
3306 /* Look out for mouse movement, timer and
3307 inactive-window events while mouse button is
3308 down */
3309 DoMethod(_win(obj), MUIM_Window_RemEventHandler, (IPTR) &data->ehn);
3310 data->ehn.ehn_Events |= (IDCMP_MOUSEMOVE | IDCMP_INTUITICKS |IDCMP_INACTIVEWINDOW);
3311 DoMethod(_win(obj), MUIM_Window_AddEventHandler, (IPTR) &data->ehn);
3315 else
3317 /* Activate object */
3318 if (msg->imsg->Code == SELECTUP && data->mouse_click)
3320 set(_win(obj), MUIA_Window_ActiveObject, (IPTR)obj);
3321 data->mouse_click = 0;
3324 /* Restore normal event mask */
3325 DoMethod(_win(obj), MUIM_Window_RemEventHandler, (IPTR) &data->ehn);
3326 data->ehn.ehn_Events &= ~(IDCMP_MOUSEMOVE | IDCMP_INTUITICKS | IDCMP_INACTIVEWINDOW);
3327 DoMethod(_win(obj), MUIM_Window_AddEventHandler, (IPTR) &data->ehn);
3329 break;
3331 case IDCMP_MOUSEMOVE:
3332 case IDCMP_INTUITICKS:
3333 if (pos.flags & MUI_LPR_ABOVE)
3334 new_active = MUIV_List_Active_Up;
3335 else if (pos.flags & MUI_LPR_BELOW)
3336 new_active = MUIV_List_Active_Down;
3337 else
3338 new_active = pos.entry;
3340 select = new_active != old_active && data->multiselect != MUIV_Listview_MultiSelect_None;
3341 if (select)
3343 DoMethod(obj, MUIM_List_Select, MUIV_List_Select_Active, MUIV_List_Select_Ask, &seltype);
3344 range_select = new_active >= 0;
3347 break;
3349 case IDCMP_INACTIVEWINDOW:
3350 /* Stop listening for events we only listen to when mouse button is
3351 down: we will not be informed of the button being released */
3352 DoMethod(_win(obj), MUIM_Window_RemEventHandler, (IPTR) &data->ehn);
3353 data->ehn.ehn_Events &= ~(IDCMP_MOUSEMOVE | IDCMP_INTUITICKS | IDCMP_INACTIVEWINDOW);
3354 DoMethod(_win(obj), MUIM_Window_AddEventHandler, (IPTR) &data->ehn);
3355 break;
3357 case IDCMP_RAWKEY:
3358 /* Scroll wheel */
3359 if (data->vert && _isinobject(data->vert, msg->imsg->MouseX, msg->imsg->MouseY))
3360 delta = 1;
3361 else if (_isinobject(data->area, msg->imsg->MouseX, msg->imsg->MouseY))
3362 delta = 4;
3363 else
3364 delta = 0;
3366 if (delta != 0)
3368 switch (msg->imsg->Code)
3370 case RAWKEY_NM_WHEEL_UP:
3371 DoWheelMove(cl, obj, -delta);
3372 break;
3374 case RAWKEY_NM_WHEEL_DOWN:
3375 DoWheelMove(cl, obj, delta);
3376 break;
3378 result = MUI_EventHandlerRC_Eat;
3380 break;
3384 /* Decide in advance if any selections may change */
3385 changing = clear || muikey == MUIKEY_TOGGLE || select;
3387 /* Change selected and active entries */
3388 if (changing)
3389 set(obj, MUIA_Listview_SelectChange, TRUE);
3391 if (clear)
3393 DoMethod(obj, MUIM_List_Select, MUIV_List_Select_All, MUIV_List_Select_Off, NULL);
3396 if (muikey == MUIKEY_TOGGLE)
3398 DoMethod(obj, MUIM_List_Select, MUIV_List_Select_Active, MUIV_List_Select_Toggle, NULL);
3399 select = FALSE;
3402 if (new_active != old_active)
3403 set(obj, MUIA_List_Active, new_active);
3405 if (select)
3407 if (range_select)
3409 if (old_active < new_active)
3410 first = old_active + 1, last = new_active;
3411 else
3412 first = new_active, last = old_active - 1;
3413 for (i = first; i <= last; i++)
3414 DoMethod(obj, MUIM_List_Select, i, seltype, NULL);
3416 else
3417 DoMethod(obj, MUIM_List_Select, MUIV_List_Select_Active, seltype, NULL);
3420 if (changing)
3421 set(obj, MUIA_Listview_SelectChange, FALSE);
3423 return result;
3426 /**************************************************************************
3427 Dispatcher
3428 **************************************************************************/
3429 BOOPSI_DISPATCHER(IPTR, List_Dispatcher, cl, obj, msg)
3431 switch (msg->MethodID)
3433 case OM_NEW:
3434 return List__OM_NEW(cl, obj, (struct opSet *)msg);
3435 case OM_DISPOSE:
3436 return List__OM_DISPOSE(cl, obj, msg);
3437 case OM_SET:
3438 return List__OM_SET(cl, obj, (struct opSet *)msg);
3439 case OM_GET:
3440 return List__OM_GET(cl, obj, (struct opGet *)msg);
3442 case MUIM_Setup:
3443 return List__MUIM_Setup(cl, obj, (struct MUIP_Setup *)msg);
3444 case MUIM_Cleanup:
3445 return List__MUIM_Cleanup(cl, obj, (struct MUIP_Cleanup *)msg);
3446 case MUIM_HandleEvent:
3447 return List__MUIM_HandleEvent(cl, obj, (struct MUIP_HandleEvent *)msg);
3448 case MUIM_AskMinMax:
3449 return List__MUIM_AskMinMax(cl, obj, (struct MUIP_AskMinMax *)msg);
3450 case MUIM_Show:
3451 return List__MUIM_Show(cl, obj, (struct MUIP_Show *)msg);
3452 case MUIM_Hide:
3453 return List__MUIM_Hide(cl, obj, (struct MUIP_Hide *)msg);
3454 case MUIM_Draw:
3455 return List__MUIM_Draw(cl, obj, (struct MUIP_Draw *)msg);
3456 case MUIM_Layout:
3457 return List__MUIM_Layout(cl, obj, (struct MUIP_Layout *)msg);
3458 case MUIM_List_Clear:
3459 return List__MUIM_Clear(cl, obj, (struct MUIP_List_Clear *)msg);
3460 case MUIM_List_Sort:
3461 return List__MUIM_Sort(cl, obj, (struct MUIP_List_Sort *)msg);
3462 case MUIM_List_Exchange:
3463 return List__MUIM_Exchange(cl, obj,
3464 (struct MUIP_List_Exchange *)msg);
3465 case MUIM_List_Insert:
3466 return List__MUIM_Insert(cl, obj, (APTR) msg);
3467 case MUIM_List_InsertSingle:
3468 return List__MUIM_InsertSingle(cl, obj, (APTR) msg);
3469 case MUIM_List_GetEntry:
3470 return List__MUIM_GetEntry(cl, obj, (APTR) msg);
3471 case MUIM_List_Redraw:
3472 return List__MUIM_Redraw(cl, obj, (APTR) msg);
3473 case MUIM_List_Remove:
3474 return List__MUIM_Remove(cl, obj, (APTR) msg);
3475 case MUIM_List_Select:
3476 return List__MUIM_Select(cl, obj, (APTR) msg);
3477 case MUIM_List_Construct:
3478 return List__MUIM_Construct(cl, obj, (APTR) msg);
3479 case MUIM_List_Destruct:
3480 return List__MUIM_Destruct(cl, obj, (APTR) msg);
3481 case MUIM_List_Compare:
3482 return List__MUIM_Compare(cl, obj, (APTR) msg);
3483 case MUIM_List_Display:
3484 return List__MUIM_Display(cl, obj, (APTR) msg);
3485 case MUIM_List_SelectChange:
3486 return List__MUIM_SelectChange(cl, obj, (APTR) msg);
3487 case MUIM_List_CreateImage:
3488 return List__MUIM_CreateImage(cl, obj, (APTR) msg);
3489 case MUIM_List_DeleteImage:
3490 return List__MUIM_DeleteImage(cl, obj, (APTR) msg);
3491 case MUIM_List_Jump:
3492 return List__MUIM_Jump(cl, obj, (APTR) msg);
3493 case MUIM_List_Move:
3494 return List__MUIM_Move(cl, obj, (struct MUIP_List_Move *)msg);
3495 case MUIM_List_NextSelected:
3496 return List__MUIM_NextSelected(cl, obj,
3497 (struct MUIP_List_NextSelected *)msg);
3498 case MUIM_List_TestPos:
3499 return List__MUIM_TestPos(cl, obj, (APTR) msg);
3500 case MUIM_DragQuery:
3501 return List__MUIM_DragQuery(cl, obj, (APTR) msg);
3502 case MUIM_DragFinish:
3503 return List__MUIM_DragFinish(cl, obj, (APTR) msg);
3504 case MUIM_DragReport:
3505 return List__MUIM_DragReport(cl, obj, (APTR) msg);
3506 case MUIM_DragDrop:
3507 return List__MUIM_DragDrop(cl, obj, (APTR) msg);
3508 case MUIM_CreateDragImage:
3509 return List__MUIM_CreateDragImage(cl, obj, (APTR) msg);
3512 return DoSuperMethodA(cl, obj, msg);
3514 BOOPSI_DISPATCHER_END
3517 * Class descriptor.
3519 const struct __MUIBuiltinClass _MUI_List_desc =
3521 MUIC_List,
3522 MUIC_Group,
3523 sizeof(struct MUI_ListData),
3524 (void *) List_Dispatcher