Fixed binary search: no more infinite loops when vendor is unknown.
[tangerine.git] / workbench / libs / muimaster / classes / list.c
blob959f02908158436c333fa7e8bd23208539c1c9d0
1 /*
2 Copyright © 2002-2009, 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/view.h>
12 #include <devices/rawkeycodes.h>
13 #include <clib/alib_protos.h>
14 #include <proto/exec.h>
15 #include <proto/graphics.h>
16 #include <proto/utility.h>
17 #include <proto/dos.h>
18 #include <proto/intuition.h>
19 #include <proto/muimaster.h>
21 /* #define MYDEBUG 1 */
22 #include "debug.h"
23 #include "mui.h"
24 #include "muimaster_intern.h"
25 #include "support.h"
26 #include "imspec.h"
27 #include "textengine.h"
28 #include "listimage.h"
29 #include "prefs.h"
31 extern struct Library *MUIMasterBase;
33 #define ENTRY_TITLE (-1)
35 #define FORMAT_TEMPLATE "DELTA=D/N,PREPARSE=P/K,WEIGHT=W/N,MINWIDTH=MIW/N,MAXWIDTH=MAW/N,COL=C/N,BAR/S"
37 #define BAR_WIDTH 2
39 enum {
40 ARG_DELTA,
41 ARG_PREPARSE,
42 ARG_WEIGHT,
43 ARG_MINWIDTH,
44 ARG_MAXWIDTH,
45 ARG_COL,
46 ARG_BAR,
47 ARG_CNT
51 struct ListEntry
53 APTR data;
54 LONG *widths; /* Widths of the columns */
55 LONG width; /* Line width */
56 LONG height; /* Line height */
57 WORD flags; /* see below */
61 struct ColumnInfo
63 int colno; /* Column number */
64 int user_width; /* user setted width -1 if entry width */
65 int min_width; /* min width percentage */
66 int max_width; /* min width percentage */
67 int weight;
68 int delta; /* ignored for the first and last column, defaults to 4 */
69 int bar;
70 STRPTR preparse;
72 int entries_width; /* width of the entries (the maximum of the widths of all entries) */
75 struct MUI_ImageSpec_intern;
77 struct MUI_ListData
79 /* bool attrs */
80 ULONG flags;
82 APTR intern_pool; /* The internal pool which the class has allocated */
83 LONG intern_puddle_size;
84 LONG intern_tresh_size;
85 APTR pool; /* the pool which is used to allocate list entries */
87 struct Hook *construct_hook;
88 struct Hook *compare_hook;
89 struct Hook *destruct_hook;
90 struct Hook *display_hook;
92 struct Hook default_compare_hook;
94 /* List managment, currently we use a simple flat array, which is not good if many entries are inserted/deleted */
95 LONG entries_num; /* Number of Entries in the list */
96 LONG entries_allocated;
97 struct ListEntry **entries;
99 LONG entries_first; /* first visible entry */
100 LONG entries_visible; /* number of visible entries, determined at MUIM_Layout */
101 LONG entries_active;
102 LONG insert_position; /* pos of the last insertion */
104 LONG entry_maxheight; /* Maximum height of an entry */
105 ULONG entry_minheight; /* from MUIA_List_MinLineHeight */
107 LONG entries_totalheight;
108 LONG entries_maxwidth;
110 LONG vertprop_entries;
111 LONG vertprop_visible;
112 LONG vertprop_first;
114 LONG confirm_entries_num; /* These are the correct entries num, used so you cannot set MUIA_List_Entries to wrong values */
116 LONG entries_top_pixel; /* Where the entries start */
118 /* Column managment, is allocated by ParseListFormat() and freed by CleanListFormat() */
119 LONG columns; /* Number of columns the list has */
120 struct ColumnInfo *ci;
121 STRPTR *preparses;
122 STRPTR *strings; /* the strings for the display function, one more as needed (for the entry position) */
124 /* Titlestuff */
125 int title_height; /* The complete height of the title */
126 STRPTR title; /* On single comlums this is the title, otherwise 1 */
128 struct MUI_EventHandlerNode ehn;
129 int mouse_click; /* see below if mouse is hold down */
131 /* Cursor images */
132 struct MUI_ImageSpec_intern *list_cursor;
133 struct MUI_ImageSpec_intern *list_select;
134 struct MUI_ImageSpec_intern *list_selcur;
136 /* Render optimization */
137 int update; /* 1 - update everything, 2 - redraw entry at update_pos, 3 - scroll to current entries_first (old value is is update_pos) */
138 int update_pos;
140 /* double click */
141 ULONG last_secs;
142 ULONG last_mics;
143 ULONG last_active;
144 ULONG doubleclick;
146 /* list type */
147 ULONG input; /* FALSE - readonly, otherwise TRUE */
149 /* list images */
150 struct MinList images;
152 /* user prefs */
153 ListviewMulti prefs_multi;
154 ListviewRefresh prefs_refresh;
155 UWORD prefs_linespacing;
156 BOOL prefs_smoothed;
157 UWORD prefs_smoothval;
160 #define LIST_ADJUSTWIDTH (1<<0)
161 #define LIST_ADJUSTHEIGHT (1<<1)
162 #define LIST_AUTOVISIBLE (1<<2)
163 #define LIST_DRAGSORTABLE (1<<3)
164 #define LIST_SHOWDROPMARKS (1<<4)
165 #define LIST_QUIET (1<<5)
168 #define MOUSE_CLICK_ENTRY 1 /* on entry clicked */
169 #define MOUSE_CLICK_TITLE 2 /* on title clicked */
171 /**************************************************************************
172 Allocate a single list entry, does not initialize it (except the pointer)
173 **************************************************************************/
174 static struct ListEntry *AllocListEntry(struct MUI_ListData *data)
176 ULONG *mem;
177 struct ListEntry *le;
178 int size = sizeof(struct ListEntry) + sizeof(LONG)*data->columns + 4; /* sizeinfo */
180 mem = AllocPooled(data->pool, size);
181 if (!mem) return NULL;
182 D(bug("List AllocListEntry %p, %ld bytes\n", mem, size));
184 mem[0] = size; /* Save the size */
185 le = (struct ListEntry*)(mem+1);
186 le->widths = (LONG*)(le + 1);
187 return le;
190 /**************************************************************************
191 Deallocate a single list entry, does not deinitialize it
192 **************************************************************************/
193 static void FreeListEntry(struct MUI_ListData *data, struct ListEntry *entry)
195 ULONG *mem = ((ULONG*)entry)-1;
196 D(bug("FreeListEntry %p size=%ld\n", mem, mem[0]));
197 FreePooled(data->pool, mem, mem[0]);
200 /**************************************************************************
201 Ensures that we there can be at least the given amount of entries within
202 the list. Returns 0 if not. It also allocates the space for the title.
203 It can be accesses with data->entries[ENTRY_TITLE]
204 **************************************************************************/
205 static int SetListSize(struct MUI_ListData *data, LONG size)
207 struct ListEntry **new_entries;
208 int new_entries_allocated;
210 if (size + 1 <= data->entries_allocated)
211 return 1;
213 new_entries_allocated = data->entries_allocated * 2 + 4;
214 if (new_entries_allocated < size + 1)
215 new_entries_allocated = size + 1 + 10; /* 10 is just random */
217 D(bug("List %p : SetListSize allocating %ld bytes\n", data,
218 new_entries_allocated * sizeof(struct ListEntry *)));
219 new_entries = AllocVec(new_entries_allocated * sizeof(struct ListEntry *),0);
220 if (NULL == new_entries)
221 return 0;
222 if (data->entries)
224 CopyMem(data->entries - 1, new_entries,
225 (data->entries_num + 1) * sizeof(struct ListEntry*));
226 FreeVec(data->entries - 1);
228 data->entries = new_entries + 1;
229 data->entries_allocated = new_entries_allocated;
230 return 1;
233 /**************************************************************************
234 Prepares the insertion of count entries at pos.
235 This function doesn't care if there is enough space in the datastructure.
236 SetListSize() must be used first.
237 With current implementation, this call will never fail
238 **************************************************************************/
239 static int PrepareInsertListEntries(struct MUI_ListData *data, int pos, int count)
241 memmove(&data->entries[pos+count],&data->entries[pos],(data->entries_num - pos)*sizeof(struct ListEntry*));
242 return 1;
245 /**************************************************************************
246 Inserts a already initialized array of Entries at the given position.
247 This function doesn't care if there is enough space in the datastructure
248 Returns 1 if something failed (never in current implementation)
249 **************************************************************************/
250 #if 0
251 static int InsertListEntries(struct MUI_ListData *data, int pos, struct ListEntry **array, int count)
253 memmove(&data->entries[pos+count],&data->entries[pos],data->entries_num - pos);
254 memcpy(&data->entries[pos],array,count);
255 return 1;
257 #endif
260 /**************************************************************************
261 Removes count (already deinitalized) list entries starting az pos.
262 **************************************************************************/
263 static void RemoveListEntries(struct MUI_ListData *data, int pos, int count)
265 #warning segfault if entries_num = pos = count = 1
266 memmove(&data->entries[pos], &data->entries[pos+count],
267 (data->entries_num - (pos + count)) * sizeof(struct ListEntry *));
270 /**************************************************************************
271 Frees all memory allocated by ParseListFormat()
272 **************************************************************************/
273 static void FreeListFormat(struct MUI_ListData *data)
275 int i;
277 if (data->ci)
279 for (i = 0; i < data->columns; i++)
281 FreeVec(data->ci[i].preparse);
282 data->ci[i].preparse = NULL;
284 FreeVec(data->ci);
285 data->ci = NULL;
287 if (data->preparses)
289 FreeVec(data->preparses);
290 data->preparses = NULL;
292 if (data->strings)
294 FreeVec(data->strings-1);
295 data->strings = NULL;
297 data->columns = 0;
300 /**************************************************************************
301 Parses the given format string (also frees a previouly parsed format).
302 Return 0 on failure.
303 **************************************************************************/
304 static int ParseListFormat(struct MUI_ListData *data, STRPTR format)
306 int new_columns,i;
307 STRPTR ptr;
308 STRPTR format_sep;
309 char c;
311 IPTR args[ARG_CNT];
312 struct RDArgs *rdargs;
314 if (!format) format = (STRPTR) "";
316 ptr = format;
318 FreeListFormat(data);
320 new_columns = 1;
322 /* Count the number of columns first */
323 while ((c = *ptr++))
324 if (c == ',')
325 new_columns++;
327 if (!(data->preparses = AllocVec((new_columns + 10) * sizeof(STRPTR), 0)))
328 return 0;
330 if (!(data->strings = AllocVec((new_columns + 1 + 10) * sizeof(STRPTR), 0))) /* hold enough space also for the entry pos, used by orginal MUI and also some security space */
331 return 0;
333 if (!(data->ci = AllocVec(new_columns * sizeof(struct ColumnInfo), 0)))
334 return 0;
336 // set defaults
337 for (i = 0; i < new_columns; i++)
339 data->ci[i].colno = -1; // -1 means: use unassigned column
340 data->ci[i].weight = 100;
341 data->ci[i].delta = 4;
342 data->ci[i].min_width = -1;
343 data->ci[i].max_width = -1;
344 data->ci[i].user_width = -1;
345 data->ci[i].bar = FALSE;
346 data->ci[i].preparse = NULL;
349 if ((format_sep = StrDup(format)) != 0)
351 for (i = 0 ; format_sep[i] != '\0' ; i++)
353 if (format_sep[i] == ',')
354 format_sep[i] = '\0';
357 if ((rdargs = AllocDosObject(DOS_RDARGS, NULL)) != 0)
359 ptr = format_sep;
360 i = 0;
363 rdargs->RDA_Source.CS_Buffer = ptr;
364 rdargs->RDA_Source.CS_Length = strlen(ptr);
365 rdargs->RDA_Source.CS_CurChr = 0;
366 rdargs->RDA_DAList = 0;
367 rdargs->RDA_Buffer = NULL;
368 rdargs->RDA_BufSiz = 0;
369 rdargs->RDA_ExtHelp = NULL;
370 rdargs->RDA_Flags = 0;
372 memset(args, 0, sizeof args);
373 if (ReadArgs(FORMAT_TEMPLATE, args, rdargs))
375 if (args[ARG_COL])
376 data->ci[i].colno = *(LONG *)args[ARG_COL];
377 if (args[ARG_WEIGHT])
378 data->ci[i].weight = *(LONG *)args[ARG_WEIGHT];
379 if (args[ARG_DELTA])
380 data->ci[i].delta = *(LONG *)args[ARG_DELTA];
381 if (args[ARG_MINWIDTH])
382 data->ci[i].min_width = *(LONG *)args[ARG_MINWIDTH];
383 if (args[ARG_MAXWIDTH])
384 data->ci[i].max_width = *(LONG *)args[ARG_MAXWIDTH];
385 data->ci[i].bar = args[ARG_BAR];
386 if (args[ARG_PREPARSE])
387 data->ci[i].preparse = StrDup((STRPTR)args[ARG_PREPARSE]);
389 FreeArgs(rdargs);
391 ptr += strlen(ptr) + 1;
392 i++;
393 } while(i < new_columns);
394 FreeDosObject(DOS_RDARGS, rdargs);
396 FreeVec(format_sep);
399 for (i = 0; i < new_columns; i++)
401 D(bug("colno %d weight %d delta %d preparse %s\n",
402 data->ci[i].colno, data->ci[i].weight, data->ci[i].delta, data->ci[i].preparse));
405 data->columns = new_columns;
406 data->strings++; /* Skip entry pos */
408 return 1;
411 /**************************************************************************
412 Call the MUIM_List_Display for the given entry. It fills out
413 data->string and data->preparses
414 **************************************************************************/
415 static void DisplayEntry(struct IClass *cl, Object *obj, int entry_pos)
417 struct MUI_ListData *data = INST_DATA(cl, obj);
418 APTR entry_data;
419 int col;
421 for (col = 0; col < data->columns; col++)
422 data->preparses[col] = data->ci[col].preparse;
424 if (entry_pos == ENTRY_TITLE)
426 if ((data->columns == 1) && (data->title != (STRPTR)1))
428 *data->strings = data->title;
429 return;
431 entry_data = NULL; /* it's a title request */
433 else
434 entry_data = data->entries[entry_pos]->data;
436 /* Get the display formation */
437 DoMethod(obj, MUIM_List_Display, (IPTR)entry_data, (IPTR)data->strings,
438 entry_pos, (IPTR)data->preparses);
441 /**************************************************************************
442 Determine the dims of a single entry and adapt the columinfo according
443 to it. pos might be ENTRY_TITLE. Returns 0 if pos entry needs to
444 be redrawn after this operation, 1 if all entries need to be redrawn.
445 **************************************************************************/
446 static int CalcDimsOfEntry(struct IClass *cl, Object *obj, int pos)
448 struct MUI_ListData *data = INST_DATA(cl, obj);
449 struct ListEntry *entry = data->entries[pos];
450 int j;
451 int ret = 0;
453 if (!entry)
454 return ret;
456 DisplayEntry(cl, obj, pos);
458 /* Clear the height */
459 data->entries[pos]->height = data->entry_minheight;
461 for (j = 0; j < data->columns; j++)
463 ZText *text = zune_text_new(data->preparses[j], data->strings[j], ZTEXT_ARG_NONE, 0);
464 if (text != NULL)
466 zune_text_get_bounds(text, obj);
468 if (text->height > data->entries[pos]->height)
470 data->entries[pos]->height = text->height;
471 /* entry height changed, redraw all entries later */
472 ret = 1;
474 data->entries[pos]->widths[j] = text->width;
476 if (text->width > data->ci[j].entries_width)
478 /* This columns width is bigger than the other in the same
479 * columns, so we store this value
481 data->ci[j].entries_width = text->width;
482 /* column width changed, redraw all entries later */
483 ret = 1;
486 zune_text_destroy(text);
489 if (data->entries[pos]->height > data->entry_maxheight)
491 data->entry_maxheight = data->entries[pos]->height;
492 /* maximum entry height changed, redraw all entries later */
493 ret = 1;
496 return ret;
499 /**************************************************************************
500 Determine the widths of the entries
501 **************************************************************************/
502 static void CalcWidths(struct IClass *cl, Object *obj)
504 int i,j;
505 struct MUI_ListData *data = INST_DATA(cl, obj);
507 if (!(_flags(obj) & MADF_SETUP))
508 return;
510 for (j = 0; j < data->columns; j++)
511 data->ci[j].entries_width = 0;
513 data->entry_maxheight = 0;
514 data->entries_totalheight = 0;
515 data->entries_maxwidth = 0;
517 for (i= (data->title ? ENTRY_TITLE : 0) ; i < data->entries_num; i++)
519 CalcDimsOfEntry(cl,obj,i);
520 data->entries_totalheight += data->entries[i]->height;
523 for (j = 0; j < data->columns; j++)
524 data->entries_maxwidth += data->ci[j].entries_width;
526 if (!data->entry_maxheight)
527 data->entry_maxheight = 1;
530 /**************************************************************************
531 Calculates the number of visible entry lines. Returns 1 if it has
532 changed
533 **************************************************************************/
534 static int CalcVertVisible(struct IClass *cl, Object *obj)
536 struct MUI_ListData *data = INST_DATA(cl, obj);
537 int old_entries_visible = data->entries_visible;
538 int old_entries_top_pixel = data->entries_top_pixel;
540 data->entries_visible = (_mheight(obj) - data->title_height)
541 / (data->entry_maxheight /* + data->prefs_linespacing */);
543 data->entries_top_pixel = _mtop(obj) + data->title_height
544 + (_mheight(obj) - data->title_height
545 - data->entries_visible * (data->entry_maxheight /* + data->prefs_linespacing */)) / 2;
547 return (old_entries_visible != data->entries_visible) || (old_entries_top_pixel != data->entries_top_pixel);
550 /**************************************************************************
551 Default hook to compare two list entries. Works for strings only.
552 **************************************************************************/
553 AROS_UFH3S(int, default_compare_func,
554 AROS_UFHA(struct Hook *, h, A0),
555 AROS_UFHA(char *, s2, A2),
556 AROS_UFHA(char *, s1, A1))
558 AROS_USERFUNC_INIT
560 return Stricmp(s1, s2);
562 AROS_USERFUNC_EXIT
565 /**************************************************************************
566 OM_NEW
567 **************************************************************************/
568 IPTR List__OM_NEW(struct IClass *cl, Object *obj, struct opSet *msg)
570 struct MUI_ListData *data;
571 struct TagItem *tag;
572 const struct TagItem *tags;
573 APTR *array = NULL;
574 STRPTR format = NULL;
575 LONG new_entries_active = MUIV_List_Active_Off;
577 obj = (Object *)DoSuperNewTags(cl, obj, NULL,
578 MUIA_Font, MUIV_Font_List,
579 MUIA_Background, MUII_ListBack,
580 TAG_MORE, (IPTR)msg->ops_AttrList);
581 if (!obj) return FALSE;
583 data = INST_DATA(cl, obj);
585 data->columns = 1;
586 data->entries_active = MUIV_List_Active_Off;
587 data->intern_puddle_size = 2008;
588 data->intern_tresh_size = 1024;
589 data->input = 1;
590 data->default_compare_hook.h_Entry = (HOOKFUNC) default_compare_func;
591 data->default_compare_hook.h_SubEntry = 0;
592 data->compare_hook = &(data->default_compare_hook);
594 /* parse initial taglist */
595 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags)); )
597 switch (tag->ti_Tag)
599 case MUIA_List_Active:
600 new_entries_active = tag->ti_Data;
601 break;
603 case MUIA_List_Pool:
604 data->pool = (APTR)tag->ti_Data;
605 break;
607 case MUIA_List_PoolPuddleSize:
608 data->intern_puddle_size = tag->ti_Data;
609 break;
611 case MUIA_List_PoolThreshSize:
612 data->intern_tresh_size = tag->ti_Data;
613 break;
615 case MUIA_List_CompareHook:
616 /* Not tested, if List_CompareHook really works. */
617 data->compare_hook = (struct Hook*)tag->ti_Data;
618 break;
620 case MUIA_List_ConstructHook:
621 data->construct_hook = (struct Hook*)tag->ti_Data;
622 break;
624 case MUIA_List_DestructHook:
625 data->destruct_hook = (struct Hook*)tag->ti_Data;
626 break;
628 case MUIA_List_DisplayHook:
629 data->display_hook = (struct Hook*)tag->ti_Data;
630 break;
632 case MUIA_List_SourceArray:
633 array = (APTR*)tag->ti_Data;
634 break;
636 case MUIA_List_Format:
637 format = (STRPTR)tag->ti_Data;
638 break;
640 case MUIA_List_Title:
641 data->title = (STRPTR)tag->ti_Data;
642 break;
644 case MUIA_List_MinLineHeight:
645 data->entry_minheight = tag->ti_Data;
646 break;
648 case MUIA_List_AdjustHeight:
649 _handle_bool_tag(data->flags, tag->ti_Data, LIST_ADJUSTHEIGHT);
650 break;
652 case MUIA_List_AdjustWidth:
653 _handle_bool_tag(data->flags, tag->ti_Data, LIST_ADJUSTWIDTH);
654 break;
659 if (!data->pool)
661 /* No memory pool given, so we create our own */
662 data->pool = data->intern_pool = CreatePool(0,data->intern_puddle_size,data->intern_tresh_size);
663 if (!data->pool)
665 CoerceMethod(cl,obj,OM_DISPOSE);
666 return 0;
670 /* parse the list format */
671 if (!(ParseListFormat(data,format)))
673 CoerceMethod(cl,obj,OM_DISPOSE);
674 return 0;
677 /* This is neccessary for at least the title */
678 if (!SetListSize(data,0))
680 CoerceMethod(cl,obj,OM_DISPOSE);
681 return 0;
684 if (data->title)
686 if (!(data->entries[ENTRY_TITLE] = AllocListEntry(data)))
688 CoerceMethod(cl,obj,OM_DISPOSE);
689 return 0;
691 } else data->entries[ENTRY_TITLE] = NULL;
694 if (array)
696 int i;
697 /* Count the number of elements */
698 for (i = 0; array[i] != NULL; i++)
700 /* Insert them */
701 DoMethod(obj, MUIM_List_Insert, (IPTR)array, i, MUIV_List_Insert_Top);
705 if ((data->entries_num) && (new_entries_active != MUIV_List_Active_Off))
707 switch (new_entries_active)
709 case MUIV_List_Active_Top:
710 new_entries_active = 0;
711 break;
713 case MUIV_List_Active_Bottom:
714 new_entries_active = data->entries_num - 1;
715 break;
718 if (new_entries_active < 0)
719 new_entries_active = 0;
720 else if (new_entries_active >= data->entries_num)
721 new_entries_active = data->entries_num - 1;
723 data->entries_active = new_entries_active;
724 /* Selected entry will be moved into visible area */
728 data->ehn.ehn_Events = IDCMP_MOUSEBUTTONS |
729 IDCMP_RAWKEY |
730 IDCMP_ACTIVEWINDOW |
731 IDCMP_INACTIVEWINDOW;
732 data->ehn.ehn_Priority = 0;
733 data->ehn.ehn_Flags = 0;
734 data->ehn.ehn_Object = obj;
735 data->ehn.ehn_Class = cl;
737 NewList((struct List *)&data->images);
739 D(bug("List_New(%lx)\n", obj));
741 return (IPTR)obj;
744 /**************************************************************************
745 OM_DISPOSE
746 **************************************************************************/
747 IPTR List__OM_DISPOSE(struct IClass *cl, Object *obj, Msg msg)
749 struct MUI_ListData *data = INST_DATA(cl, obj);
751 D(bug("List Dispose\n"));
753 /* Call destruct method for every entry and free the entries manual to avoid notification */
754 while (data->confirm_entries_num)
756 struct ListEntry *lentry = data->entries[--data->confirm_entries_num];
757 DoMethod(obj, MUIM_List_Destruct, (IPTR)lentry->data, (IPTR)data->pool);
758 FreeListEntry(data, lentry);
761 if (data->intern_pool)
762 DeletePool(data->intern_pool);
763 if (data->entries)
764 FreeVec(data->entries - 1); /* title is currently before all other elements */
766 FreeListFormat(data);
768 return DoSuperMethodA(cl,obj,msg);
772 /**************************************************************************
773 OM_SET
774 **************************************************************************/
775 IPTR List__OM_SET(struct IClass *cl, Object *obj, struct opSet *msg)
777 struct MUI_ListData *data = INST_DATA(cl, obj);
778 struct TagItem *tag;
779 const struct TagItem *tags;
781 /* parse initial taglist */
782 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags)); )
784 switch (tag->ti_Tag)
786 case MUIA_List_CompareHook:
787 data->compare_hook = (struct Hook*)tag->ti_Data;
788 break;
790 case MUIA_List_ConstructHook:
791 data->construct_hook = (struct Hook*)tag->ti_Data;
792 break;
794 case MUIA_List_DestructHook:
795 data->destruct_hook = (struct Hook*)tag->ti_Data;
796 break;
798 case MUIA_List_DisplayHook:
799 data->display_hook = (struct Hook*)tag->ti_Data;
800 break;
802 case MUIA_List_VertProp_First:
803 data->vertprop_first = tag->ti_Data;
804 if (data->entries_first != tag->ti_Data)
806 set(obj,MUIA_List_First,tag->ti_Data);
808 break;
810 case MUIA_List_VertProp_Entries:
811 data->vertprop_entries = tag->ti_Data;
812 break;
814 case MUIA_List_VertProp_Visible:
815 data->vertprop_visible = tag->ti_Data;
816 data->entries_visible = tag->ti_Data;
817 break;
819 case MUIA_List_Active:
821 LONG new_entries_active = tag->ti_Data;
823 if ((data->entries_num) && (new_entries_active != MUIV_List_Active_Off))
825 switch (new_entries_active)
827 case MUIV_List_Active_Top:
828 new_entries_active = 0;
829 break;
831 case MUIV_List_Active_Bottom:
832 new_entries_active = data->entries_num - 1;
833 break;
835 case MUIV_List_Active_Up:
836 new_entries_active = data->entries_active - 1;
837 break;
839 case MUIV_List_Active_Down:
840 new_entries_active = data->entries_active + 1;
841 break;
843 case MUIV_List_Active_PageUp:
844 new_entries_active = data->entries_active - data->entries_visible;
845 break;
847 case MUIV_List_Active_PageDown:
848 new_entries_active = data->entries_active + data->entries_visible;
849 break;
852 if (new_entries_active < 0) new_entries_active = 0;
853 else if (new_entries_active >= data->entries_num) new_entries_active = data->entries_num - 1;
854 } else new_entries_active = -1;
856 if (data->entries_active != new_entries_active)
858 LONG old = data->entries_active;
859 data->entries_active = new_entries_active;
861 data->update = 2;
862 data->update_pos = old;
863 MUI_Redraw(obj,MADF_DRAWUPDATE);
864 data->update = 2;
865 data->update_pos = data->entries_active;
866 MUI_Redraw(obj,MADF_DRAWUPDATE);
868 /* Selectchange stuff */
869 if (old != -1)
871 DoMethod(obj,MUIM_List_SelectChange,old,MUIV_List_Select_Off,0);
874 if (new_entries_active != -1)
876 DoMethod(obj,MUIM_List_SelectChange,new_entries_active,MUIV_List_Select_On,0);
877 DoMethod(obj,MUIM_List_SelectChange,new_entries_active,MUIV_List_Select_Active,0);
878 } else DoMethod(obj,MUIM_List_SelectChange,MUIV_List_Active_Off,MUIV_List_Select_Off,0);
880 set(obj,MUIA_Listview_SelectChange,TRUE);
882 if (new_entries_active != -1)
884 DoMethod(obj, MUIM_List_Jump, MUIV_List_Jump_Active);
888 break;
890 case MUIA_List_First:
891 data->update_pos = data->entries_first;
892 data->update = 3;
893 data->entries_first = tag->ti_Data;
895 MUI_Redraw(obj,MADF_DRAWUPDATE);
896 if (data->vertprop_first != tag->ti_Data)
898 set(obj,MUIA_List_VertProp_First,tag->ti_Data);
900 break;
902 case MUIA_List_Visible:
903 if (data->vertprop_visible != tag->ti_Data)
904 set(obj,MUIA_List_VertProp_Visible, tag->ti_Data);
905 break;
907 case MUIA_List_Entries:
908 if (data->confirm_entries_num == tag->ti_Data)
910 data->entries_num = tag->ti_Data;
911 set(obj, MUIA_List_VertProp_Entries, data->entries_num);
912 } else
914 D(bug("Bug: confirm_entries != MUIA_List_Entries!\n"));
916 break;
918 case MUIA_List_Quiet:
919 _handle_bool_tag(data->flags, tag->ti_Data, LIST_QUIET);
920 if (!tag->ti_Data)
922 DoMethod(obj, MUIM_List_Redraw, MUIV_List_Redraw_All);
924 break;
928 return DoSuperMethodA(cl, obj, (Msg)msg);
931 /**************************************************************************
932 OM_GET
933 **************************************************************************/
934 IPTR List__OM_GET(struct IClass *cl, Object *obj, struct opGet *msg)
936 /* small macro to simplify return value storage */
937 #define STORE *(msg->opg_Storage)
938 struct MUI_ListData *data = INST_DATA(cl, obj);
940 switch (msg->opg_AttrID)
942 case MUIA_List_Entries: STORE = data->entries_num; return 1;
943 case MUIA_List_First: STORE = data->entries_first; return 1;
944 case MUIA_List_Active: STORE = data->entries_active; return 1;
945 case MUIA_List_InsertPosition: STORE = data->insert_position; return 1;
946 case MUIA_List_Title: STORE = (unsigned long)data->title; return 1;
947 case MUIA_List_VertProp_Entries: STORE = STORE = data->vertprop_entries; return 1;
948 case MUIA_List_VertProp_Visible: STORE = data->vertprop_visible; return 1;
949 case MUIA_List_VertProp_First: STORE = data->vertprop_first; return 1;
951 case MUIA_Listview_DoubleClick: STORE = 0; return 1;
954 if (DoSuperMethodA(cl, obj, (Msg) msg)) return 1;
955 return 0;
956 #undef STORE
959 /**************************************************************************
960 MUIM_Setup
961 **************************************************************************/
962 IPTR List__MUIM_Setup(struct IClass *cl, Object *obj, struct MUIP_Setup *msg)
964 struct MUI_ListData *data = INST_DATA(cl, obj);
966 if (!DoSuperMethodA(cl, obj, (Msg) msg))
967 return 0;
969 data->prefs_multi = muiGlobalInfo(obj)->mgi_Prefs->list_multi;
970 data->prefs_refresh = muiGlobalInfo(obj)->mgi_Prefs->list_refresh;
971 data->prefs_linespacing = muiGlobalInfo(obj)->mgi_Prefs->list_linespacing;
972 data->prefs_smoothed = muiGlobalInfo(obj)->mgi_Prefs->list_smoothed;
973 data->prefs_smoothval = muiGlobalInfo(obj)->mgi_Prefs->list_smoothval;
975 CalcWidths(cl,obj);
977 if (data->title)
979 data->title_height = data->entries[ENTRY_TITLE]->height + 2;
981 else
983 data->title_height = 0;
986 DoMethod(_win(obj),MUIM_Window_AddEventHandler, (IPTR)&data->ehn);
988 data->list_cursor = zune_imspec_setup(MUII_ListCursor, muiRenderInfo(obj));
989 data->list_select = zune_imspec_setup(MUII_ListSelect, muiRenderInfo(obj));
990 data->list_selcur = zune_imspec_setup(MUII_ListSelCur, muiRenderInfo(obj));
992 return 1;
995 /**************************************************************************
996 MUIM_Cleanup
997 **************************************************************************/
998 IPTR List__MUIM_Cleanup(struct IClass *cl, Object *obj, struct MUIP_Cleanup *msg)
1000 struct MUI_ListData *data = INST_DATA(cl, obj);
1001 struct ListImage *li = List_First(&data->images);
1003 while (li)
1005 struct ListImage *next = Node_Next(li);
1006 DoMethod(obj, MUIM_List_DeleteImage, (IPTR)li);
1007 li = next;
1010 zune_imspec_cleanup(data->list_cursor);
1011 zune_imspec_cleanup(data->list_select);
1012 zune_imspec_cleanup(data->list_selcur);
1014 DoMethod(_win(obj),MUIM_Window_RemEventHandler, (IPTR)&data->ehn);
1015 data->ehn.ehn_Events &= ~(IDCMP_MOUSEMOVE | IDCMP_INTUITICKS);
1016 data->mouse_click = 0;
1018 return DoSuperMethodA(cl, obj, (Msg) msg);
1021 /**************************************************************************
1022 MUIM_AskMinMax
1023 **************************************************************************/
1024 IPTR List__MUIM_AskMinMax(struct IClass *cl, Object *obj,struct MUIP_AskMinMax *msg)
1026 struct MUI_ListData *data = INST_DATA(cl, obj);
1028 DoSuperMethodA(cl, obj, (Msg)msg);
1031 if ((data->flags & LIST_ADJUSTWIDTH) && (data->entries_num > 0))
1033 msg->MinMaxInfo->MinWidth += data->entries_maxwidth;
1034 msg->MinMaxInfo->DefWidth += data->entries_maxwidth;
1035 msg->MinMaxInfo->MaxWidth += data->entries_maxwidth;
1037 else
1039 msg->MinMaxInfo->MinWidth += 40;
1040 msg->MinMaxInfo->DefWidth += 100;
1041 msg->MinMaxInfo->MaxWidth = MUI_MAXMAX;
1044 if (data->entries_num > 0)
1046 if (data->flags & LIST_ADJUSTHEIGHT)
1048 msg->MinMaxInfo->MinHeight += data->entries_totalheight;
1049 msg->MinMaxInfo->DefHeight += data->entries_totalheight;
1050 msg->MinMaxInfo->MaxHeight += data->entries_totalheight;
1052 else
1054 ULONG h = data->entry_maxheight + data->prefs_linespacing;
1055 msg->MinMaxInfo->MinHeight += 2 * h + data->prefs_linespacing;
1056 msg->MinMaxInfo->DefHeight += 8 * h + data->prefs_linespacing;
1057 msg->MinMaxInfo->MaxHeight = MUI_MAXMAX;
1060 else
1062 msg->MinMaxInfo->MinHeight += 36;
1063 msg->MinMaxInfo->DefHeight += 96;
1064 msg->MinMaxInfo->MaxHeight = MUI_MAXMAX;
1066 D(bug("List %p minheigh=%d, line maxh=%d\n",
1067 obj, msg->MinMaxInfo->MinHeight, data->entry_maxheight));
1068 return TRUE;
1071 /**************************************************************************
1072 MUIM_Layout
1073 **************************************************************************/
1074 IPTR List__MUIM_Layout(struct IClass *cl, Object *obj,struct MUIP_Layout *msg)
1076 struct MUI_ListData *data = INST_DATA(cl, obj);
1077 ULONG rc = DoSuperMethodA(cl,obj,(Msg)msg);
1078 LONG new_entries_first = data->entries_first;
1080 /* Calc the numbers of entries visible */
1081 CalcVertVisible(cl,obj);
1083 #if 0 /* Don't do this! */
1084 if (data->entries_active < new_entries_first)
1085 new_entries_first = data->entries_active;
1086 #endif
1088 if (data->entries_active + 1 >=
1089 (data->entries_first + data->entries_visible))
1090 new_entries_first =
1091 data->entries_active - data->entries_visible + 1;
1093 if ((new_entries_first + data->entries_visible >=
1094 data->entries_num)
1096 (data->entries_visible <= data->entries_num))
1097 new_entries_first =
1098 data->entries_num - data->entries_visible;
1100 if (data->entries_num <= data->entries_visible)
1101 new_entries_first = 0;
1103 if (new_entries_first < 0) new_entries_first = 0;
1105 set(obj, new_entries_first != data->entries_first ?
1106 MUIA_List_First : TAG_IGNORE,
1107 new_entries_first);
1109 /* So the notify takes happens */
1110 set(obj, MUIA_List_VertProp_Visible, data->entries_visible);
1112 return rc;
1116 /**************************************************************************
1117 MUIM_Show
1118 **************************************************************************/
1119 IPTR List__MUIM_Show(struct IClass *cl, Object *obj, struct MUIP_Show *msg)
1121 struct MUI_ListData *data = INST_DATA(cl, obj);
1122 ULONG rc = DoSuperMethodA(cl, obj, (Msg)msg);
1124 zune_imspec_show(data->list_cursor, obj);
1125 zune_imspec_show(data->list_select, obj);
1126 zune_imspec_show(data->list_selcur, obj);
1127 return rc;
1131 /**************************************************************************
1132 MUIM_Hide
1133 **************************************************************************/
1134 IPTR List__MUIM_Hide(struct IClass *cl, Object *obj, struct MUIP_Hide *msg)
1136 struct MUI_ListData *data = INST_DATA(cl, obj);
1138 #if 0
1139 if (data->ehn.ehn_Events & (IDCMP_MOUSEMOVE | IDCMP_INTUITICKS))
1141 DoMethod(_win(obj),MUIM_Window_RemEventHandler, (IPTR)&data->ehn);
1142 data->ehn.ehn_Events &= ~(IDCMP_MOUSEMOVE | IDCMP_INTUITICKS);
1143 DoMethod(_win(obj),MUIM_Window_AddEventHandler, (IPTR)&data->ehn);
1145 data->mouse_click = 0;
1146 #endif
1148 zune_imspec_hide(data->list_cursor);
1149 zune_imspec_hide(data->list_select);
1150 zune_imspec_hide(data->list_selcur);
1152 return DoSuperMethodA(cl, obj, (Msg)msg);
1156 /**************************************************************************
1157 Draw an entry at entry_pos at the given y location. To draw the title,
1158 set pos to ENTRY_TITLE
1159 **************************************************************************/
1160 static VOID List_DrawEntry(struct IClass *cl, Object *obj, int entry_pos, int y)
1162 struct MUI_ListData *data = INST_DATA(cl, obj);
1163 int col,x1,x2;
1165 /* To be surem we don't draw anything if there is no title */
1166 if (entry_pos == ENTRY_TITLE && !data->title) return;
1168 DisplayEntry(cl,obj,entry_pos);
1169 x1 = _mleft(obj);
1171 for (col = 0; col < data->columns; col++)
1173 ZText *text;
1174 x2 = x1 + data->ci[col].entries_width;
1176 if ((text = zune_text_new(data->preparses[col], data->strings[col], ZTEXT_ARG_NONE, 0)))
1178 /* Could be made simpler, as we don't really need the bounds */
1179 zune_text_get_bounds(text, obj);
1180 /* Note, this was MPEN_SHADOW before */
1181 SetAPen(_rp(obj), muiRenderInfo(obj)->mri_Pens[MPEN_TEXT]);
1182 zune_text_draw(text, obj, x1, x2, y); /* totally wrong! */
1183 zune_text_destroy(text);
1185 x1 = x2 + data->ci[col].delta + (data->ci[col].bar ? BAR_WIDTH : 0);
1189 /**************************************************************************
1190 MUIM_Draw
1191 **************************************************************************/
1192 IPTR List__MUIM_Draw(struct IClass *cl, Object *obj, struct MUIP_Draw *msg)
1194 struct MUI_ListData *data = INST_DATA(cl, obj);
1195 int entry_pos,y;
1196 APTR clip;
1197 int start, end;
1198 BOOL scroll_caused_damage = FALSE;
1200 DoSuperMethodA(cl, obj, (Msg) msg);
1202 if (msg->flags & MADF_DRAWUPDATE)
1204 if (data->update == 1)
1205 DoMethod(obj, MUIM_DrawBackground, _mleft(obj), _mtop(obj),
1206 _mwidth(obj), _mheight(obj),
1207 0, data->entries_first * data->entry_maxheight, 0);
1209 else
1211 DoMethod(obj, MUIM_DrawBackground, _mleft(obj), _mtop(obj),
1212 _mwidth(obj), _mheight(obj),
1213 0, data->entries_first * data->entry_maxheight, 0);
1216 clip = MUI_AddClipping(muiRenderInfo(obj), _mleft(obj), _mtop(obj),
1217 _mwidth(obj), _mheight(obj));
1219 if (!(msg->flags & MADF_DRAWUPDATE)
1220 || ((msg->flags & MADF_DRAWUPDATE) && data->update == 1))
1222 y = _mtop(obj);
1223 /* Draw Title
1225 if (data->title_height && data->title)
1227 List_DrawEntry(cl,obj,ENTRY_TITLE,y);
1228 y += data->entries[ENTRY_TITLE]->height;
1229 SetAPen(_rp(obj),_pens(obj)[MPEN_SHADOW]);
1230 Move(_rp(obj),_mleft(obj), y);
1231 Draw(_rp(obj),_mright(obj), y);
1232 SetAPen(_rp(obj),_pens(obj)[MPEN_SHINE]);
1233 y++;
1234 Move(_rp(obj),_mleft(obj), y);
1235 Draw(_rp(obj),_mright(obj), y);
1239 y = data->entries_top_pixel;
1241 start = data->entries_first;
1242 end = data->entries_first + data->entries_visible;
1244 if ((msg->flags & MADF_DRAWUPDATE) && data->update == 3)
1246 int diffy = data->entries_first - data->update_pos;
1247 int top,bottom;
1248 if (abs(diffy) < data->entries_visible)
1250 scroll_caused_damage = (_rp(obj)->Layer->Flags & LAYERREFRESH) ? FALSE : TRUE;
1252 ScrollRaster(_rp(obj), 0, diffy * data->entry_maxheight,
1253 _mleft(obj), y,
1254 _mright(obj), y + data->entry_maxheight * data->entries_visible);
1256 scroll_caused_damage =
1257 scroll_caused_damage && (_rp(obj)->Layer->Flags & LAYERREFRESH);
1259 if (diffy > 0)
1261 start = end - diffy;
1262 y += data->entry_maxheight * (data->entries_visible - diffy);
1264 else end = start - diffy;
1267 top = y;
1268 bottom = y + (end - start) * data->entry_maxheight;
1270 DoMethod(obj, MUIM_DrawBackground, _mleft(obj), top,
1271 _mwidth(obj), bottom - top + 1,
1272 0, top - _mtop(obj) + data->entries_first * data->entry_maxheight, 0);
1273 } /* if ((msg->flags & MADF_DRAWUPDATE) && data->update == 3) */
1275 for (entry_pos = start; entry_pos < end && entry_pos < data->entries_num; entry_pos++)
1277 //struct ListEntry *entry = data->entries[entry_pos];
1279 if (!(msg->flags & MADF_DRAWUPDATE) ||
1280 ((msg->flags & MADF_DRAWUPDATE) && data->update == 1) ||
1281 ((msg->flags & MADF_DRAWUPDATE) && data->update == 3) ||
1282 ((msg->flags & MADF_DRAWUPDATE) && data->update == 2 && data->update_pos == entry_pos))
1284 if (entry_pos == data->entries_active)
1286 zune_imspec_draw(data->list_cursor, muiRenderInfo(obj),
1287 _mleft(obj),y,_mwidth(obj), data->entry_maxheight,
1288 0, y - data->entries_top_pixel,0);
1289 } else
1291 if ((msg->flags & MADF_DRAWUPDATE) && data->update == 2 && data->update_pos == entry_pos)
1293 DoMethod(obj,MUIM_DrawBackground,_mleft(obj),y,_mwidth(obj), data->entry_maxheight,
1294 0,y - _mtop(obj) + data->entries_first * data->entry_maxheight,0);
1297 List_DrawEntry(cl,obj,entry_pos,y);
1299 y += data->entry_maxheight;
1300 } /* for */
1302 MUI_RemoveClipping(muiRenderInfo(obj),clip);
1304 data->update = 0;
1306 if (scroll_caused_damage)
1308 if (MUI_BeginRefresh(muiRenderInfo(obj), 0))
1310 /* Theoretically it might happen that more damage is caused
1311 after ScrollRaster. By something else, like window movement
1312 in front of our window. Therefore refresh root object of
1313 window, not just this object */
1315 Object *o;
1317 get(_win(obj),MUIA_Window_RootObject, &o);
1318 MUI_Redraw(o, MADF_DRAWOBJECT);
1320 MUI_EndRefresh(muiRenderInfo(obj), 0);
1324 ULONG x1 = _mleft(obj);
1325 ULONG col;
1326 y = _mtop(obj);
1328 if (data->title_height && data->title)
1330 for (col = 0; col < data->columns; col++)
1332 ULONG halfdelta = data->ci[col].delta / 2;
1333 x1 += data->ci[col].entries_width + halfdelta;
1335 if(x1 + (data->ci[col].bar ? BAR_WIDTH : 0) > _mright(obj))
1336 break;
1338 if(data->ci[col].bar)
1340 SetAPen(_rp(obj),_pens(obj)[MPEN_SHINE]);
1341 Move(_rp(obj),x1, y);
1342 Draw(_rp(obj),x1, y + data->entries[ENTRY_TITLE]->height - 1);
1343 SetAPen(_rp(obj),_pens(obj)[MPEN_SHADOW]);
1344 Move(_rp(obj),x1 + 1, y);
1345 Draw(_rp(obj),x1 + 1, y + data->entries[ENTRY_TITLE]->height - 1);
1347 x1 += BAR_WIDTH;
1349 x1 += data->ci[col].delta - halfdelta;
1351 y += data->entries[ENTRY_TITLE]->height + 1;
1354 x1 = _mleft(obj);
1356 for (col = 0; col < data->columns; col++)
1358 ULONG halfdelta = data->ci[col].delta / 2;
1359 x1 += data->ci[col].entries_width + halfdelta;
1361 if(x1 + (data->ci[col].bar ? BAR_WIDTH : 0) > _mright(obj))
1362 break;
1364 if(data->ci[col].bar)
1366 SetAPen(_rp(obj),_pens(obj)[MPEN_SHINE]);
1367 Move(_rp(obj),x1, y);
1368 Draw(_rp(obj),x1, _mbottom(obj));
1369 SetAPen(_rp(obj),_pens(obj)[MPEN_SHADOW]);
1370 Move(_rp(obj),x1 + 1, y);
1371 Draw(_rp(obj),x1 + 1, _mbottom(obj));
1373 x1 += BAR_WIDTH;
1376 x1 += data->ci[col].delta - halfdelta;
1379 return 0;
1382 /**************************************************************************
1383 Makes the entry at the given mouse position the active one.
1384 Relx and Rely are relative mouse coordinates to the upper left of
1385 the object
1386 **************************************************************************/
1387 static VOID List_MakeActive(struct IClass *cl, Object *obj, LONG relx, LONG rely)
1389 struct MUI_ListData *data = INST_DATA(cl, obj);
1391 if (data->entries_num == 0)
1392 return;
1394 LONG eclicky = rely + _top(obj) - data->entries_top_pixel; /* y coordinates transfromed to the entries */
1395 LONG new_act = eclicky / data->entry_maxheight + data->entries_first;
1396 LONG old_act = data->entries_active;
1398 if (eclicky < 0)
1400 new_act = data->entries_first - 1;
1402 else if (new_act > data->entries_first + data->entries_visible)
1404 new_act = data->entries_first + data->entries_visible;
1407 if (new_act >= data->entries_num) new_act = data->entries_num - 1;
1408 else if (new_act < 0) new_act = 0;
1410 /* Notify only when active entry has changed */
1411 if (old_act != new_act)
1412 set(obj, MUIA_List_Active, new_act);
1415 static void DoWheelMove(struct IClass *cl, Object *obj, LONG wheely, UWORD qual)
1417 struct MUI_ListData *data = INST_DATA(cl, obj);
1418 LONG new = data->entries_first;
1420 if (qual & IEQUALIFIER_CONTROL)
1422 if (wheely < 0) new = 0;
1423 if (wheely > 0) new = data->entries_num;
1425 else if (qual & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
1427 new += (wheely * data->entries_visible);
1429 else
1431 new += wheely * 3;
1434 if (new > data->entries_num - data->entries_visible)
1436 new = data->entries_num - data->entries_visible;
1439 if (new < 0)
1441 new = 0;
1444 if (new != data->entries_first)
1446 set(obj, MUIA_List_First, new);
1451 /**************************************************************************
1452 MUIM_HandleEvent
1453 **************************************************************************/
1454 IPTR List__MUIM_HandleEvent(struct IClass *cl, Object *obj, struct MUIP_HandleEvent *msg)
1456 struct MUI_ListData *data = INST_DATA(cl, obj);
1458 if (msg->imsg)
1460 LONG mx = msg->imsg->MouseX - _left(obj);
1461 LONG my = msg->imsg->MouseY - _top(obj);
1462 switch (msg->imsg->Class)
1464 case IDCMP_MOUSEBUTTONS:
1465 if (msg->imsg->Code == SELECTDOWN)
1467 if (mx >= 0 && mx < _width(obj) && my >= 0 && my < _height(obj))
1469 LONG eclicky = my + _top(obj) - data->entries_top_pixel; /* y coordinates transfromed to the entries */
1470 data->mouse_click = MOUSE_CLICK_ENTRY;
1471 /* Now check if it was clicked on a title or on the entries */
1472 if (eclicky >= 0 && eclicky < data->entries_visible * data->entry_maxheight)
1474 List_MakeActive(cl, obj, mx, my); /* sets data->entries_active */
1476 if (data->last_active == data->entries_active
1477 && DoubleClick(data->last_secs, data->last_mics, msg->imsg->Seconds, msg->imsg->Micros))
1479 set(obj, MUIA_Listview_DoubleClick, TRUE);
1480 data->last_active = -1;
1481 data->last_secs = data->last_mics = 0;
1482 } else
1484 data->last_active = data->entries_active;
1485 data->last_secs = msg->imsg->Seconds;
1486 data->last_mics = msg->imsg->Micros;
1490 DoMethod(_win(obj),MUIM_Window_RemEventHandler, (IPTR)&data->ehn);
1491 data->ehn.ehn_Events |= (IDCMP_MOUSEMOVE | IDCMP_INTUITICKS);
1492 DoMethod(_win(obj),MUIM_Window_AddEventHandler, (IPTR)&data->ehn);
1494 return MUI_EventHandlerRC_Eat;
1496 } else
1498 if (msg->imsg->Code == SELECTUP && data->mouse_click)
1500 DoMethod(_win(obj),MUIM_Window_RemEventHandler, (IPTR)&data->ehn);
1501 data->ehn.ehn_Events &= ~(IDCMP_MOUSEMOVE | IDCMP_INTUITICKS);
1502 DoMethod(_win(obj),MUIM_Window_AddEventHandler, (IPTR)&data->ehn);
1503 data->mouse_click = 0;
1504 return 0;
1507 break;
1509 case IDCMP_INTUITICKS:
1510 case IDCMP_MOUSEMOVE:
1511 if (data->mouse_click)
1513 List_MakeActive(cl, obj, mx, my);
1515 break;
1517 case IDCMP_RAWKEY:
1518 switch(msg->imsg->Code)
1520 case RAWKEY_NM_WHEEL_UP:
1521 if (_isinobject(msg->imsg->MouseX, msg->imsg->MouseY))
1523 DoWheelMove(cl, obj, -1, msg->imsg->Qualifier);
1525 break;
1527 case RAWKEY_NM_WHEEL_DOWN:
1528 if (_isinobject(msg->imsg->MouseX, msg->imsg->MouseY))
1530 DoWheelMove(cl, obj, 1, msg->imsg->Qualifier);
1532 break;
1535 break;
1537 case IDCMP_ACTIVEWINDOW:
1538 case IDCMP_INACTIVEWINDOW:
1539 if (data->ehn.ehn_Events & (IDCMP_MOUSEMOVE | IDCMP_INTUITICKS))
1541 DoMethod(_win(obj),MUIM_Window_RemEventHandler, (IPTR)&data->ehn);
1542 data->ehn.ehn_Events &= ~(IDCMP_MOUSEMOVE | IDCMP_INTUITICKS);
1543 DoMethod(_win(obj),MUIM_Window_AddEventHandler, (IPTR)&data->ehn);
1544 data->mouse_click = 0;
1546 break;
1550 return 0;
1553 /**************************************************************************
1554 MUIM_List_Clear
1555 **************************************************************************/
1556 IPTR List__MUIM_Clear(struct IClass *cl, Object *obj, struct MUIP_List_Clear *msg)
1558 struct MUI_ListData *data = INST_DATA(cl, obj);
1560 while (data->confirm_entries_num)
1562 struct ListEntry *lentry = data->entries[--data->confirm_entries_num];
1563 DoMethod(obj, MUIM_List_Destruct, (IPTR)lentry->data, (IPTR)data->pool);
1564 FreeListEntry(data,lentry);
1566 /* Should never fail when shrinking */
1567 SetListSize(data,0);
1569 if (data->confirm_entries_num != data->entries_num)
1571 SetAttrs(obj,
1572 MUIA_List_Entries,0,
1573 MUIA_List_First,0,
1574 /* Notify only when no entry was active */
1575 data->entries_active != MUIV_List_Active_Off ? MUIA_List_Active : TAG_DONE, MUIV_List_Active_Off,
1576 TAG_DONE);
1578 data->update = 1;
1579 MUI_Redraw(obj,MADF_DRAWUPDATE);
1582 return 0;
1585 /**************************************************************************
1586 MUIM_List_Exchange
1587 **************************************************************************/
1588 IPTR List__MUIM_Exchange(struct IClass *cl, Object *obj, struct MUIP_List_Exchange *msg)
1590 struct MUI_ListData *data = INST_DATA(cl, obj);
1591 LONG pos1,
1592 pos2;
1594 switch (msg->pos1)
1596 case MUIV_List_Exchange_Top: pos1 = 0; break;
1597 case MUIV_List_Exchange_Active: pos1 = data->entries_active; break;
1598 case MUIV_List_Exchange_Bottom: pos1 = data->entries_num - 1; break;
1599 default: pos1 = msg->pos1;
1602 switch (msg->pos2)
1604 case MUIV_List_Exchange_Top: pos2 = 0; break;
1605 case MUIV_List_Exchange_Active: pos2 = data->entries_active; break;
1606 case MUIV_List_Exchange_Bottom: pos2 = data->entries_num - 1; break;
1607 case MUIV_List_Exchange_Next: pos2 = pos1 + 1; break;
1608 case MUIV_List_Exchange_Previous: pos2 = pos1 - 1; break;
1609 default: pos2 = msg->pos2;
1612 if (pos1 >= 0 && pos1 < data->entries_num && pos2 >= 0 && pos2 <= data->entries_num && pos1 != pos2)
1614 struct ListEntry *save = data->entries[pos1];
1615 data->entries[pos1] = data->entries[pos2];
1616 data->entries[pos2] = save;
1618 data->update = 2;
1619 data->update_pos = pos1;
1620 MUI_Redraw(obj,MADF_DRAWUPDATE);
1622 data->update = 2;
1623 data->update_pos = pos2;
1624 MUI_Redraw(obj,MADF_DRAWUPDATE);
1626 return TRUE;
1628 else
1630 return FALSE;
1634 /**************************************************************************
1635 MUIM_List_Redraw
1636 **************************************************************************/
1637 IPTR List__MUIM_Redraw(struct IClass *cl, Object *obj, struct MUIP_List_Redraw *msg)
1639 struct MUI_ListData *data = INST_DATA(cl, obj);
1641 if (msg->pos == MUIV_List_Redraw_All)
1643 data->update = 1;
1644 CalcWidths(cl,obj);
1645 MUI_Redraw(obj,MADF_DRAWUPDATE);
1646 } else
1648 LONG pos;
1649 if (msg->pos == MUIV_List_Redraw_Active) pos = data->entries_active;
1650 else pos = msg->pos;
1652 if (pos != -1)
1654 if(CalcDimsOfEntry(cl, obj, pos))
1655 data->update = 1;
1656 else
1658 data->update = 2;
1659 data->update_pos = pos;
1661 MUI_Redraw(obj,MADF_DRAWUPDATE);
1664 return 0;
1667 /**************************************************************************
1668 MUIM_List_Remove
1669 **************************************************************************/
1670 IPTR List__MUIM_Remove(struct IClass *cl, Object *obj, struct MUIP_List_Remove *msg)
1672 struct MUI_ListData *data = INST_DATA(cl, obj);
1673 LONG pos,cur;
1674 LONG new_act;
1675 struct ListEntry *lentry;
1676 //int rem_count = 1;
1678 if (!data->entries_num) return 0;
1680 switch(msg->pos)
1682 case MUIV_List_Remove_First:
1683 pos = 0;
1684 break;
1686 case MUIV_List_Remove_Active:
1687 pos = data->entries_active;
1688 break;
1690 case MUIV_List_Remove_Last:
1691 pos = data->entries_num - 1;
1692 break;
1694 case MUIV_List_Remove_Selected:
1695 /* TODO: needs special handling */
1696 pos = data->entries_active;
1697 break;
1699 default:
1700 pos = msg->pos;
1701 break;
1704 if (pos < 0 || pos >= data->entries_num)
1705 return 0;
1707 new_act = data->entries_active;
1709 if (pos == new_act && new_act == data->entries_num - 1)
1710 new_act--; /* might become MUIV_List_Active_Off */
1712 lentry = data->entries[pos];
1713 DoMethod(obj, MUIM_List_Destruct, (IPTR)lentry->data, (IPTR)data->pool);
1715 cur = pos + 1;
1717 RemoveListEntries(data, pos, cur - pos);
1718 data->confirm_entries_num -= cur - pos;
1720 /* ensure that the active element is in a valid range */
1721 if (new_act >= data->entries_num) new_act = data->entries_num - 1;
1723 SetAttrs(obj,
1724 MUIA_List_Entries, data->confirm_entries_num,
1725 (new_act >= pos) || (new_act != data->entries_active) ?
1726 MUIA_List_Active : TAG_DONE, new_act, /* Inform only if neccessary (for notify) */
1727 TAG_DONE);
1729 data->update = 1;
1730 MUI_Redraw(obj,MADF_DRAWUPDATE);
1732 return 0;
1735 /**************************************************************************
1736 MUIM_List_Insert
1737 **************************************************************************/
1739 IPTR List__MUIM_Insert(struct IClass *cl, Object *obj, struct MUIP_List_Insert *msg)
1741 struct MUI_ListData *data = INST_DATA(cl, obj);
1742 LONG pos,count,sort;
1744 count = msg->count;
1745 sort=0;
1747 if (count == -1)
1749 /* Count the number of entries */
1750 for(count = 0; msg->entries[count] != NULL; count++)
1754 if (count <= 0)
1755 return ~0;
1757 switch (msg->pos)
1759 case MUIV_List_Insert_Top:
1760 pos = 0;
1761 break;
1763 case MUIV_List_Insert_Active:
1764 if (data->entries_active != -1) pos = data->entries_active;
1765 else pos = data->entries_active;
1766 break;
1768 case MUIV_List_Insert_Sorted:
1769 pos = data->entries_num;
1770 sort = 1; /* we sort'em later */
1771 break;
1773 case MUIV_List_Insert_Bottom:
1774 pos = data->entries_num;
1775 break;
1777 default:
1778 if (msg->pos > data->entries_num) pos = data->entries_num;
1779 else if (msg->pos < 0) pos = 0;
1780 else pos = msg->pos;
1781 break;
1784 if (!(SetListSize(data,data->entries_num + count)))
1785 return ~0;
1787 LONG until = pos + count;
1788 APTR *toinsert = msg->entries;
1790 if (!(PrepareInsertListEntries(data, pos, count)))
1791 return ~0;
1793 while (pos < until)
1795 struct ListEntry *lentry;
1797 if (!(lentry = AllocListEntry(data)))
1799 /* Panic, but we must be in a consistent state, so remove
1800 ** the space where the following list entries should have gone
1802 RemoveListEntries(data, pos, until - pos);
1803 return ~0;
1806 /* now call the construct method which returns us a pointer which
1807 we need to store */
1808 lentry->data = (APTR)DoMethod(obj, MUIM_List_Construct,
1809 (IPTR)*toinsert, (IPTR)data->pool);
1810 if (!lentry->data)
1812 FreeListEntry(data,lentry);
1813 RemoveListEntries(data, pos, until - pos);
1815 /* TODO: Also check for visible stuff like below */
1816 if (data->entries_num != data->confirm_entries_num)
1817 set(obj,MUIA_List_Entries,data->confirm_entries_num);
1818 return ~0;
1821 data->entries[pos] = lentry;
1822 data->confirm_entries_num++;
1824 if (_flags(obj) & MADF_SETUP)
1826 /* We have to calculate the width and height of the newly inserted entry,
1827 this has to be done after inserting the element into the list */
1828 CalcDimsOfEntry(cl, obj, pos);
1831 toinsert++;
1832 pos++;
1833 } // while (pos < until)
1836 if (_flags(obj) & MADF_SETUP)
1837 CalcVertVisible(cl,obj); /* Recalculate the number of visible entries */
1839 if (data->entries_num != data->confirm_entries_num)
1841 SetAttrs(obj,
1842 MUIA_List_Entries, data->confirm_entries_num,
1843 MUIA_List_Visible, data->entries_visible,
1844 TAG_DONE);
1847 /* If the array is already sorted, we could do a simple insert
1848 * sort and would be much faster than with qsort.
1849 * If an array is not yet sorted, does a MUIV_List_Insert_Sorted
1850 * sort the whole array?
1852 * I think, we better sort the whole array:
1854 if (sort)
1856 DoMethod(obj,MUIM_List_Sort);
1857 /* TODO: which pos to return here !? */
1858 /* MUIM_List_Sort already called MUI_Redraw */
1860 else
1862 if (!(data->flags & LIST_QUIET))
1864 data->update = 1;
1865 MUI_Redraw(obj,MADF_DRAWUPDATE);
1868 data->insert_position = pos;
1870 return (ULONG)pos;
1873 /**************************************************************************
1874 MUIM_List_InsertSingle
1875 **************************************************************************/
1876 IPTR List__MUIM_InsertSingle(struct IClass *cl, Object *obj, struct MUIP_List_InsertSingle *msg)
1878 return DoMethod(obj,MUIM_List_Insert, (IPTR)&msg->entry, 1, msg->pos);
1881 /**************************************************************************
1882 MUIM_List_GetEntry
1883 **************************************************************************/
1884 IPTR List__MUIM_GetEntry(struct IClass *cl, Object *obj, struct MUIP_List_GetEntry *msg)
1886 struct MUI_ListData *data = INST_DATA(cl, obj);
1887 int pos = msg->pos;
1889 if (pos == MUIV_List_GetEntry_Active) pos = data->entries_active;
1891 if (pos < 0 || pos >= data->entries_num)
1893 *msg->entry = NULL;
1894 return 0;
1896 *msg->entry = data->entries[pos]->data;
1897 return (IPTR)*msg->entry;
1900 /**************************************************************************
1901 MUIM_List_Construct
1902 **************************************************************************/
1903 IPTR List__MUIM_Construct(struct IClass *cl, Object *obj, struct MUIP_List_Construct *msg)
1905 struct MUI_ListData *data = INST_DATA(cl, obj);
1907 if (NULL == data->construct_hook)
1908 return (IPTR)msg->entry;
1909 if ((ULONG)data->construct_hook == MUIV_List_ConstructHook_String)
1911 int len = msg->entry ? strlen((STRPTR)msg->entry) : 0;
1912 ULONG *mem = AllocPooled(msg->pool, len+5);
1914 if (NULL == mem)
1915 return 0;
1916 mem[0] = len + 5;
1917 if (msg->entry != NULL)
1918 strcpy((STRPTR)(mem+1), (STRPTR)msg->entry);
1919 else
1920 *(STRPTR)(mem+1) = 0;
1921 return (IPTR)(mem+1);
1923 return CallHookPkt(data->construct_hook, msg->pool, msg->entry);
1926 /**************************************************************************
1927 MUIM_List_Destruct
1928 **************************************************************************/
1929 IPTR List__MUIM_Destruct(struct IClass *cl, Object *obj, struct MUIP_List_Destruct *msg)
1931 struct MUI_ListData *data = INST_DATA(cl, obj);
1933 if (NULL == data->destruct_hook)
1934 return 0;
1936 if ((ULONG)data->destruct_hook == MUIV_List_DestructHook_String)
1938 ULONG *mem = ((ULONG*)msg->entry) - 1;
1939 FreePooled(msg->pool, mem, mem[0]);
1941 else
1943 CallHookPkt(data->destruct_hook, msg->pool, msg->entry);
1945 return 0;
1948 /**************************************************************************
1949 MUIM_List_Compare
1950 **************************************************************************/
1951 IPTR List__MUIM_Compare(struct IClass *cl, Object *obj, struct MUIP_List_Compare *msg)
1953 struct MUI_ListData *data = INST_DATA(cl, obj);
1955 return CallHookPkt(data->compare_hook, msg->entry2, msg->entry1);
1958 /**************************************************************************
1959 MUIM_List_Display
1960 **************************************************************************/
1961 IPTR List__MUIM_Display(struct IClass *cl, Object *obj, struct MUIP_List_Display *msg)
1963 struct MUI_ListData *data = INST_DATA(cl, obj);
1965 if (NULL == data->display_hook)
1967 if (msg->entry)
1968 *msg->array = msg->entry;
1969 else
1970 *msg->array = 0;
1971 return 1;
1974 *((ULONG*)(msg->array - 1)) = msg->entry_pos;
1975 return CallHookPkt(data->display_hook, msg->array, msg->entry);
1978 /**************************************************************************
1979 MUIM_List_SelectChange
1980 **************************************************************************/
1981 IPTR List__MUIM_SelectChange(struct IClass *cl, Object *obj, struct MUIP_List_SelectChange *msg)
1983 return 1;
1986 /**************************************************************************
1987 MUIM_List_CreateImage
1988 Called by a List subclass in its Setup method.
1989 Connects an Area subclass object to the list, much like an object gets
1990 connected to a window. List call Setup and AskMinMax on that object,
1991 keeps a reference to it (that reference will be returned).
1992 Text engine will dereference that pointer and draw the object with its
1993 default size.
1994 **************************************************************************/
1995 IPTR List__MUIM_CreateImage(struct IClass *cl, Object *obj, struct MUIP_List_CreateImage *msg)
1997 struct MUI_ListData *data = INST_DATA(cl, obj);
1998 struct ListImage *li;
2000 /* List must be already setup in Setup of your subclass */
2001 if (!(_flags(obj) & MADF_SETUP))
2002 return 0;
2003 li = AllocPooled(data->pool, sizeof(struct ListImage));
2004 if (!li)
2005 return 0;
2006 li->obj = msg->obj;
2008 AddTail((struct List *)&data->images, (struct Node *)li);
2009 DoMethod(li->obj, MUIM_ConnectParent, (IPTR)obj);
2010 DoSetupMethod(li->obj, muiRenderInfo(obj));
2013 return (IPTR)li;
2016 /**************************************************************************
2017 MUIM_List_DeleteImage
2018 **************************************************************************/
2019 IPTR List__MUIM_DeleteImage(struct IClass *cl, Object *obj, struct MUIP_List_DeleteImage *msg)
2021 struct MUI_ListData *data = INST_DATA(cl, obj);
2022 struct ListImage *li = (struct ListImage *)msg->listimg;
2024 if (li)
2026 DoMethod(li->obj, MUIM_Cleanup);
2027 DoMethod(li->obj, MUIM_DisconnectParent);
2028 Remove((struct Node *)li);
2029 FreePooled(data->pool, li, sizeof(struct ListImage));
2032 return 0;
2035 /**************************************************************************
2036 MUIM_List_Jump
2037 **************************************************************************/
2038 IPTR List__MUIM_Jump(struct IClass *cl, Object *obj, struct MUIP_List_Jump *msg)
2040 struct MUI_ListData *data = INST_DATA(cl, obj);
2041 LONG pos = msg->pos;
2043 switch(pos)
2045 case MUIV_List_Jump_Top:
2046 pos = 0;
2047 break;
2049 case MUIV_List_Jump_Active:
2050 pos = data->entries_active;
2051 break;
2053 case MUIV_List_Jump_Bottom:
2054 pos = data->entries_num - 1;
2055 break;
2057 case MUIV_List_Jump_Down:
2058 pos = data->entries_first + data->entries_visible;
2059 break;
2061 case MUIV_List_Jump_Up:
2062 pos = data->entries_first - 1;
2063 break;
2067 if (pos > data->entries_num)
2069 pos = data->entries_num - 1;
2071 if (pos < 0) pos = 0;
2073 if (pos < data->entries_first)
2075 set(obj, MUIA_List_First, pos);
2077 else if (pos >= data->entries_first + data->entries_visible)
2079 pos -= (data->entries_visible - 1);
2080 if (pos < 0) pos = 0;
2081 if (pos != data->entries_first)
2083 set(obj, MUIA_List_First, pos);
2088 return TRUE;
2091 /**************************************************************************
2092 MUIM_List_Sort
2093 **************************************************************************/
2094 IPTR List__MUIM_Sort(struct IClass *cl, Object *obj, struct MUIP_List_Sort *msg)
2096 struct MUI_ListData *data = INST_DATA(cl, obj);
2098 int i, j, max;
2099 struct MUIP_List_Compare cmpmsg = {MUIM_List_Compare, NULL, NULL, 0, 0};
2101 if (data->entries_num > 1)
2104 Simple sort algorithm. Feel free to improve it.
2106 for (i = 0; i < data->entries_num - 1; i++)
2108 max = i;
2109 for (j = i + 1; j < data->entries_num; j++)
2111 cmpmsg.entry1 = data->entries[max]->data;
2112 cmpmsg.entry2 = data->entries[j]->data;
2113 if ((LONG)DoMethodA(obj, (Msg)&cmpmsg) > 0)
2115 max = j;
2118 if (i != max)
2120 APTR tmp = data->entries[i];
2121 data->entries[i] = data->entries[max];
2122 data->entries[max] = tmp;
2127 if (!(data->flags & LIST_QUIET))
2129 data->update = 1;
2130 MUI_Redraw(obj,MADF_DRAWUPDATE);
2133 return 0;
2136 /**************************************************************************
2137 Dispatcher
2138 **************************************************************************/
2139 BOOPSI_DISPATCHER(IPTR, List_Dispatcher, cl, obj, msg)
2141 switch (msg->MethodID)
2143 case OM_NEW: return List__OM_NEW(cl, obj, (struct opSet *)msg);
2144 case OM_DISPOSE: return List__OM_DISPOSE(cl,obj, msg);
2145 case OM_SET: return List__OM_SET(cl,obj,(struct opSet *)msg);
2146 case OM_GET: return List__OM_GET(cl,obj,(struct opGet *)msg);
2148 case MUIM_Setup: return List__MUIM_Setup(cl,obj,(struct MUIP_Setup *)msg);
2149 case MUIM_Cleanup: return List__MUIM_Cleanup(cl,obj,(struct MUIP_Cleanup *)msg);
2150 case MUIM_AskMinMax: return List__MUIM_AskMinMax(cl,obj,(struct MUIP_AskMinMax *)msg);
2151 case MUIM_Show: return List__MUIM_Show(cl,obj,(struct MUIP_Show *)msg);
2152 case MUIM_Hide: return List__MUIM_Hide(cl,obj,(struct MUIP_Hide *)msg);
2153 case MUIM_Draw: return List__MUIM_Draw(cl,obj,(struct MUIP_Draw *)msg);
2154 case MUIM_Layout: return List__MUIM_Layout(cl,obj,(struct MUIP_Layout *)msg);
2155 case MUIM_HandleEvent: return List__MUIM_HandleEvent(cl,obj,(struct MUIP_HandleEvent *)msg);
2156 case MUIM_List_Clear: return List__MUIM_Clear(cl,obj,(struct MUIP_List_Clear *)msg);
2157 case MUIM_List_Sort: return List__MUIM_Sort(cl,obj,(struct MUIP_List_Sort *)msg);
2158 case MUIM_List_Exchange: return List__MUIM_Exchange(cl,obj,(struct MUIP_List_Exchange *)msg);
2159 case MUIM_List_Insert: return List__MUIM_Insert(cl,obj,(APTR)msg);
2160 case MUIM_List_InsertSingle: return List__MUIM_InsertSingle(cl,obj,(APTR)msg);
2161 case MUIM_List_GetEntry: return List__MUIM_GetEntry(cl,obj,(APTR)msg);
2162 case MUIM_List_Redraw: return List__MUIM_Redraw(cl,obj,(APTR)msg);
2163 case MUIM_List_Remove: return List__MUIM_Remove(cl,obj,(APTR)msg);
2165 case MUIM_List_Construct: return List__MUIM_Construct(cl,obj,(APTR)msg);
2166 case MUIM_List_Destruct: return List__MUIM_Destruct(cl,obj,(APTR)msg);
2167 case MUIM_List_Compare: return List__MUIM_Compare(cl,obj,(APTR)msg);
2168 case MUIM_List_Display: return List__MUIM_Display(cl,obj,(APTR)msg);
2169 case MUIM_List_SelectChange: return List__MUIM_SelectChange(cl,obj,(APTR)msg);
2170 case MUIM_List_CreateImage: return List__MUIM_CreateImage(cl,obj,(APTR)msg);
2171 case MUIM_List_DeleteImage: return List__MUIM_DeleteImage(cl,obj,(APTR)msg);
2172 case MUIM_List_Jump: return List__MUIM_Jump(cl,obj,(APTR)msg);
2175 return DoSuperMethodA(cl, obj, msg);
2177 BOOPSI_DISPATCHER_END
2180 * Class descriptor.
2182 const struct __MUIBuiltinClass _MUI_List_desc = {
2183 MUIC_List,
2184 MUIC_Area,
2185 sizeof(struct MUI_ListData),
2186 (void*)List_Dispatcher