muimaster.library: migrate handling of SelectChange and DoubleClick from Listview...
[AROS.git] / workbench / libs / muimaster / classes / list.c
blobb66c1141a10fb8e4a389d05b082e2f68f206b80a
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;
167 /***************************/
168 /* Former Listview members */
169 /***************************/
171 Object *vert;
172 IPTR scroller_pos;
173 BOOL read_only;
174 IPTR multiselect;
176 /* clicked column */
177 LONG click_column;
178 LONG def_click_column;
180 LONG mouse_click; /* see below if mouse is held down */
182 /* double click */
183 ULONG last_secs;
184 ULONG last_mics;
185 ULONG last_active;
187 struct MUI_EventHandlerNode ehn;
189 /* user prefs */
190 ListviewMulti prefs_multi;
192 BOOL select_change;
193 BOOL doubleclick;
198 #define MOUSE_CLICK_ENTRY 1 /* on entry clicked */
199 #define MOUSE_CLICK_TITLE 2 /* on title clicked */
201 #define LIST_ADJUSTWIDTH (1<<0)
202 #define LIST_ADJUSTHEIGHT (1<<1)
203 #define LIST_AUTOVISIBLE (1<<2)
204 #define LIST_DRAGSORTABLE (1<<3)
205 #define LIST_SHOWDROPMARKS (1<<4)
206 #define LIST_QUIET (1<<5)
209 /****** List.mui/MUIA_List_CompareHook ***************************************
211 * NAME
212 * MUIA_List_CompareHook -- (V4) [IS.], struct Hook *
214 * FUNCTION
215 * The provided hook indicates the sort ordering of two list entries.
216 * The hook receives list-entry data pointers as its second and third
217 * arguments. The hook should return a negative value if the first entry
218 * should be placed before the second entry, a positive value if the
219 * first entry should be placed after the second entry, and zero if the
220 * entries are equal.
222 * In addition to being used internally for sorting operations, this hook
223 * will be called when MUIM_List_Compare is externally invoked.
225 * If this attribute is not specified or is set to NULL, all list entries
226 * must be strings.
228 ******************************************************************************
232 /****** List.mui/MUIA_List_MultiTestHook *************************************
234 * NAME
235 * MUIA_List_MultiTestHook -- (V4) [IS.], struct Hook *
237 * FUNCTION
238 * The provided hook indicates whether a particular list entry
239 * may be multiselected. The hook receives the list-entry data pointer as
240 * its third argument, and returns a Boolean value. If this attribute is
241 * not specified or is set to NULL, all list entries are considered
242 * multi-selectable.
244 * Whenever an entry is about to be selected, this hook is called if
245 * there are other entries already selected. If the hook returns TRUE,
246 * the entry may be multi-selected; if the hook returns FALSE, the entry
247 * remains unselected.
249 * Additionally, if a non-multi-selectable entry has been selected (as
250 * the only selected entry in the list), any attempt to select an
251 * additional entry will fail.
253 ******************************************************************************
257 /**************************************************************************
258 Allocate a single list entry, does not initialize it (except the pointer)
259 **************************************************************************/
260 static struct ListEntry *AllocListEntry(struct MUI_ListData *data)
262 ULONG *mem;
263 struct ListEntry *le;
264 int size = sizeof(struct ListEntry) + sizeof(LONG) * data->columns + 4;
265 /* sizeinfo */
266 LONG j;
268 mem = AllocPooled(data->pool, size);
269 if (!mem)
270 return NULL;
271 D(bug("List AllocListEntry %p, %ld bytes\n", mem, size));
273 mem[0] = size; /* Save the size */
274 le = (struct ListEntry *)(mem + 1);
275 le->widths = (LONG *) (le + 1);
277 /* Initialize fields */
278 le->height = 0;
279 le->width = 0;
280 le->flags = 0;
281 for (j = 0; j < data->columns; j++)
282 le->widths[j] = 0;
284 return le;
287 /**************************************************************************
288 Deallocate a single list entry, does not deinitialize it
289 **************************************************************************/
290 static void FreeListEntry(struct MUI_ListData *data,
291 struct ListEntry *entry)
293 ULONG *mem = ((ULONG *) entry) - 1;
294 D(bug("FreeListEntry %p size=%ld\n", mem, mem[0]));
295 FreePooled(data->pool, mem, mem[0]);
298 /**************************************************************************
299 Ensures that there can be at least the given amount of entries within
300 the list. Returns 0 if not. It also allocates the space for the title.
301 It can be accessed with data->entries[ENTRY_TITLE]
302 **************************************************************************/
303 static int SetListSize(struct MUI_ListData *data, LONG size)
305 struct ListEntry **new_entries;
306 int new_entries_allocated;
308 if (size + 1 <= data->entries_allocated)
309 return 1;
311 new_entries_allocated = data->entries_allocated * 2 + 4;
312 if (new_entries_allocated < size + 1)
313 new_entries_allocated = size + 1 + 10; /* 10 is just random */
315 D(bug("List %p : SetListSize allocating %ld bytes\n", data,
316 new_entries_allocated * sizeof(struct ListEntry *)));
317 new_entries =
318 AllocVec(new_entries_allocated * sizeof(struct ListEntry *), 0);
319 if (NULL == new_entries)
320 return 0;
321 if (data->entries)
323 CopyMem(data->entries - 1, new_entries,
324 (data->entries_num + 1) * sizeof(struct ListEntry *));
325 FreeVec(data->entries - 1);
327 data->entries = new_entries + 1;
328 data->entries_allocated = new_entries_allocated;
329 return 1;
332 /**************************************************************************
333 Prepares the insertion of count entries at pos.
334 This function doesn't care if there is enough space in the datastructure.
335 SetListSize() must be used first.
336 With current implementation, this call will never fail
337 **************************************************************************/
338 static int PrepareInsertListEntries(struct MUI_ListData *data, int pos,
339 int count)
341 memmove(&data->entries[pos + count], &data->entries[pos],
342 (data->entries_num - pos) * sizeof(struct ListEntry *));
343 return 1;
346 /**************************************************************************
347 Removes count (already deinitalized) list entries starting az pos.
348 **************************************************************************/
349 static void RemoveListEntries(struct MUI_ListData *data, int pos, int count)
351 // FIXME: segfault if entries_num = pos = count = 1
352 memmove(&data->entries[pos], &data->entries[pos + count],
353 (data->entries_num - (pos + count)) * sizeof(struct ListEntry *));
356 /**************************************************************************
357 Frees all memory allocated by ParseListFormat()
358 **************************************************************************/
359 static void FreeListFormat(struct MUI_ListData *data)
361 int i;
363 if (data->ci)
365 for (i = 0; i < data->columns; i++)
367 FreeVec(data->ci[i].preparse);
368 data->ci[i].preparse = NULL;
370 FreeVec(data->ci);
371 data->ci = NULL;
373 FreeVec(data->preparses);
374 data->preparses = NULL;
375 if (data->strings)
377 FreeVec(data->strings - 1);
378 data->strings = NULL;
380 data->columns = 0;
383 /**************************************************************************
384 Parses the given format string (also frees a previously parsed format).
385 Return 0 on failure.
386 **************************************************************************/
387 static int ParseListFormat(struct MUI_ListData *data, STRPTR format)
389 int new_columns, i;
390 STRPTR ptr;
391 STRPTR format_sep;
392 char c;
394 IPTR args[ARG_CNT];
395 struct RDArgs *rdargs;
397 if (!format)
398 format = (STRPTR) "";
400 ptr = format;
402 FreeListFormat(data);
404 new_columns = 1;
406 /* Count the number of columns first */
407 while ((c = *ptr++))
408 if (c == ',')
409 new_columns++;
411 if (!(data->preparses =
412 AllocVec((new_columns + 10) * sizeof(STRPTR), 0)))
413 return 0;
415 if (!(data->strings = AllocVec((new_columns + 1 + 10)
416 * sizeof(STRPTR), 0))) /* hold enough space also for the entry pos,
417 * used by orginal MUI and also some
418 * security space */
419 return 0;
421 if (!(data->ci = AllocVec(new_columns * sizeof(struct ColumnInfo), 0)))
422 return 0;
424 // set defaults
425 for (i = 0; i < new_columns; i++)
427 data->ci[i].colno = -1; // -1 means: use unassigned column
428 data->ci[i].weight = 100;
429 data->ci[i].delta = 4;
430 data->ci[i].min_width = -1;
431 data->ci[i].max_width = -1;
432 data->ci[i].user_width = -1;
433 data->ci[i].bar = FALSE;
434 data->ci[i].preparse = NULL;
437 if ((format_sep = StrDup(format)) != 0)
439 for (i = 0; format_sep[i] != '\0'; i++)
441 if (format_sep[i] == ',')
442 format_sep[i] = '\0';
445 if ((rdargs = AllocDosObject(DOS_RDARGS, NULL)) != 0)
447 ptr = format_sep;
448 i = 0;
451 rdargs->RDA_Source.CS_Buffer = ptr;
452 rdargs->RDA_Source.CS_Length = strlen(ptr);
453 rdargs->RDA_Source.CS_CurChr = 0;
454 rdargs->RDA_DAList = 0;
455 rdargs->RDA_Buffer = NULL;
456 rdargs->RDA_BufSiz = 0;
457 rdargs->RDA_ExtHelp = NULL;
458 rdargs->RDA_Flags = 0;
460 memset(args, 0, sizeof args);
461 if (ReadArgs(FORMAT_TEMPLATE, args, rdargs))
463 if (args[ARG_COL])
464 data->ci[i].colno = *(LONG *) args[ARG_COL];
465 if (args[ARG_WEIGHT])
466 data->ci[i].weight = *(LONG *) args[ARG_WEIGHT];
467 if (args[ARG_DELTA])
468 data->ci[i].delta = *(LONG *) args[ARG_DELTA];
469 if (args[ARG_MINWIDTH])
470 data->ci[i].min_width =
471 *(LONG *) args[ARG_MINWIDTH];
472 if (args[ARG_MAXWIDTH])
473 data->ci[i].max_width =
474 *(LONG *) args[ARG_MAXWIDTH];
475 data->ci[i].bar = args[ARG_BAR];
476 if (args[ARG_PREPARSE])
477 data->ci[i].preparse =
478 StrDup((STRPTR) args[ARG_PREPARSE]);
480 FreeArgs(rdargs);
482 ptr += strlen(ptr) + 1;
483 i++;
485 while (i < new_columns);
486 FreeDosObject(DOS_RDARGS, rdargs);
488 FreeVec(format_sep);
491 for (i = 0; i < new_columns; i++)
493 D(bug("colno %d weight %d delta %d preparse %s\n",
494 data->ci[i].colno, data->ci[i].weight, data->ci[i].delta,
495 data->ci[i].preparse));
498 data->columns = new_columns;
499 data->strings++; /* Skip entry pos */
501 return 1;
504 /**************************************************************************
505 Call the MUIM_List_Display for the given entry. It fills out
506 data->string and data->preparses
507 **************************************************************************/
508 static void DisplayEntry(struct IClass *cl, Object *obj, int entry_pos)
510 struct MUI_ListData *data = INST_DATA(cl, obj);
511 APTR entry_data;
512 int col;
514 for (col = 0; col < data->columns; col++)
515 data->preparses[col] = data->ci[col].preparse;
517 if (entry_pos == ENTRY_TITLE)
519 if ((data->columns == 1) && (data->title != (STRPTR) 1))
521 *data->strings = data->title;
522 return;
524 entry_data = NULL; /* it's a title request */
526 else
527 entry_data = data->entries[entry_pos]->data;
529 /* Get the display formation */
530 DoMethod(obj, MUIM_List_Display, (IPTR) entry_data,
531 (IPTR) data->strings, entry_pos, (IPTR) data->preparses);
534 /**************************************************************************
535 Determine the dims of a single entry and adapt the columninfo according
536 to it. pos might be ENTRY_TITLE. Returns 0 if pos entry needs to
537 be redrawn after this operation, 1 if all entries need to be redrawn.
538 **************************************************************************/
539 static int CalcDimsOfEntry(struct IClass *cl, Object *obj, int pos)
541 struct MUI_ListData *data = INST_DATA(cl, obj);
542 struct ListEntry *entry = data->entries[pos];
543 int j;
544 int ret = 0;
546 if (!entry)
547 return ret;
549 if (!(_flags(obj) & MADF_SETUP))
550 return ret;
552 DisplayEntry(cl, obj, pos);
554 /* Set height to at least minheight */
555 if (data->entries[pos]->height < data->entry_minheight)
556 data->entries[pos]->height = data->entry_minheight;
558 for (j = 0; j < data->columns; j++)
560 ZText *text =
561 zune_text_new(data->preparses[j], data->strings[j],
562 ZTEXT_ARG_NONE, 0);
563 if (text != NULL)
565 zune_text_get_bounds(text, obj);
567 if (text->height > data->entries[pos]->height)
569 data->entries[pos]->height = text->height;
570 /* entry height changed, redraw all entries later */
571 ret = 1;
573 data->entries[pos]->widths[j] = text->width;
575 if (text->width > data->ci[j].entries_width)
577 /* This columns width is bigger than the other in the same
578 * columns, so we store this value
580 data->ci[j].entries_width = text->width;
581 /* column width changed, redraw all entries later */
582 ret = 1;
585 zune_text_destroy(text);
588 if (data->entries[pos]->height > data->entry_maxheight)
590 data->entry_maxheight = data->entries[pos]->height;
591 /* maximum entry height changed, redraw all entries later */
592 ret = 1;
595 return ret;
598 /**************************************************************************
599 Determine the widths of the entries
600 **************************************************************************/
601 static void CalcWidths(struct IClass *cl, Object *obj)
603 int i, j;
604 struct MUI_ListData *data = INST_DATA(cl, obj);
606 if (!(_flags(obj) & MADF_SETUP))
607 return;
609 for (j = 0; j < data->columns; j++)
610 data->ci[j].entries_width = 0;
612 data->entry_maxheight = 0;
613 data->entries_totalheight = 0;
614 data->entries_maxwidth = 0;
616 for (i = (data->title ? ENTRY_TITLE : 0); i < data->entries_num; i++)
618 CalcDimsOfEntry(cl, obj, i);
619 data->entries_totalheight += data->entries[i]->height;
622 for (j = 0; j < data->columns; j++)
623 data->entries_maxwidth += data->ci[j].entries_width;
625 if (!data->entry_maxheight)
626 data->entry_maxheight = 1;
629 /**************************************************************************
630 Calculates the number of visible entry lines. Returns 1 if it has
631 changed
632 **************************************************************************/
633 static int CalcVertVisible(struct IClass *cl, Object *obj)
635 struct MUI_ListData *data = INST_DATA(cl, obj);
636 int old_entries_visible = data->entries_visible;
637 int old_entries_top_pixel = data->entries_top_pixel;
639 data->entries_visible = (_mheight(data->area) - data->title_height)
640 / (data->entry_maxheight /* + data->prefs_linespacing */ );
642 /* Distribute extra vertical space evenly between top and bottom of
643 * list */
645 data->entries_top_pixel = _mtop(data->area) + data->title_height
646 + (_mheight(data->area) - data->title_height
648 data->entries_visible *
649 (data->entry_maxheight /* + data->prefs_linespacing */ )) / 2;
651 return (old_entries_visible != data->entries_visible)
652 || (old_entries_top_pixel != data->entries_top_pixel);
655 /**************************************************************************
656 Default hook to compare two list entries. Works for strings only.
657 **************************************************************************/
658 AROS_UFH3S(int, default_compare_func,
659 AROS_UFHA(struct Hook *, h, A0),
660 AROS_UFHA(char *, s2, A2),
661 AROS_UFHA(char *, s1, A1))
663 AROS_USERFUNC_INIT
665 return Stricmp(s1, s2);
667 AROS_USERFUNC_EXIT
670 /**************************************************************************
671 OM_NEW
672 **************************************************************************/
673 IPTR List__OM_NEW(struct IClass *cl, Object *obj, struct opSet *msg)
675 struct MUI_ListData *data;
676 struct TagItem *tag;
677 struct TagItem *tags;
678 APTR *array = NULL;
679 LONG new_entries_active = MUIV_List_Active_Off;
680 struct TagItem rectattrs[2] = {{TAG_IGNORE, TAG_IGNORE }, {TAG_DONE, TAG_DONE}};
681 Object *vert, *area;
683 /* search for MUIA_Frame as it has to be passed to rectangle object */
684 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
686 if (tag->ti_Tag == MUIA_Frame)
688 rectattrs[0].ti_Tag = MUIA_Frame;
689 rectattrs[0].ti_Data = tag->ti_Data;
690 tag->ti_Tag = TAG_IGNORE;
691 break;
695 obj = (Object *) DoSuperNewTags(cl, obj, NULL,
696 MUIA_Group_Horiz, TRUE,
697 MUIA_InnerLeft, 0,
698 MUIA_InnerRight, 0,
699 MUIA_Group_Spacing, 0,
700 MUIA_Font, MUIV_Font_List,
701 MUIA_ShowSelState, FALSE,
702 MUIA_InputMode, MUIV_InputMode_RelVerify,
703 MUIA_Background, MUII_ListBack,
704 TAG_MORE, (IPTR) msg->ops_AttrList,
705 TAG_DONE);
707 if (!obj)
708 return FALSE;
710 data = INST_DATA(cl, obj);
712 data->columns = 1;
713 data->entries_active = MUIV_List_Active_Off;
714 data->intern_puddle_size = 2008;
715 data->intern_thresh_size = 1024;
716 data->default_compare_hook.h_Entry = (HOOKFUNC) default_compare_func;
717 data->default_compare_hook.h_SubEntry = 0;
718 data->compare_hook = &(data->default_compare_hook);
719 data->flags = LIST_SHOWDROPMARKS;
720 data->area_replaced = FALSE;
722 data->last_active = -1;
724 data->ehn.ehn_Events = IDCMP_MOUSEBUTTONS | IDCMP_RAWKEY;
725 data->ehn.ehn_Priority = 0;
726 data->ehn.ehn_Flags = 0;
727 data->ehn.ehn_Object = obj;
728 data->ehn.ehn_Class = cl;
730 area = (Object *)GetTagData(MUIA_List_ListArea, (IPTR) 0, msg->ops_AttrList);
732 if (!area)
733 area = RectangleObject, TAG_MORE, (IPTR) rectattrs, End;
734 else
735 data->area_replaced = TRUE;
736 data->area = area;
738 vert = ScrollbarObject, MUIA_Group_Horiz, FALSE, End;
739 data->vert = vert;
741 /* parse initial taglist */
742 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
744 switch (tag->ti_Tag)
746 case MUIA_List_Active:
747 new_entries_active = tag->ti_Data;
748 break;
750 case MUIA_List_Pool:
751 data->pool = (APTR) tag->ti_Data;
752 break;
754 case MUIA_List_PoolPuddleSize:
755 data->intern_puddle_size = tag->ti_Data;
756 break;
758 case MUIA_List_PoolThreshSize:
759 data->intern_thresh_size = tag->ti_Data;
760 break;
762 case MUIA_List_CompareHook:
763 data->compare_hook = (struct Hook *)tag->ti_Data;
764 if (data->compare_hook == NULL)
765 data->compare_hook = &data->default_compare_hook;
766 break;
768 case MUIA_List_ConstructHook:
769 data->construct_hook = (struct Hook *)tag->ti_Data;
770 break;
772 case MUIA_List_DestructHook:
773 data->destruct_hook = (struct Hook *)tag->ti_Data;
774 break;
776 case MUIA_List_DisplayHook:
777 data->display_hook = (struct Hook *)tag->ti_Data;
778 break;
780 case MUIA_List_MultiTestHook:
781 data->multi_test_hook = (struct Hook *)tag->ti_Data;
782 break;
784 case MUIA_List_SourceArray:
785 array = (APTR *) tag->ti_Data;
786 break;
788 case MUIA_List_Format:
789 data->format = StrDup((STRPTR) tag->ti_Data);
790 break;
792 case MUIA_List_Title:
793 data->title = (STRPTR) tag->ti_Data;
794 break;
796 case MUIA_List_MinLineHeight:
797 data->entry_minheight = tag->ti_Data;
798 break;
800 case MUIA_List_AdjustHeight:
801 _handle_bool_tag(data->flags, tag->ti_Data, LIST_ADJUSTHEIGHT);
802 break;
804 case MUIA_List_AdjustWidth:
805 _handle_bool_tag(data->flags, tag->ti_Data, LIST_ADJUSTWIDTH);
806 break;
808 case MUIA_List_AutoVisible:
809 _handle_bool_tag(data->flags, tag->ti_Data, LIST_AUTOVISIBLE);
810 break;
812 case MUIA_List_ShowDropMarks:
813 _handle_bool_tag(data->flags, tag->ti_Data, LIST_SHOWDROPMARKS);
814 break;
816 case MUIA_List_DragSortable:
817 _handle_bool_tag(data->flags, tag->ti_Data, LIST_DRAGSORTABLE);
818 set(obj, MUIA_Draggable, tag->ti_Data);
819 break;
821 case MUIA_Listview_ScrollerPos:
822 data->scroller_pos = tag->ti_Data;
823 break;
825 case MUIA_Listview_Input:
826 data->read_only = !tag->ti_Data;
827 break;
829 case MUIA_Listview_MultiSelect:
830 data->multiselect = tag->ti_Data;
831 break;
833 case MUIA_Listview_DoubleClick:
834 data->doubleclick = tag->ti_Data != 0;
835 break;
839 /* Add list and/or scroller */
840 switch (data->scroller_pos)
842 case MUIV_Listview_ScrollerPos_None:
843 DoMethod(obj, OM_ADDMEMBER, area);
844 break;
845 case MUIV_Listview_ScrollerPos_Left:
846 DoMethod(obj, OM_ADDMEMBER, vert);
847 DoMethod(obj, OM_ADDMEMBER, area);
848 break;
849 default:
850 DoMethod(obj, OM_ADDMEMBER, area);
851 DoMethod(obj, OM_ADDMEMBER, vert);
852 break;
855 if (vert)
857 LONG entries = 0, first = 0, visible = 0;
859 get(obj, MUIA_List_VertProp_First, &first);
860 get(obj, MUIA_List_VertProp_Visible, &visible);
861 get(obj, MUIA_List_VertProp_Entries, &entries);
863 SetAttrs(data->vert,
864 MUIA_Prop_First, first,
865 MUIA_Prop_Visible, visible, MUIA_Prop_Entries, entries, TAG_DONE);
867 /* Pass prop object as DestObj (based on code in NList) */
868 DoMethod(obj, MUIM_Notify, MUIA_List_VertProp_First, MUIV_EveryTime,
869 (IPTR) vert, 3, MUIM_NoNotifySet, MUIA_Prop_First, MUIV_TriggerValue);
870 DoMethod(obj, MUIM_Notify, MUIA_List_VertProp_Visible, MUIV_EveryTime,
871 (IPTR) vert, 3, MUIM_NoNotifySet, MUIA_Prop_Visible, MUIV_TriggerValue);
872 DoMethod(obj, MUIM_Notify, MUIA_List_VertProp_Entries, MUIV_EveryTime,
873 (IPTR) vert, 3, MUIM_NoNotifySet, MUIA_Prop_Entries, MUIV_TriggerValue);
876 if (!data->pool)
878 /* No memory pool given, so we create our own */
879 data->pool = data->intern_pool =
880 CreatePool(0, data->intern_puddle_size,
881 data->intern_thresh_size);
882 if (!data->pool)
884 CoerceMethod(cl, obj, OM_DISPOSE);
885 return 0;
889 /* parse the list format */
890 if (!(ParseListFormat(data, data->format)))
892 CoerceMethod(cl, obj, OM_DISPOSE);
893 return 0;
896 /* This is neccessary for at least the title */
897 if (!SetListSize(data, 0))
899 CoerceMethod(cl, obj, OM_DISPOSE);
900 return 0;
903 if (data->title)
905 if (!(data->entries[ENTRY_TITLE] = AllocListEntry(data)))
907 CoerceMethod(cl, obj, OM_DISPOSE);
908 return 0;
911 else
912 data->entries[ENTRY_TITLE] = NULL;
914 if (array)
916 int i;
917 /* Count the number of elements */
918 for (i = 0; array[i] != NULL; i++)
920 /* Insert them */
921 DoMethod(obj, MUIM_List_Insert, (IPTR) array, i,
922 MUIV_List_Insert_Top);
925 if ((data->entries_num) && (new_entries_active != MUIV_List_Active_Off))
927 switch (new_entries_active)
929 case MUIV_List_Active_Top:
930 new_entries_active = 0;
931 break;
933 case MUIV_List_Active_Bottom:
934 new_entries_active = data->entries_num - 1;
935 break;
938 if (new_entries_active < 0)
939 new_entries_active = 0;
940 else if (new_entries_active >= data->entries_num)
941 new_entries_active = data->entries_num - 1;
943 data->entries_active = new_entries_active;
944 /* Selected entry will be moved into visible area */
947 NewList((struct List *)&data->images);
949 D(bug("List_New(%lx)\n", obj));
951 return (IPTR) obj;
954 /**************************************************************************
955 OM_DISPOSE
956 **************************************************************************/
957 IPTR List__OM_DISPOSE(struct IClass *cl, Object *obj, Msg msg)
959 struct MUI_ListData *data = INST_DATA(cl, obj);
961 D(bug("List Dispose\n"));
963 /* Call destruct method for every entry and free the entries manually
964 * to avoid notification */
965 while (data->confirm_entries_num)
967 struct ListEntry *lentry =
968 data->entries[--data->confirm_entries_num];
969 DoMethod(obj, MUIM_List_Destruct, (IPTR) lentry->data,
970 (IPTR) data->pool);
971 FreeListEntry(data, lentry);
974 if (data->intern_pool)
975 DeletePool(data->intern_pool);
976 if (data->entries)
977 FreeVec(data->entries - 1);
978 /* title is currently before all other elements */
980 FreeListFormat(data);
981 FreeVec(data->format);
983 return DoSuperMethodA(cl, obj, msg);
987 /**************************************************************************
988 OM_SET
989 **************************************************************************/
990 IPTR List__OM_SET(struct IClass *cl, Object *obj, struct opSet *msg)
992 struct MUI_ListData *data = INST_DATA(cl, obj);
993 struct TagItem *tag;
994 struct TagItem *tags;
996 /* parse taglist */
997 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
999 switch (tag->ti_Tag)
1001 case MUIA_List_CompareHook:
1002 data->compare_hook = (struct Hook *)tag->ti_Data;
1003 if (data->compare_hook == NULL)
1004 data->compare_hook = &data->default_compare_hook;
1005 break;
1007 case MUIA_List_ConstructHook:
1008 data->construct_hook = (struct Hook *)tag->ti_Data;
1009 break;
1011 case MUIA_List_DestructHook:
1012 data->destruct_hook = (struct Hook *)tag->ti_Data;
1013 break;
1015 case MUIA_List_DisplayHook:
1016 data->display_hook = (struct Hook *)tag->ti_Data;
1017 break;
1019 case MUIA_List_MultiTestHook:
1020 data->multi_test_hook = (struct Hook *)tag->ti_Data;
1021 if (data->multi_test_hook != NULL)
1023 /* Clearing current selections is the easiest way to keep
1024 * selections consistent with the new hook */
1025 DoMethod(obj, MUIM_List_Select, MUIV_List_Select_All,
1026 MUIV_List_Select_Off, NULL);
1028 break;
1030 case MUIA_List_Title:
1031 data->title = (STRPTR) tag->ti_Data;
1032 DoMethod(obj, MUIM_List_Redraw, MUIV_List_Redraw_All);
1033 break;
1035 case MUIA_List_VertProp_First:
1036 data->vertprop_first = tag->ti_Data;
1037 if (data->entries_first != tag->ti_Data)
1039 set(obj, MUIA_List_First, tag->ti_Data);
1041 break;
1043 case MUIA_List_Format:
1044 data->format = StrDup((STRPTR) tag->ti_Data);
1045 ParseListFormat(data, data->format);
1046 // FIXME: should we check for errors?
1047 DoMethod(obj, MUIM_List_Redraw, MUIV_List_Redraw_All);
1048 break;
1050 case MUIA_List_VertProp_Entries:
1051 data->vertprop_entries = tag->ti_Data;
1052 break;
1054 case MUIA_List_VertProp_Visible:
1055 data->vertprop_visible = tag->ti_Data;
1056 data->entries_visible = tag->ti_Data;
1057 break;
1059 case MUIA_List_Active:
1061 LONG new_entries_active = tag->ti_Data;
1063 if ((data->entries_num)
1064 && (new_entries_active != MUIV_List_Active_Off))
1066 switch (new_entries_active)
1068 case MUIV_List_Active_Top:
1069 new_entries_active = 0;
1070 break;
1072 case MUIV_List_Active_Bottom:
1073 new_entries_active = data->entries_num - 1;
1074 break;
1076 case MUIV_List_Active_Up:
1077 new_entries_active = data->entries_active - 1;
1078 break;
1080 case MUIV_List_Active_Down:
1081 new_entries_active = data->entries_active + 1;
1082 break;
1084 case MUIV_List_Active_PageUp:
1085 new_entries_active =
1086 data->entries_active - data->entries_visible;
1087 break;
1089 case MUIV_List_Active_PageDown:
1090 new_entries_active =
1091 data->entries_active + data->entries_visible;
1092 break;
1095 if (new_entries_active < 0)
1096 new_entries_active = 0;
1097 else if (new_entries_active >= data->entries_num)
1098 new_entries_active = data->entries_num - 1;
1100 else
1101 new_entries_active = -1;
1103 if (data->entries_active != new_entries_active)
1105 LONG old = data->entries_active;
1106 data->entries_active = new_entries_active;
1108 /* Selectchange stuff */
1109 if (new_entries_active != -1)
1111 DoMethod(obj, MUIM_List_SelectChange,
1112 new_entries_active, MUIV_List_Select_On, 0);
1113 DoMethod(obj, MUIM_List_SelectChange,
1114 new_entries_active, MUIV_List_Select_Active, 0);
1116 else
1117 DoMethod(obj, MUIM_List_SelectChange,
1118 MUIV_List_Active_Off, MUIV_List_Select_Off, 0);
1120 data->update = 2;
1121 data->update_pos = old;
1122 MUI_Redraw(obj, MADF_DRAWUPDATE);
1123 data->update = 2;
1124 data->update_pos = data->entries_active;
1125 MUI_Redraw(obj, MADF_DRAWUPDATE);
1127 /* Make new active entry visible (if there is one and
1128 list is visible) */
1129 if (new_entries_active != -1
1130 && (_flags(obj) & MADF_SETUP))
1132 DoMethod(obj, MUIM_List_Jump,
1133 MUIV_List_Jump_Active);
1137 break;
1139 case MUIA_List_First:
1140 data->update_pos = data->entries_first;
1141 data->update = 3;
1142 data->entries_first = tag->ti_Data;
1144 MUI_Redraw(obj, MADF_DRAWUPDATE);
1145 if ((data->vertprop_first != tag->ti_Data)
1146 && (!(data->flags & LIST_QUIET)))
1148 set(obj, MUIA_List_VertProp_First, tag->ti_Data);
1150 break;
1152 case MUIA_List_Visible: /* Shouldn't be settable? */
1153 if (data->vertprop_visible != tag->ti_Data)
1154 set(obj, MUIA_List_VertProp_Visible, tag->ti_Data);
1155 break;
1157 case MUIA_List_Entries:
1158 if (data->confirm_entries_num == tag->ti_Data)
1160 data->entries_num = tag->ti_Data;
1161 if (!(data->flags & LIST_QUIET))
1163 set(obj, MUIA_List_VertProp_Entries, data->entries_num);
1166 else
1168 D(bug("Bug: confirm_entries != MUIA_List_Entries!\n"));
1170 break;
1172 case MUIA_List_Quiet:
1173 _handle_bool_tag(data->flags, tag->ti_Data, LIST_QUIET);
1174 if (!tag->ti_Data)
1176 DoMethod(obj, MUIM_List_Redraw, MUIV_List_Redraw_All);
1177 if (data->entries_num != XGET(obj, MUIA_List_VertProp_Entries))
1178 set(obj, MUIA_List_VertProp_Entries, data->entries_num);
1179 if (data->vertprop_first !=
1180 XGET(obj, MUIA_List_VertProp_First))
1181 set(obj, MUIA_List_VertProp_First, data->vertprop_first);
1183 break;
1185 case MUIA_List_AutoVisible:
1186 _handle_bool_tag(data->flags, tag->ti_Data, LIST_AUTOVISIBLE);
1187 break;
1189 case MUIA_List_ShowDropMarks:
1190 _handle_bool_tag(data->flags, tag->ti_Data, LIST_SHOWDROPMARKS);
1191 break;
1193 case MUIA_List_DragSortable:
1194 _handle_bool_tag(data->flags, tag->ti_Data, LIST_DRAGSORTABLE);
1195 set(obj, MUIA_Draggable, tag->ti_Data);
1196 break;
1198 case MUIA_Selected:
1199 /* Swallow this so the Area class doesn't redraw us */
1200 tag->ti_Tag = TAG_IGNORE;
1201 break;
1203 case MUIA_Disabled:
1204 if (_flags(obj) & MADF_SETUP)
1206 /* Stop listening for events we only listen to when mouse
1207 button is down: we will not be informed of the button
1208 being released */
1209 DoMethod(_win(obj), MUIM_Window_RemEventHandler, (IPTR) &data->ehn);
1210 data->ehn.ehn_Events &= ~(IDCMP_MOUSEMOVE | IDCMP_INTUITICKS | IDCMP_INACTIVEWINDOW);
1211 DoMethod(_win(obj), MUIM_Window_AddEventHandler, (IPTR) &data->ehn);
1213 break;
1215 case MUIA_Listview_DoubleClick: /* private set */
1216 data->doubleclick = tag->ti_Data != 0;
1217 break;
1219 case MUIA_Listview_SelectChange: /* private set */
1220 data->select_change = tag->ti_Data != 0;
1221 break;
1225 return DoSuperMethodA(cl, obj, (Msg) msg);
1228 /**************************************************************************
1229 OM_GET
1230 **************************************************************************/
1231 IPTR List__OM_GET(struct IClass *cl, Object *obj, struct opGet *msg)
1233 /* small macro to simplify return value storage */
1234 #define STORE *(msg->opg_Storage)
1235 struct MUI_ListData *data = INST_DATA(cl, obj);
1237 switch (msg->opg_AttrID)
1239 case MUIA_List_Entries:
1240 STORE = data->entries_num;
1241 return 1;
1242 case MUIA_List_First:
1243 STORE = data->entries_first;
1244 return 1;
1245 case MUIA_List_Active:
1246 STORE = data->entries_active;
1247 return 1;
1248 case MUIA_List_InsertPosition:
1249 STORE = data->insert_position;
1250 return 1;
1251 case MUIA_List_Title:
1252 STORE = (IPTR) data->title;
1253 return 1;
1254 case MUIA_List_VertProp_Entries:
1255 STORE = data->vertprop_entries;
1256 return 1;
1257 case MUIA_List_VertProp_Visible:
1258 case MUIA_List_Visible:
1259 STORE = data->vertprop_visible;
1260 return 1;
1261 case MUIA_List_VertProp_First:
1262 STORE = data->vertprop_first;
1263 return 1;
1264 case MUIA_List_Format:
1265 STORE = (IPTR) data->format;
1266 return 1;
1267 case MUIA_List_AutoVisible:
1268 STORE = data->flags & LIST_AUTOVISIBLE;
1269 return 1;
1270 case MUIA_List_ShowDropMarks:
1271 STORE = data->flags & LIST_SHOWDROPMARKS;
1272 return 1;
1273 case MUIA_List_DragSortable:
1274 STORE = data->flags & LIST_DRAGSORTABLE;
1275 return 1;
1276 case MUIA_Listview_ClickColumn:
1277 STORE = data->click_column;
1278 return 1;
1279 case MUIA_Listview_DoubleClick:
1280 STORE = data->doubleclick;
1281 return 1;
1282 case MUIA_Listview_SelectChange:
1283 STORE = data->select_change;
1284 return 1;
1287 if (DoSuperMethodA(cl, obj, (Msg) msg))
1288 return 1;
1289 return 0;
1290 #undef STORE
1293 /**************************************************************************
1294 MUIM_Setup
1295 **************************************************************************/
1296 IPTR List__MUIM_Setup(struct IClass *cl, Object *obj,
1297 struct MUIP_Setup *msg)
1299 struct MUI_ListData *data = INST_DATA(cl, obj);
1301 if (!DoSuperMethodA(cl, obj, (Msg) msg))
1302 return 0;
1304 data->prefs_refresh = muiGlobalInfo(obj)->mgi_Prefs->list_refresh;
1305 data->prefs_linespacing =
1306 muiGlobalInfo(obj)->mgi_Prefs->list_linespacing;
1307 data->prefs_smoothed = muiGlobalInfo(obj)->mgi_Prefs->list_smoothed;
1308 data->prefs_smoothval = muiGlobalInfo(obj)->mgi_Prefs->list_smoothval;
1310 CalcWidths(cl, obj);
1312 data->list_cursor =
1313 zune_imspec_setup(MUII_ListCursor, muiRenderInfo(obj));
1314 data->list_select =
1315 zune_imspec_setup(MUII_ListSelect, muiRenderInfo(obj));
1316 data->list_selcur =
1317 zune_imspec_setup(MUII_ListSelCur, muiRenderInfo(obj));
1319 data->prefs_multi = muiGlobalInfo(obj)->mgi_Prefs->list_multi;
1320 if (data->multiselect == MUIV_Listview_MultiSelect_Default)
1322 if (data->prefs_multi == LISTVIEW_MULTI_SHIFTED)
1323 data->multiselect = MUIV_Listview_MultiSelect_Shifted;
1324 else
1325 data->multiselect = MUIV_Listview_MultiSelect_Always;
1328 DoMethod(_win(obj), MUIM_Window_AddEventHandler, (IPTR) &data->ehn);
1330 return 1;
1333 /**************************************************************************
1334 MUIM_Cleanup
1335 **************************************************************************/
1336 IPTR List__MUIM_Cleanup(struct IClass *cl, Object *obj,
1337 struct MUIP_Cleanup *msg)
1339 struct MUI_ListData *data = INST_DATA(cl, obj);
1340 struct ListImage *li = List_First(&data->images);
1342 while (li)
1344 struct ListImage *next = Node_Next(li);
1345 DoMethod(obj, MUIM_List_DeleteImage, (IPTR) li);
1346 li = next;
1349 zune_imspec_cleanup(data->list_cursor);
1350 zune_imspec_cleanup(data->list_select);
1351 zune_imspec_cleanup(data->list_selcur);
1353 DoMethod(_win(obj), MUIM_Window_RemEventHandler, (IPTR) &data->ehn);
1354 data->mouse_click = 0;
1356 return DoSuperMethodA(cl, obj, (Msg) msg);
1359 /**************************************************************************
1360 MUIM_AskMinMax
1361 **************************************************************************/
1362 IPTR List__MUIM_AskMinMax(struct IClass *cl, Object *obj,
1363 struct MUIP_AskMinMax *msg)
1365 struct MUI_ListData *data = INST_DATA(cl, obj);
1367 DoSuperMethodA(cl, obj, (Msg) msg);
1370 if ((data->flags & LIST_ADJUSTWIDTH) && (data->entries_num > 0))
1372 msg->MinMaxInfo->MinWidth += data->entries_maxwidth;
1373 msg->MinMaxInfo->DefWidth += data->entries_maxwidth;
1374 msg->MinMaxInfo->MaxWidth += data->entries_maxwidth;
1376 else
1378 msg->MinMaxInfo->MinWidth += 40;
1379 msg->MinMaxInfo->DefWidth += 100;
1380 msg->MinMaxInfo->MaxWidth = MUI_MAXMAX;
1383 if (data->entries_num > 0)
1385 if (data->flags & LIST_ADJUSTHEIGHT)
1387 msg->MinMaxInfo->MinHeight += data->entries_totalheight;
1388 msg->MinMaxInfo->DefHeight += data->entries_totalheight;
1389 msg->MinMaxInfo->MaxHeight += data->entries_totalheight;
1391 else
1393 ULONG h = data->entry_maxheight + data->prefs_linespacing;
1394 msg->MinMaxInfo->MinHeight += 2 * h + data->prefs_linespacing;
1395 msg->MinMaxInfo->DefHeight += 8 * h + data->prefs_linespacing;
1396 msg->MinMaxInfo->MaxHeight = MUI_MAXMAX;
1399 else
1401 msg->MinMaxInfo->MinHeight += 36;
1402 msg->MinMaxInfo->DefHeight += 96;
1403 msg->MinMaxInfo->MaxHeight = MUI_MAXMAX;
1405 D(bug("List %p minheigh=%d, line maxh=%d\n",
1406 obj, msg->MinMaxInfo->MinHeight, data->entry_maxheight));
1407 return TRUE;
1410 /****i* List.mui/MUIM_Layout *************************************************
1412 * NAME
1413 * MUIM_Layout
1415 ******************************************************************************
1419 IPTR List__MUIM_Layout(struct IClass *cl, Object *obj,
1420 struct MUIP_Layout *msg)
1422 struct MUI_ListData *data = INST_DATA(cl, obj);
1423 ULONG rc = DoSuperMethodA(cl, obj, (Msg) msg);
1424 LONG new_entries_first = data->entries_first;
1426 /* Calc the numbers of entries visible */
1427 CalcVertVisible(cl, obj);
1429 /* Ensure active entry is visible if requested */
1430 if (data->entries_active + 1 >=
1431 (data->entries_first + data->entries_visible)
1432 && (data->flags & LIST_AUTOVISIBLE) != 0)
1433 new_entries_first =
1434 data->entries_active - data->entries_visible + 1;
1436 /* Ensure there are no unnecessary empty lines */
1437 if ((new_entries_first + data->entries_visible >=
1438 data->entries_num)
1439 && (data->entries_visible <= data->entries_num))
1440 new_entries_first = data->entries_num - data->entries_visible;
1442 /* Always show the start of the list if it isn't long enough to fill the
1443 view */
1444 if (data->entries_num <= data->entries_visible)
1445 new_entries_first = 0;
1447 if (new_entries_first < 0)
1448 new_entries_first = 0;
1450 set(obj, new_entries_first != data->entries_first ?
1451 MUIA_List_First : TAG_IGNORE, new_entries_first);
1453 /* So the notify happens */
1454 set(obj, MUIA_List_VertProp_Visible, data->entries_visible);
1456 return rc;
1460 /**************************************************************************
1461 MUIM_Show
1462 **************************************************************************/
1463 IPTR List__MUIM_Show(struct IClass *cl, Object *obj,
1464 struct MUIP_Show *msg)
1466 struct MUI_ListData *data = INST_DATA(cl, obj);
1467 ULONG rc = DoSuperMethodA(cl, obj, (Msg) msg);
1469 zune_imspec_show(data->list_cursor, obj);
1470 zune_imspec_show(data->list_select, obj);
1471 zune_imspec_show(data->list_selcur, obj);
1472 return rc;
1476 /**************************************************************************
1477 MUIM_Hide
1478 **************************************************************************/
1479 IPTR List__MUIM_Hide(struct IClass *cl, Object *obj,
1480 struct MUIP_Hide *msg)
1482 struct MUI_ListData *data = INST_DATA(cl, obj);
1484 zune_imspec_hide(data->list_cursor);
1485 zune_imspec_hide(data->list_select);
1486 zune_imspec_hide(data->list_selcur);
1488 return DoSuperMethodA(cl, obj, (Msg) msg);
1492 /**************************************************************************
1493 Draw an entry at entry_pos at the given row. To draw the title, set pos to
1494 ENTRY_TITLE
1495 **************************************************************************/
1496 static VOID List_DrawEntry(struct IClass *cl, Object *obj, int entry_pos,
1497 int y)
1499 struct MUI_ListData *data = INST_DATA(cl, obj);
1500 int col, x1, x2;
1502 /* To be sure we don't draw anything if there is no title */
1503 if (entry_pos == ENTRY_TITLE && !data->title)
1504 return;
1506 DisplayEntry(cl, obj, entry_pos);
1507 x1 = _mleft(data->area);
1509 for (col = 0; col < data->columns; col++)
1511 ZText *text;
1512 x2 = x1 + data->ci[col].entries_width;
1514 if ((text =
1515 zune_text_new(data->preparses[col], data->strings[col],
1516 ZTEXT_ARG_NONE, 0)))
1518 /* Could be made simpler, as we don't really need the bounds */
1519 zune_text_get_bounds(text, obj);
1520 /* Note, this was MPEN_SHADOW before */
1521 SetAPen(_rp(obj), muiRenderInfo(obj)->mri_Pens[MPEN_TEXT]);
1522 zune_text_draw(text, obj, x1, x2, y); /* totally wrong! */
1523 zune_text_destroy(text);
1525 x1 = x2 + data->ci[col].delta + (data->ci[col].bar ? BAR_WIDTH : 0);
1529 /**************************************************************************
1530 MUIM_Draw
1531 **************************************************************************/
1532 IPTR List__MUIM_Draw(struct IClass *cl, Object *obj, struct MUIP_Draw *msg)
1534 struct MUI_ListData *data = INST_DATA(cl, obj);
1535 int entry_pos, y;
1536 APTR clip;
1537 int start, end;
1538 BOOL scroll_caused_damage = FALSE;
1539 struct MUI_ImageSpec_intern *highlight;
1540 IPTR ret = (IPTR)0;
1542 if (data->flags & LIST_QUIET)
1543 return 0;
1545 ret = DoSuperMethodA(cl, obj, (Msg) msg);
1547 if (data->area_replaced)
1548 return ret;
1550 /* Calculate the title height */
1551 if (data->title)
1553 data->title_height = data->entries[ENTRY_TITLE]->height + 2;
1555 else
1557 data->title_height = 0;
1560 /* Calc the numbers of entries visible */
1561 CalcVertVisible(cl, obj);
1563 if ((msg->flags & MADF_DRAWUPDATE) == 0 || data->update == 1)
1565 DoMethod(obj, MUIM_DrawBackground, _mleft(data->area), _mtop(data->area),
1566 _mwidth(data->area), _mheight(data->area),
1567 0, data->entries_first * data->entry_maxheight, 0);
1570 clip = MUI_AddClipping(muiRenderInfo(obj), _mleft(data->area), _mtop(data->area),
1571 _mwidth(data->area), _mheight(data->area));
1573 if ((msg->flags & MADF_DRAWUPDATE) == 0 || data->update == 1)
1575 y = _mtop(data->area);
1576 /* Draw Title
1578 if (data->title_height && data->title)
1580 List_DrawEntry(cl, obj, ENTRY_TITLE, y);
1581 y += data->entries[ENTRY_TITLE]->height;
1582 SetAPen(_rp(obj), _pens(obj)[MPEN_SHADOW]);
1583 Move(_rp(obj), _mleft(data->area), y);
1584 Draw(_rp(obj), _mright(data->area), y);
1585 SetAPen(_rp(obj), _pens(obj)[MPEN_SHINE]);
1586 y++;
1587 Move(_rp(obj), _mleft(data->area), y);
1588 Draw(_rp(obj), _mright(data->area), y);
1592 y = data->entries_top_pixel;
1594 start = data->entries_first;
1595 end = data->entries_first + data->entries_visible;
1597 if ((msg->flags & MADF_DRAWUPDATE) && data->update == 3)
1599 int diffy = data->entries_first - data->update_pos;
1600 int top, bottom;
1601 if (abs(diffy) < data->entries_visible)
1603 scroll_caused_damage =
1604 (_rp(obj)->Layer->Flags & LAYERREFRESH) ? FALSE : TRUE;
1606 ScrollRaster(_rp(obj), 0, diffy * data->entry_maxheight,
1607 _mleft(data->area), y,
1608 _mright(data->area),
1609 y + data->entry_maxheight * data->entries_visible);
1611 scroll_caused_damage =
1612 scroll_caused_damage
1613 && (_rp(obj)->Layer->Flags & LAYERREFRESH);
1615 if (diffy > 0)
1617 start = end - diffy;
1618 y += data->entry_maxheight * (data->entries_visible -
1619 diffy);
1621 else
1622 end = start - diffy;
1625 top = y;
1626 bottom = y + (end - start) * data->entry_maxheight;
1628 DoMethod(obj, MUIM_DrawBackground, _mleft(data->area), top,
1629 _mwidth(data->area), bottom - top + 1,
1631 top - _mtop(data->area) + data->entries_first * data->entry_maxheight,
1635 for (entry_pos = start;
1636 entry_pos < end && entry_pos < data->entries_num; entry_pos++)
1638 struct ListEntry *entry = data->entries[entry_pos];
1640 if (!(msg->flags & MADF_DRAWUPDATE) ||
1641 ((msg->flags & MADF_DRAWUPDATE) && data->update == 1) ||
1642 ((msg->flags & MADF_DRAWUPDATE) && data->update == 3) ||
1643 ((msg->flags & MADF_DRAWUPDATE) && data->update == 2
1644 && data->update_pos == entry_pos))
1646 /* Choose appropriate highlight image */
1648 if (entry_pos == data->entries_active
1649 && entry->flags & ENTRY_SELECTED)
1650 highlight = data->list_selcur;
1651 else if (entry_pos == data->entries_active)
1652 highlight = data->list_cursor;
1653 else if (entry->flags & ENTRY_SELECTED)
1654 highlight = data->list_select;
1655 else
1656 highlight = NULL;
1658 /* Draw highlight or background */
1660 if (highlight != NULL)
1662 zune_imspec_draw(highlight, muiRenderInfo(obj),
1663 _mleft(data->area), y, _mwidth(data->area), data->entry_maxheight,
1664 0, y - data->entries_top_pixel, 0);
1666 else if ((msg->flags & MADF_DRAWUPDATE) && data->update == 2
1667 && data->update_pos == entry_pos)
1669 DoMethod(obj, MUIM_DrawBackground, _mleft(data->area), y,
1670 _mwidth(data->area), data->entry_maxheight, 0,
1671 y - _mtop(data->area) +
1672 data->entries_first * data->entry_maxheight, 0);
1675 List_DrawEntry(cl, obj, entry_pos, y);
1677 y += data->entry_maxheight;
1680 MUI_RemoveClipping(muiRenderInfo(obj), clip);
1682 data->update = 0;
1684 if (scroll_caused_damage)
1686 if (MUI_BeginRefresh(muiRenderInfo(obj), 0))
1688 /* Theoretically it might happen that more damage is caused
1689 after ScrollRaster. By something else, like window movement
1690 in front of our window. Therefore refresh root object of
1691 window, not just this object */
1693 Object *o = NULL;
1695 get(_win(obj), MUIA_Window_RootObject, &o);
1696 MUI_Redraw(o, MADF_DRAWOBJECT);
1698 MUI_EndRefresh(muiRenderInfo(obj), 0);
1702 ULONG x1 = _mleft(data->area);
1703 ULONG col;
1704 y = _mtop(data->area);
1706 if (data->title_height && data->title)
1708 for (col = 0; col < data->columns; col++)
1710 ULONG halfdelta = data->ci[col].delta / 2;
1711 x1 += data->ci[col].entries_width + halfdelta;
1713 if (x1 + (data->ci[col].bar ? BAR_WIDTH : 0) > _mright(data->area))
1714 break;
1716 if (data->ci[col].bar)
1718 SetAPen(_rp(obj), _pens(obj)[MPEN_SHINE]);
1719 Move(_rp(obj), x1, y);
1720 Draw(_rp(obj), x1,
1721 y + data->entries[ENTRY_TITLE]->height - 1);
1722 SetAPen(_rp(obj), _pens(obj)[MPEN_SHADOW]);
1723 Move(_rp(obj), x1 + 1, y);
1724 Draw(_rp(obj), x1 + 1,
1725 y + data->entries[ENTRY_TITLE]->height - 1);
1727 x1 += BAR_WIDTH;
1729 x1 += data->ci[col].delta - halfdelta;
1731 y += data->entries[ENTRY_TITLE]->height + 1;
1734 x1 = _mleft(data->area);
1736 for (col = 0; col < data->columns; col++)
1738 ULONG halfdelta = data->ci[col].delta / 2;
1739 x1 += data->ci[col].entries_width + halfdelta;
1741 if (x1 + (data->ci[col].bar ? BAR_WIDTH : 0) > _mright(data->area))
1742 break;
1744 if (data->ci[col].bar)
1746 SetAPen(_rp(obj), _pens(obj)[MPEN_SHINE]);
1747 Move(_rp(obj), x1, y);
1748 Draw(_rp(obj), x1, _mbottom(data->area));
1749 SetAPen(_rp(obj), _pens(obj)[MPEN_SHADOW]);
1750 Move(_rp(obj), x1 + 1, y);
1751 Draw(_rp(obj), x1 + 1, _mbottom(data->area));
1753 x1 += BAR_WIDTH;
1756 x1 += data->ci[col].delta - halfdelta;
1759 return 0;
1762 /****** List.mui/MUIM_List_Clear *********************************************
1764 * NAME
1765 * MUIM_List_Clear (V4)
1767 * SYNOPSIS
1768 * DoMethod(obj, MUIM_List_Clear);
1770 * FUNCTION
1771 * Removes all entries from the list.
1773 ******************************************************************************
1777 IPTR List__MUIM_Clear(struct IClass *cl, Object *obj,
1778 struct MUIP_List_Clear *msg)
1780 struct MUI_ListData *data = INST_DATA(cl, obj);
1782 while (data->confirm_entries_num)
1784 struct ListEntry *lentry =
1785 data->entries[--data->confirm_entries_num];
1786 DoMethod(obj, MUIM_List_Destruct, (IPTR) lentry->data,
1787 (IPTR) data->pool);
1788 FreeListEntry(data, lentry);
1790 /* Should never fail when shrinking */
1791 SetListSize(data, 0);
1794 if (data->confirm_entries_num != data->entries_num)
1796 SetAttrs(obj, MUIA_List_Entries, 0, MUIA_List_First, 0,
1797 /* Notify only when no entry was active */
1798 data->entries_active !=
1799 MUIV_List_Active_Off ? MUIA_List_Active : TAG_DONE,
1800 MUIV_List_Active_Off, TAG_DONE);
1802 data->update = 1;
1803 MUI_Redraw(obj, MADF_DRAWUPDATE);
1806 return 0;
1809 /**************************************************************************
1810 MUIM_List_Exchange
1811 **************************************************************************/
1812 IPTR List__MUIM_Exchange(struct IClass *cl, Object *obj,
1813 struct MUIP_List_Exchange *msg)
1815 struct MUI_ListData *data = INST_DATA(cl, obj);
1816 LONG pos1, pos2;
1818 switch (msg->pos1)
1820 case MUIV_List_Exchange_Top:
1821 pos1 = 0;
1822 break;
1823 case MUIV_List_Exchange_Active:
1824 pos1 = data->entries_active;
1825 break;
1826 case MUIV_List_Exchange_Bottom:
1827 pos1 = data->entries_num - 1;
1828 break;
1829 default:
1830 pos1 = msg->pos1;
1833 switch (msg->pos2)
1835 case MUIV_List_Exchange_Top:
1836 pos2 = 0;
1837 break;
1838 case MUIV_List_Exchange_Active:
1839 pos2 = data->entries_active;
1840 break;
1841 case MUIV_List_Exchange_Bottom:
1842 pos2 = data->entries_num - 1;
1843 break;
1844 case MUIV_List_Exchange_Next:
1845 pos2 = pos1 + 1;
1846 break;
1847 case MUIV_List_Exchange_Previous:
1848 pos2 = pos1 - 1;
1849 break;
1850 default:
1851 pos2 = msg->pos2;
1854 if (pos1 >= 0 && pos1 < data->entries_num && pos2 >= 0
1855 && pos2 < data->entries_num && pos1 != pos2)
1857 struct ListEntry *save = data->entries[pos1];
1858 data->entries[pos1] = data->entries[pos2];
1859 data->entries[pos2] = save;
1861 data->update = 2;
1862 data->update_pos = pos1;
1863 MUI_Redraw(obj, MADF_DRAWUPDATE);
1865 data->update = 2;
1866 data->update_pos = pos2;
1867 MUI_Redraw(obj, MADF_DRAWUPDATE);
1869 return TRUE;
1871 else
1873 return FALSE;
1877 /**************************************************************************
1878 MUIM_List_Redraw
1879 **************************************************************************/
1880 IPTR List__MUIM_Redraw(struct IClass *cl, Object *obj,
1881 struct MUIP_List_Redraw *msg)
1883 struct MUI_ListData *data = INST_DATA(cl, obj);
1885 if (!(data->flags & LIST_QUIET))
1887 if (msg->pos == MUIV_List_Redraw_All)
1889 data->update = 1;
1890 CalcWidths(cl, obj);
1891 MUI_Redraw(obj, MADF_DRAWUPDATE);
1893 else
1895 LONG pos = -1;
1896 if (msg->pos == MUIV_List_Redraw_Active)
1897 pos = data->entries_active;
1898 else if (msg->pos == MUIV_List_Redraw_Entry)
1900 LONG i;
1901 for (i = 0; i < data->entries_num; i++)
1902 if (data->entries[i]->data == msg->entry)
1904 pos = i;
1905 break;
1908 else
1909 pos = msg->pos;
1911 if (pos != -1)
1913 if (CalcDimsOfEntry(cl, obj, pos))
1914 data->update = 1;
1915 else
1917 data->update = 2;
1918 data->update_pos = pos;
1920 MUI_Redraw(obj, MADF_DRAWUPDATE);
1924 return 0;
1927 /**************************************************************************
1928 MUIM_List_Remove
1929 **************************************************************************/
1930 IPTR List__MUIM_Remove(struct IClass *cl, Object *obj,
1931 struct MUIP_List_Remove *msg)
1933 struct MUI_ListData *data = INST_DATA(cl, obj);
1934 LONG pos, cur;
1935 LONG new_act;
1936 struct ListEntry *lentry;
1937 //int rem_count = 1;
1939 if (!data->entries_num)
1940 return 0;
1942 switch (msg->pos)
1944 case MUIV_List_Remove_First:
1945 pos = 0;
1946 break;
1948 case MUIV_List_Remove_Active:
1949 pos = data->entries_active;
1950 break;
1952 case MUIV_List_Remove_Last:
1953 pos = data->entries_num - 1;
1954 break;
1956 case MUIV_List_Remove_Selected:
1957 /* TODO: needs special handling */
1958 pos = data->entries_active;
1959 break;
1961 default:
1962 pos = msg->pos;
1963 break;
1966 if (pos < 0 || pos >= data->entries_num)
1967 return 0;
1969 new_act = data->entries_active;
1971 if (pos == new_act && new_act == data->entries_num - 1)
1972 new_act--; /* might become MUIV_List_Active_Off */
1974 lentry = data->entries[pos];
1975 DoMethod(obj, MUIM_List_Destruct, (IPTR) lentry->data,
1976 (IPTR) data->pool);
1978 cur = pos + 1;
1980 RemoveListEntries(data, pos, cur - pos);
1981 data->confirm_entries_num -= cur - pos;
1983 /* ensure that the active element is in a valid range */
1984 if (new_act >= data->entries_num)
1985 new_act = data->entries_num - 1;
1987 SetAttrs(obj, MUIA_List_Entries, data->confirm_entries_num,
1988 (new_act >= pos) || (new_act != data->entries_active) ?
1989 MUIA_List_Active : TAG_DONE,
1990 new_act, /* Inform only if neccessary (for notify) */
1991 TAG_DONE);
1993 data->update = 1;
1994 MUI_Redraw(obj, MADF_DRAWUPDATE);
1996 return 0;
1999 /**************************************************************************
2000 MUIM_List_Select
2001 **************************************************************************/
2002 IPTR List__MUIM_Select(struct IClass *cl, Object *obj,
2003 struct MUIP_List_Select *msg)
2005 struct MUI_ListData *data = INST_DATA(cl, obj);
2006 LONG pos, i, count, selcount=0, state=0;
2007 BOOL multi_allowed = TRUE, new_select_state = FALSE;
2009 /* Establish the range of entries affected */
2010 switch (msg->pos)
2012 case MUIV_List_Select_Active:
2013 pos = data->entries_active;
2014 if (pos == MUIV_List_Active_Off)
2015 count = 0;
2016 else
2017 count = 1;
2018 break;
2020 case MUIV_List_Select_All:
2021 pos = 0;
2022 count = data->entries_num;
2023 break;
2025 default:
2026 pos = msg->pos;
2027 count = 1;
2028 if (pos < 0 || pos >= data->entries_num)
2029 return 0;
2030 break;
2033 if (msg->seltype != MUIV_List_Select_Ask && data->multi_test_hook != NULL)
2035 /* Disallow selection of an additional entry if there is a currently
2036 selected entry that is not multi-selectable (in such case there
2037 will only be one entry currently selected, so no need to iterate) */
2038 i = MUIV_List_NextSelected_Start;
2039 DoMethod(obj, MUIM_List_NextSelected, (IPTR) &i);
2040 if (i != MUIV_List_NextSelected_End)
2041 selcount++;
2042 if (data->multi_test_hook != NULL && selcount != 0)
2043 multi_allowed = CallHookPkt(data->multi_test_hook, NULL,
2044 data->entries[i]->data);
2047 /* Change or check state of each entry in the range */
2048 for (i = pos; i < pos + count; i++)
2050 state = data->entries[i]->flags & ENTRY_SELECTED;
2051 switch (msg->seltype)
2053 case MUIV_List_Select_Off:
2054 new_select_state = FALSE;
2055 break;
2057 case MUIV_List_Select_On:
2058 new_select_state = TRUE;
2059 break;
2061 case MUIV_List_Select_Toggle:
2062 new_select_state = !state;
2063 break;
2065 default:
2066 if (data->entries[i]->flags & ENTRY_SELECTED)
2067 selcount++;
2068 break;
2071 if (msg->seltype != MUIV_List_Select_Ask)
2073 /* Disallow selection if entry is not multi-selectable and
2074 * there are already selected entries */
2075 if (data->multi_test_hook != NULL && new_select_state)
2076 new_select_state = multi_allowed && (selcount == 0 ||
2077 CallHookPkt(data->multi_test_hook, NULL,
2078 data->entries[i]->data));
2080 if (new_select_state)
2081 data->entries[i]->flags |= ENTRY_SELECTED;
2082 else
2083 data->entries[i]->flags &= ~ENTRY_SELECTED;
2087 /* Report old state or number of selected entries */
2088 if (msg->info)
2090 if (msg->pos == MUIV_List_Select_All
2091 && msg->seltype == MUIV_List_Select_Ask)
2092 *msg->info = selcount;
2093 else
2094 *msg->info = state;
2097 /* Redraw unless it was just an enquiry */
2098 if (msg->seltype != MUIV_List_Select_Ask)
2100 if (count > 1)
2101 data->update = 1;
2102 else
2104 data->update = 2;
2105 data->update_pos = pos;
2107 MUI_Redraw(obj, MADF_DRAWUPDATE);
2110 return 0;
2113 /**************************************************************************
2114 MUIM_List_Insert
2115 **************************************************************************/
2117 IPTR List__MUIM_Insert(struct IClass *cl, Object *obj,
2118 struct MUIP_List_Insert *msg)
2120 struct MUI_ListData *data = INST_DATA(cl, obj);
2121 LONG pos, count, sort;
2123 count = msg->count;
2124 sort = 0;
2126 if (count == -1)
2128 /* Count the number of entries */
2129 for (count = 0; msg->entries[count] != NULL; count++)
2133 if (count <= 0)
2134 return ~0;
2136 switch (msg->pos)
2138 case MUIV_List_Insert_Top:
2139 pos = 0;
2140 break;
2142 case MUIV_List_Insert_Active:
2143 if (data->entries_active != -1)
2144 pos = data->entries_active;
2145 else
2146 pos = 0;
2147 break;
2149 case MUIV_List_Insert_Sorted:
2150 pos = data->entries_num;
2151 sort = 1; /* we sort'em later */
2152 break;
2154 case MUIV_List_Insert_Bottom:
2155 pos = data->entries_num;
2156 break;
2158 default:
2159 if (msg->pos > data->entries_num)
2160 pos = data->entries_num;
2161 else if (msg->pos < 0)
2162 pos = 0;
2163 else
2164 pos = msg->pos;
2165 break;
2168 if (!(SetListSize(data, data->entries_num + count)))
2169 return ~0;
2171 LONG until = pos + count;
2172 APTR *toinsert = msg->entries;
2174 if (!(PrepareInsertListEntries(data, pos, count)))
2175 return ~0;
2177 while (pos < until)
2179 struct ListEntry *lentry;
2181 if (!(lentry = AllocListEntry(data)))
2183 /* Panic, but we must be in a consistent state, so remove
2184 * the space where the following list entries should have gone
2186 RemoveListEntries(data, pos, until - pos);
2187 return ~0;
2190 /* now call the construct method which returns us a pointer which
2191 we need to store */
2192 lentry->data = (APTR) DoMethod(obj, MUIM_List_Construct,
2193 (IPTR) * toinsert, (IPTR) data->pool);
2194 if (!lentry->data)
2196 FreeListEntry(data, lentry);
2197 RemoveListEntries(data, pos, until - pos);
2199 /* TODO: Also check for visible stuff like below */
2200 if (data->entries_num != data->confirm_entries_num)
2201 set(obj, MUIA_List_Entries, data->confirm_entries_num);
2202 return ~0;
2205 data->entries[pos] = lentry;
2206 data->confirm_entries_num++;
2208 if (_flags(obj) & MADF_SETUP)
2210 /* We have to calculate the width and height of the newly
2211 * inserted entry. This has to be done after inserting the
2212 * element into the list */
2213 CalcDimsOfEntry(cl, obj, pos);
2216 toinsert++;
2217 pos++;
2220 /* Recalculate the number of visible entries */
2221 if (_flags(obj) & MADF_SETUP)
2222 CalcVertVisible(cl, obj);
2224 if (data->entries_num != data->confirm_entries_num)
2226 SetAttrs(obj,
2227 MUIA_List_Entries, data->confirm_entries_num,
2228 MUIA_List_Visible, data->entries_visible, TAG_DONE);
2231 /* If the array is already sorted, we could do a simple insert
2232 * sort and would be much faster than with qsort.
2233 * If an array is not yet sorted, does a MUIV_List_Insert_Sorted
2234 * sort the whole array?
2236 * I think, we better sort the whole array:
2238 if (sort)
2240 DoMethod(obj, MUIM_List_Sort);
2241 /* TODO: which pos to return here !? */
2242 /* MUIM_List_Sort already called MUI_Redraw */
2244 else
2246 data->update = 1;
2247 MUI_Redraw(obj, MADF_DRAWUPDATE);
2249 data->insert_position = pos;
2251 return (ULONG) pos;
2254 /**************************************************************************
2255 MUIM_List_InsertSingle
2256 **************************************************************************/
2257 IPTR List__MUIM_InsertSingle(struct IClass *cl, Object *obj,
2258 struct MUIP_List_InsertSingle *msg)
2260 return DoMethod(obj, MUIM_List_Insert, (IPTR) & msg->entry, 1,
2261 msg->pos);
2264 /**************************************************************************
2265 MUIM_List_GetEntry
2266 **************************************************************************/
2267 IPTR List__MUIM_GetEntry(struct IClass *cl, Object *obj,
2268 struct MUIP_List_GetEntry *msg)
2270 struct MUI_ListData *data = INST_DATA(cl, obj);
2271 int pos = msg->pos;
2273 if (pos == MUIV_List_GetEntry_Active)
2274 pos = data->entries_active;
2276 if (pos < 0 || pos >= data->entries_num)
2278 *msg->entry = NULL;
2279 return 0;
2281 *msg->entry = data->entries[pos]->data;
2282 return (IPTR) *msg->entry;
2285 /**************************************************************************
2286 MUIM_List_Construct
2287 **************************************************************************/
2288 IPTR List__MUIM_Construct(struct IClass *cl, Object *obj,
2289 struct MUIP_List_Construct *msg)
2291 struct MUI_ListData *data = INST_DATA(cl, obj);
2293 if (NULL == data->construct_hook)
2294 return (IPTR) msg->entry;
2295 if ((IPTR) data->construct_hook == MUIV_List_ConstructHook_String)
2297 int len = msg->entry ? strlen((STRPTR) msg->entry) : 0;
2298 ULONG *mem = AllocPooled(msg->pool, len + 5);
2300 if (NULL == mem)
2301 return 0;
2302 mem[0] = len + 5;
2303 if (msg->entry != NULL)
2304 strcpy((STRPTR) (mem + 1), (STRPTR) msg->entry);
2305 else
2306 *(STRPTR) (mem + 1) = 0;
2307 return (IPTR) (mem + 1);
2309 return CallHookPkt(data->construct_hook, msg->pool, msg->entry);
2312 /**************************************************************************
2313 MUIM_List_Destruct
2314 **************************************************************************/
2315 IPTR List__MUIM_Destruct(struct IClass *cl, Object *obj,
2316 struct MUIP_List_Destruct *msg)
2318 struct MUI_ListData *data = INST_DATA(cl, obj);
2320 if (NULL == data->destruct_hook)
2321 return 0;
2323 if ((IPTR) data->destruct_hook == MUIV_List_DestructHook_String)
2325 ULONG *mem = ((ULONG *) msg->entry) - 1;
2326 FreePooled(msg->pool, mem, mem[0]);
2328 else
2330 CallHookPkt(data->destruct_hook, msg->pool, msg->entry);
2332 return 0;
2335 /****** List.mui/MUIM_List_Compare *******************************************
2337 * NAME
2338 * MUIM_List_Compare (V20)
2340 * SYNOPSIS
2341 * DoMethod(obj, MUIM_List_Compare, APTR entry1, APTR entry2,
2342 * LONG sort_type1, LONG sort_type2);
2344 * FUNCTION
2345 * Compare two list entries according to the current comparison hook
2346 * (MUIA_List_CompareHook).
2348 * INPUTS
2349 * entry1 - the first entry data.
2350 * entry2 - the second entry data.
2351 * sort_type1 - undocumented.
2352 * sort_type2 - undocumented.
2354 * SEE ALSO
2355 * MUIA_List_CompareHook, MUIM_List_Sort.
2357 ******************************************************************************
2361 IPTR List__MUIM_Compare(struct IClass *cl, Object *obj,
2362 struct MUIP_List_Compare *msg)
2364 struct MUI_ListData *data = INST_DATA(cl, obj);
2366 return CallHookPkt(data->compare_hook, msg->entry2, msg->entry1);
2369 /**************************************************************************
2370 MUIM_List_Display
2371 **************************************************************************/
2372 IPTR List__MUIM_Display(struct IClass *cl, Object *obj,
2373 struct MUIP_List_Display *msg)
2375 struct MUI_ListData *data = INST_DATA(cl, obj);
2377 if (NULL == data->display_hook)
2379 if (msg->entry)
2380 *msg->array = msg->entry;
2381 else
2382 *msg->array = 0;
2383 return 1;
2386 *((ULONG *) (msg->array - 1)) = msg->entry_pos;
2387 return CallHookPkt(data->display_hook, msg->array, msg->entry);
2390 /**************************************************************************
2391 MUIM_List_SelectChange
2392 **************************************************************************/
2393 IPTR List__MUIM_SelectChange(struct IClass *cl, Object *obj,
2394 struct MUIP_List_SelectChange *msg)
2396 return 1;
2399 /**************************************************************************
2400 MUIM_List_CreateImage
2401 Called by a List subclass in its Setup method.
2402 Connects an Area subclass object to the list, much like an object gets
2403 connected to a window. List calls Setup and AskMinMax on that object,
2404 keeps a reference to it (that reference will be returned).
2405 Text engine will dereference that pointer and draw the object with its
2406 default size.
2407 **************************************************************************/
2408 IPTR List__MUIM_CreateImage(struct IClass *cl, Object *obj,
2409 struct MUIP_List_CreateImage *msg)
2411 struct MUI_ListData *data = INST_DATA(cl, obj);
2412 struct ListImage *li;
2414 if (!msg->obj)
2415 return 0;
2417 /* List must be already setup in Setup of your subclass */
2418 if (!(_flags(obj) & MADF_SETUP))
2419 return 0;
2420 li = AllocPooled(data->pool, sizeof(struct ListImage));
2421 if (!li)
2422 return 0;
2423 li->obj = msg->obj;
2425 AddTail((struct List *)&data->images, (struct Node *)li);
2426 DoMethod(li->obj, MUIM_ConnectParent, (IPTR) obj);
2427 DoSetupMethod(li->obj, muiRenderInfo(obj));
2430 return (IPTR) li;
2433 /**************************************************************************
2434 MUIM_List_DeleteImage
2435 **************************************************************************/
2436 IPTR List__MUIM_DeleteImage(struct IClass *cl, Object *obj,
2437 struct MUIP_List_DeleteImage *msg)
2439 struct MUI_ListData *data = INST_DATA(cl, obj);
2440 struct ListImage *li = (struct ListImage *)msg->listimg;
2442 if (li)
2444 DoMethod(li->obj, MUIM_Cleanup);
2445 DoMethod(li->obj, MUIM_DisconnectParent);
2446 Remove((struct Node *)li);
2447 FreePooled(data->pool, li, sizeof(struct ListImage));
2450 return 0;
2453 /****** List.mui/MUIM_List_Jump **********************************************
2455 * NAME
2456 * MUIM_List_Jump (V4)
2458 * SYNOPSIS
2459 * DoMethod(obj, MUIM_List_Jump, LONG pos);
2461 * FUNCTION
2462 * Scrolls the list so that a particular entry is visible.
2464 * INPUTS
2465 * pos - index of entry that should become visible, or one of these
2466 * special values:
2467 * MUIV_List_Jump_Active: show the active entry.
2468 * MUIV_List_Jump_Top: show the first entry.
2469 * MUIV_List_Jump_Bottom: show the last entry.
2470 * MUIV_List_Jump_Up: show the previous hidden entry.
2471 * MUIV_List_Jump_Down: show the next hidden entry.
2473 ******************************************************************************
2477 IPTR List__MUIM_Jump(struct IClass *cl, Object *obj,
2478 struct MUIP_List_Jump *msg)
2480 struct MUI_ListData *data = INST_DATA(cl, obj);
2481 LONG pos = msg->pos;
2483 switch (pos)
2485 case MUIV_List_Jump_Top:
2486 pos = 0;
2487 break;
2489 case MUIV_List_Jump_Active:
2490 pos = data->entries_active;
2491 break;
2493 case MUIV_List_Jump_Bottom:
2494 pos = data->entries_num - 1;
2495 break;
2497 case MUIV_List_Jump_Down:
2498 pos = data->entries_first + data->entries_visible;
2499 break;
2501 case MUIV_List_Jump_Up:
2502 pos = data->entries_first - 1;
2503 break;
2506 if (pos >= data->entries_num)
2508 pos = data->entries_num - 1;
2510 if (pos < 0)
2511 pos = 0;
2513 if (pos < data->entries_first)
2515 set(obj, MUIA_List_First, pos);
2517 else if (pos >= data->entries_first + data->entries_visible)
2519 pos -= (data->entries_visible - 1);
2520 if (pos < 0)
2521 pos = 0;
2522 if (pos != data->entries_first)
2524 set(obj, MUIA_List_First, pos);
2528 return TRUE;
2531 /****** List.mui/MUIM_List_Sort **********************************************
2533 * NAME
2534 * MUIM_List_Sort (V4)
2536 * SYNOPSIS
2537 * DoMethod(obj, MUIM_List_Sort);
2539 * FUNCTION
2540 * Sort the list's entries according to the current comparison hook
2541 * (MUIA_List_CompareHook).
2543 * SEE ALSO
2544 * MUIA_List_CompareHook, MUIM_List_Compare.
2546 ******************************************************************************
2550 IPTR List__MUIM_Sort(struct IClass *cl, Object *obj,
2551 struct MUIP_List_Sort *msg)
2553 struct MUI_ListData *data = INST_DATA(cl, obj);
2555 int i, j, max;
2556 struct MUIP_List_Compare cmpmsg =
2557 { MUIM_List_Compare, NULL, NULL, 0, 0 };
2559 if (data->entries_num > 1)
2562 Simple sort algorithm. Feel free to improve it.
2564 for (i = 0; i < data->entries_num - 1; i++)
2566 max = i;
2567 for (j = i + 1; j < data->entries_num; j++)
2569 cmpmsg.entry1 = data->entries[max]->data;
2570 cmpmsg.entry2 = data->entries[j]->data;
2571 if ((LONG) DoMethodA(obj, (Msg) & cmpmsg) > 0)
2573 max = j;
2576 if (i != max)
2578 APTR tmp = data->entries[i];
2579 data->entries[i] = data->entries[max];
2580 data->entries[max] = tmp;
2585 data->update = 1;
2586 MUI_Redraw(obj, MADF_DRAWUPDATE);
2588 return 0;
2591 /****** List.mui/MUIM_List_Move **********************************************
2593 * NAME
2594 * MUIM_List_Move (V9)
2596 * SYNOPSIS
2597 * DoMethod(obj, MUIM_List_Move, LONG from, LONG to);
2599 * FUNCTION
2600 * Move a list entry to a new position.
2602 * INPUTS
2603 * from - the current index of the entry that should be moved, or one of
2604 * these special values:
2605 * MUIV_List_Move_Active: the active entry.
2606 * MUIV_List_Move_Top: the first entry.
2607 * MUIV_List_Move_Bottom: the last entry.
2608 * to - the index of the entry's new position, or one of
2609 * these special values:
2610 * MUIV_List_Move_Active: the active entry.
2611 * MUIV_List_Move_Top: the first entry.
2612 * MUIV_List_Move_Bottom: the last entry.
2614 ******************************************************************************
2618 IPTR List__MUIM_Move(struct IClass *cl, Object *obj,
2619 struct MUIP_List_Move *msg)
2621 struct MUI_ListData *data = INST_DATA(cl, obj);
2623 LONG from, to;
2624 int i;
2626 /* Normalise special 'from' values */
2627 switch (msg->from)
2629 case MUIV_List_Move_Top:
2630 from = 0;
2631 break;
2632 case MUIV_List_Move_Active:
2633 from = data->entries_active;
2634 break;
2635 case MUIV_List_Move_Bottom:
2636 from = data->entries_num - 1;
2637 break;
2638 default:
2639 from = msg->from;
2642 /* Normalise special 'to' values */
2643 switch (msg->to)
2645 case MUIV_List_Move_Top:
2646 to = 0;
2647 break;
2648 case MUIV_List_Move_Active:
2649 to = data->entries_active;
2650 break;
2651 case MUIV_List_Move_Bottom:
2652 to = data->entries_num - 1;
2653 break;
2654 case MUIV_List_Move_Next:
2655 to = from + 1;
2656 break;
2657 case MUIV_List_Move_Previous:
2658 to = from - 1;
2659 break;
2660 default:
2661 to = msg->to;
2664 /* Check that values are within valid bounds */
2665 if (from > data->entries_num - 1 || from < 0
2666 || to > data->entries_num - 1 || to < 0 || from == to)
2667 return (IPTR) FALSE;
2669 /* Shift all entries in the range between the 'from' and 'to' positions */
2670 if (from < to)
2672 struct ListEntry *backup = data->entries[from];
2673 for (i = from; i < to; i++)
2674 data->entries[i] = data->entries[i + 1];
2675 data->entries[to] = backup;
2677 else
2679 struct ListEntry *backup = data->entries[from];
2680 for (i = from; i > to; i--)
2681 data->entries[i] = data->entries[i - 1];
2682 data->entries[to] = backup;
2685 /* Update index of active entry */
2686 if (from == data->entries_active)
2687 data->entries_active = to;
2688 else if (data->entries_active > from && data->entries_active < to)
2689 data->entries_active--;
2690 else if (data->entries_active < from && data->entries_active >= to)
2691 data->entries_active++;
2693 /* Reflect list changes visually */
2694 data->update = 1;
2695 MUI_Redraw(obj, MADF_DRAWUPDATE);
2697 return TRUE;
2700 /**************************************************************************
2701 MUIM_List_NextSelected
2702 **************************************************************************/
2703 IPTR List__MUIM_NextSelected(struct IClass *cl, Object *obj,
2704 struct MUIP_List_NextSelected *msg)
2706 struct MUI_ListData *data = INST_DATA(cl, obj);
2707 LONG pos, i;
2708 BOOL found = FALSE;
2710 /* Get the first entry to check */
2711 pos = *msg->pos;
2712 if (pos == MUIV_List_NextSelected_Start)
2713 pos = 0;
2714 else
2715 pos++;
2717 /* Find the next selected entry */
2718 for (i = pos; i < data->entries_num && !found; i++)
2720 if (data->entries[i]->flags & ENTRY_SELECTED)
2722 pos = i;
2723 found = TRUE;
2727 /* Return index of selected entry, or indicate there are no more */
2728 if (!found)
2729 pos = MUIV_List_NextSelected_End;
2730 *msg->pos = pos;
2732 return TRUE;
2735 /**************************************************************************
2736 MUIM_List_TestPos
2737 **************************************************************************/
2738 IPTR List__MUIM_TestPos(struct IClass *cl, Object *obj,
2739 struct MUIP_List_TestPos *msg)
2741 struct MUI_ListData *data = INST_DATA(cl, obj);
2742 struct MUI_List_TestPos_Result *result = msg->res;
2743 LONG col = -1, row = -1;
2744 UWORD flags = 0, i;
2745 LONG mx = msg->x - _left(data->area);
2746 LONG entries_visible;
2748 if (data->entries_visible <= data->entries_num)
2749 entries_visible = data->entries_visible;
2750 else
2751 entries_visible = data->entries_num;
2752 LONG ey = msg->y - data->entries_top_pixel;
2753 /* y coordinates transformed to the entries */
2755 /* Now check if it was clicked on a title or on entries */
2756 if (ey < 0)
2757 flags |= MUI_LPR_ABOVE;
2758 else if (ey >= entries_visible * data->entry_maxheight)
2759 flags |= MUI_LPR_BELOW;
2760 else
2762 /* Identify row */
2763 row = ey / data->entry_maxheight + data->entries_first;
2764 result->yoffset =
2765 ey % data->entry_maxheight - data->entry_maxheight / 2;
2768 if (mx < 0)
2769 flags |= MUI_LPR_LEFT;
2770 else if (mx >= _width(data->area))
2771 flags |= MUI_LPR_RIGHT;
2772 else
2774 /* Identify column */
2775 if (data->entries_num > 0 && data->columns > 0)
2777 LONG width_sum = 0;
2778 col = data->columns - 1;
2779 for (i = 0; i < data->columns; i++)
2781 result->xoffset = mx - width_sum;
2782 width_sum +=
2783 data->ci[i].entries_width +
2784 data->ci[i].delta +
2785 (data->ci[i].bar ? BAR_WIDTH : 0);
2786 D(bug("[List/MUIM_TestPos] i %d "
2787 "width %d width_sum %d mx %d\n",
2788 i, data->ci[i].entries_width, width_sum, mx));
2789 if (mx < width_sum)
2791 col = i;
2792 D(bug("[List/MUIM_TestPos] Column hit %d\n", col));
2793 break;
2799 result->entry = row;
2800 result->column = col;
2801 result->flags = flags;
2803 return TRUE;
2806 /****i* List.mui/MUIM_DragQuery **********************************************
2808 * NAME
2809 * MUIM_DragQuery
2811 ******************************************************************************
2815 IPTR List__MUIM_DragQuery(struct IClass *cl, Object *obj,
2816 struct MUIP_DragQuery *msg)
2818 if (msg->obj == obj)
2819 return MUIV_DragQuery_Accept;
2820 else
2821 return MUIV_DragQuery_Refuse;
2825 /****i* List.mui/MUIM_DragFinish *********************************************
2827 * NAME
2828 * MUIM_DragFinish
2830 ******************************************************************************
2834 IPTR List__MUIM_DragFinish(struct IClass *cl, Object *obj,
2835 struct MUIP_DragFinish *msg)
2837 struct MUI_ListData *data = INST_DATA(cl, obj);
2839 data->drop_mark_y = -1;
2841 return DoSuperMethodA(cl, obj, (Msg) msg);
2844 /****i* List.mui/MUIM_DragReport *********************************************
2846 * NAME
2847 * MUIM_DragReport
2849 ******************************************************************************
2853 IPTR List__MUIM_DragReport(struct IClass *cl, Object *obj,
2854 struct MUIP_DragReport *msg)
2856 struct MUI_ListData *data = INST_DATA(cl, obj);
2857 struct MUI_List_TestPos_Result pos;
2858 struct RastPort *rp = _rp(obj);
2859 LONG n, y;
2860 UWORD old_pattern;
2862 /* Choose new drop mark position */
2864 DoMethod(obj, MUIM_List_TestPos, msg->x, msg->y, (IPTR) &pos);
2865 if (pos.entry != -1)
2867 n = pos.entry;
2868 if (pos.yoffset > 0)
2869 n++;
2871 else if ((pos.flags & MUI_LPR_ABOVE) != 0)
2872 n = data->entries_first;
2873 else
2875 n = MIN(data->entries_visible, data->entries_num)
2876 - data->entries_first;
2879 /* Clear old drop mark */
2881 if ((data->flags & LIST_SHOWDROPMARKS) != 0)
2883 y = data->entries_top_pixel + (n - data->entries_first)
2884 * data->entry_maxheight;
2885 if (y != data->drop_mark_y)
2887 DoMethod(obj, MUIM_DrawBackground, _mleft(data->area), data->drop_mark_y,
2888 _mwidth(data->area), 1, 0, 0, 0);
2890 /* Draw new drop mark and store its position */
2892 SetABPenDrMd(rp, _pens(obj)[MPEN_SHINE], _pens(obj)[MPEN_SHADOW],
2893 JAM2);
2894 old_pattern = rp->LinePtrn;
2895 SetDrPt(rp, 0xF0F0);
2896 Move(rp, _mleft(data->area), y);
2897 Draw(rp, _mright(data->area), y);
2898 SetDrPt(rp, old_pattern);
2899 data->drop_mark_y = y;
2903 return TRUE;
2907 /****i* List.mui/MUIM_DragDrop ***********************************************
2909 * NAME
2910 * MUIM_DragDrop
2912 ******************************************************************************
2916 IPTR List__MUIM_DragDrop(struct IClass *cl, Object *obj,
2917 struct MUIP_DragDrop *msg)
2919 struct MUI_ListData *data = INST_DATA(cl, obj);
2920 struct MUI_List_TestPos_Result pos;
2921 LONG n;
2923 /* Find drop position */
2925 DoMethod(obj, MUIM_List_TestPos, msg->x, msg->y, (IPTR) &pos);
2926 if (pos.entry != -1)
2928 /* Change drop position when coords move past centre of entry, not
2929 * entry boundary */
2931 n = pos.entry;
2932 if (pos.yoffset > 0)
2933 n++;
2935 /* Ensure that dropped entry will be positioned between the two
2936 * entries that are above and below the drop mark, rather than
2937 * strictly at the numeric index shown */
2939 if (n > data->entries_active)
2940 n--;
2942 else if ((pos.flags & MUI_LPR_ABOVE) != 0)
2943 n = MUIV_List_Move_Top;
2944 else
2945 n = MUIV_List_Move_Bottom;
2947 DoMethod(msg->obj, MUIM_List_Move, MUIV_List_Move_Active, n);
2949 return TRUE;
2953 /****i* List.mui/MUIM_CreateDragImage ****************************************
2955 * NAME
2956 * MUIM_CreateDragImage
2958 ******************************************************************************
2962 static IPTR List__MUIM_CreateDragImage(struct IClass *cl, Object *obj,
2963 struct MUIP_CreateDragImage *msg)
2965 struct MUI_ListData *data = INST_DATA(cl, obj);
2966 BOOL success = TRUE;
2967 struct MUI_List_TestPos_Result pos;
2968 WORD width, height, left, top;
2969 struct MUI_DragImage *img = NULL;
2970 const struct ZuneFrameGfx *zframe;
2971 LONG depth;
2973 /* Get info on dragged entry */
2974 DoMethod(obj, MUIM_List_TestPos, _left(data->area) - msg->touchx,
2975 _top(data->area) - msg->touchy, (IPTR) &pos);
2976 if (pos.entry == -1)
2977 success = FALSE;
2979 if (success)
2981 /* Get boundaries of entry */
2982 width = _mwidth(data->area);
2983 height = data->entry_maxheight;
2984 left = _mleft(data->area);
2985 top = _top(data->area) - msg->touchy
2986 - (pos.yoffset + data->entry_maxheight / 2);
2988 /* Allocate drag image structure */
2989 img = (struct MUI_DragImage *)
2990 AllocVec(sizeof(struct MUI_DragImage), MEMF_CLEAR);
2991 if (img == NULL)
2992 success = FALSE;
2995 if (success)
2997 /* Get drag frame */
2998 zframe = zune_zframe_get(obj,
2999 &muiGlobalInfo(obj)->mgi_Prefs->frames[MUIV_Frame_Drag]);
3001 /* Allocate drag image buffer */
3002 img->width = width + zframe->ileft + zframe->iright;
3003 img->height = height + zframe->itop + zframe->ibottom;
3004 depth = GetBitMapAttr(_screen(obj)->RastPort.BitMap, BMA_DEPTH);
3005 img->bm = AllocBitMap(img->width, img->height, depth, BMF_MINPLANES,
3006 _screen(obj)->RastPort.BitMap);
3008 if (img->bm != NULL)
3010 /* Render entry */
3011 struct RastPort temprp;
3012 InitRastPort(&temprp);
3013 temprp.BitMap = img->bm;
3014 ClipBlit(_rp(obj), left, top, &temprp,
3015 zframe->ileft, zframe->itop, width, height,
3016 0xc0);
3018 /* Render frame */
3019 struct RastPort *rp_save = muiRenderInfo(obj)->mri_RastPort;
3020 muiRenderInfo(obj)->mri_RastPort = &temprp;
3021 zframe->draw(zframe->customframe, muiRenderInfo(obj), 0, 0,
3022 img->width, img->height, 0, 0, img->width, img->height);
3023 muiRenderInfo(obj)->mri_RastPort = rp_save;
3026 /* Ensure drag point matches where user clicked */
3027 img->touchx = msg->touchx - zframe->ileft + _addleft(obj);
3028 img->touchy = -(pos.yoffset + data->entry_maxheight / 2)
3029 - zframe->itop;
3030 img->flags = 0;
3033 return (IPTR) img;
3036 static void DoWheelMove(struct IClass *cl, Object *obj, LONG wheely)
3038 LONG new, first, entries, visible;
3040 new = first = XGET(obj, MUIA_List_First);
3041 entries = XGET(obj, MUIA_List_Entries);
3042 visible = XGET(obj, MUIA_List_Visible);
3044 new += wheely;
3046 if (new > entries - visible)
3048 new = entries - visible;
3051 if (new < 0)
3053 new = 0;
3056 if (new != first)
3058 set(obj, MUIA_List_First, new);
3062 /**************************************************************************
3063 MUIM_HandleEvent
3064 **************************************************************************/
3065 IPTR List__MUIM_HandleEvent(struct IClass *cl, Object *obj, struct MUIP_HandleEvent *msg)
3067 struct MUI_ListData *data = INST_DATA(cl, obj);
3068 struct MUI_List_TestPos_Result pos;
3069 LONG seltype, old_active, new_active, visible, first, last, i;
3070 IPTR result = 0;
3071 BOOL select = FALSE, clear = FALSE, range_select = FALSE, changing;
3072 WORD delta;
3073 typeof(msg->muikey) muikey = msg->muikey;
3075 new_active = old_active = XGET(obj, MUIA_List_Active);
3076 visible = XGET(obj, MUIA_List_Visible);
3078 if (muikey != MUIKEY_NONE)
3080 result = MUI_EventHandlerRC_Eat;
3082 /* Make keys behave differently in read-only mode */
3083 if (data->read_only)
3085 switch (muikey)
3087 case MUIKEY_TOP:
3088 muikey = MUIKEY_LINESTART;
3089 break;
3091 case MUIKEY_BOTTOM:
3092 muikey = MUIKEY_LINEEND;
3093 break;
3095 case MUIKEY_UP:
3096 muikey = MUIKEY_LEFT;
3097 break;
3099 case MUIKEY_DOWN:
3100 case MUIKEY_PRESS:
3101 muikey = MUIKEY_RIGHT;
3102 break;
3106 switch (muikey)
3108 case MUIKEY_TOGGLE:
3109 if (data->multiselect != MUIV_Listview_MultiSelect_None
3110 && !data->read_only)
3112 select = TRUE;
3113 data->click_column = data->def_click_column;
3114 new_active = MUIV_List_Active_Down;
3116 else
3118 DoMethod(obj, MUIM_List_Jump, 0);
3119 muikey = MUIKEY_NONE;
3121 break;
3123 case MUIKEY_TOP:
3124 new_active = MUIV_List_Active_Top;
3125 break;
3127 case MUIKEY_BOTTOM:
3128 new_active = MUIV_List_Active_Bottom;
3129 break;
3131 case MUIKEY_LEFT:
3132 case MUIKEY_WORDLEFT:
3133 DoMethod(obj, MUIM_List_Jump, MUIV_List_Jump_Up);
3134 break;
3136 case MUIKEY_RIGHT:
3137 case MUIKEY_WORDRIGHT:
3138 DoMethod(obj, MUIM_List_Jump, MUIV_List_Jump_Down);
3139 break;
3141 case MUIKEY_LINESTART:
3142 DoMethod(obj, MUIM_List_Jump, MUIV_List_Jump_Top);
3143 break;
3145 case MUIKEY_LINEEND:
3146 DoMethod(obj, MUIM_List_Jump, MUIV_List_Jump_Bottom);
3147 break;
3149 case MUIKEY_UP:
3150 new_active = MUIV_List_Active_Up;
3151 break;
3153 case MUIKEY_DOWN:
3154 new_active = MUIV_List_Active_Down;
3155 break;
3157 case MUIKEY_PAGEUP:
3158 if (data->read_only)
3159 DoWheelMove(cl, obj, -visible);
3160 else
3161 new_active = MUIV_List_Active_PageUp;
3162 break;
3164 case MUIKEY_PAGEDOWN:
3165 if (data->read_only)
3166 DoWheelMove(cl, obj, visible);
3167 else
3168 new_active = MUIV_List_Active_PageDown;
3169 break;
3171 default:
3172 result = 0;
3175 else if (msg->imsg)
3177 DoMethod(obj, MUIM_List_TestPos, msg->imsg->MouseX, msg->imsg->MouseY, (IPTR) &pos);
3179 switch (msg->imsg->Class)
3181 case IDCMP_MOUSEBUTTONS:
3182 if (msg->imsg->Code == SELECTDOWN)
3184 if (_isinobject(data->area, msg->imsg->MouseX, msg->imsg->MouseY))
3186 data->mouse_click = MOUSE_CLICK_ENTRY;
3188 if (!data->read_only && pos.entry != -1)
3190 new_active = pos.entry;
3192 clear = (data->multiselect == MUIV_Listview_MultiSelect_Shifted
3193 && (msg->imsg->Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)) == 0);
3194 seltype = clear ? MUIV_List_Select_On: MUIV_List_Select_Toggle;
3195 select = data->multiselect != MUIV_Listview_MultiSelect_None;
3197 /* Handle MUIA_Listview_ClickColumn */
3198 data->click_column = pos.column;
3199 superset(cl, obj, MUIA_Listview_ClickColumn,
3200 data->click_column);
3202 /* Handle double clicking */
3203 if (data->last_active == pos.entry
3204 && DoubleClick(data->last_secs, data->last_mics, msg->imsg->Seconds, msg->imsg->Micros))
3206 set(obj, MUIA_Listview_DoubleClick, TRUE);
3207 data->last_active = -1;
3208 data->last_secs = data->last_mics = 0;
3210 else
3212 data->last_active = pos.entry;
3213 data->last_secs = msg->imsg->Seconds;
3214 data->last_mics = msg->imsg->Micros;
3217 /* Look out for mouse movement, timer and
3218 inactive-window events while mouse button is
3219 down */
3220 DoMethod(_win(obj), MUIM_Window_RemEventHandler, (IPTR) &data->ehn);
3221 data->ehn.ehn_Events |= (IDCMP_MOUSEMOVE | IDCMP_INTUITICKS |IDCMP_INACTIVEWINDOW);
3222 DoMethod(_win(obj), MUIM_Window_AddEventHandler, (IPTR) &data->ehn);
3226 else
3228 /* Activate object */
3229 if (msg->imsg->Code == SELECTUP && data->mouse_click)
3231 set(_win(obj), MUIA_Window_ActiveObject, (IPTR)obj);
3232 data->mouse_click = 0;
3235 /* Restore normal event mask */
3236 DoMethod(_win(obj), MUIM_Window_RemEventHandler, (IPTR) &data->ehn);
3237 data->ehn.ehn_Events &= ~(IDCMP_MOUSEMOVE | IDCMP_INTUITICKS | IDCMP_INACTIVEWINDOW);
3238 DoMethod(_win(obj), MUIM_Window_AddEventHandler, (IPTR) &data->ehn);
3240 break;
3242 case IDCMP_MOUSEMOVE:
3243 case IDCMP_INTUITICKS:
3244 if (pos.flags & MUI_LPR_ABOVE)
3245 new_active = MUIV_List_Active_Up;
3246 else if (pos.flags & MUI_LPR_BELOW)
3247 new_active = MUIV_List_Active_Down;
3248 else
3249 new_active = pos.entry;
3251 select = new_active != old_active && data->multiselect != MUIV_Listview_MultiSelect_None;
3252 if (select)
3254 DoMethod(obj, MUIM_List_Select, MUIV_List_Select_Active, MUIV_List_Select_Ask, &seltype);
3255 range_select = new_active >= 0;
3258 break;
3260 case IDCMP_INACTIVEWINDOW:
3261 /* Stop listening for events we only listen to when mouse button is
3262 down: we will not be informed of the button being released */
3263 DoMethod(_win(obj), MUIM_Window_RemEventHandler, (IPTR) &data->ehn);
3264 data->ehn.ehn_Events &= ~(IDCMP_MOUSEMOVE | IDCMP_INTUITICKS | IDCMP_INACTIVEWINDOW);
3265 DoMethod(_win(obj), MUIM_Window_AddEventHandler, (IPTR) &data->ehn);
3266 break;
3268 case IDCMP_RAWKEY:
3269 /* Scroll wheel */
3270 if (_isinobject(data->vert, msg->imsg->MouseX, msg->imsg->MouseY))
3271 delta = 1;
3272 else if (_isinobject(data->area, msg->imsg->MouseX, msg->imsg->MouseY))
3273 delta = 4;
3274 else
3275 delta = 0;
3277 if (delta != 0)
3279 switch (msg->imsg->Code)
3281 case RAWKEY_NM_WHEEL_UP:
3282 DoWheelMove(cl, obj, -delta);
3283 break;
3285 case RAWKEY_NM_WHEEL_DOWN:
3286 DoWheelMove(cl, obj, delta);
3287 break;
3289 result = MUI_EventHandlerRC_Eat;
3291 break;
3295 /* Decide in advance if any selections may change */
3296 changing = clear || muikey == MUIKEY_TOGGLE || select;
3298 /* Change selected and active entries */
3299 if (changing)
3300 set(obj, MUIA_Listview_SelectChange, TRUE);
3302 if (clear)
3304 DoMethod(obj, MUIM_List_Select, MUIV_List_Select_All, MUIV_List_Select_Off, NULL);
3307 if (muikey == MUIKEY_TOGGLE)
3309 DoMethod(obj, MUIM_List_Select, MUIV_List_Select_Active, MUIV_List_Select_Toggle, NULL);
3310 select = FALSE;
3313 if (new_active != old_active)
3314 set(obj, MUIA_List_Active, new_active);
3316 if (select)
3318 if (range_select)
3320 if (old_active < new_active)
3321 first = old_active + 1, last = new_active;
3322 else
3323 first = new_active, last = old_active - 1;
3324 for (i = first; i <= last; i++)
3325 DoMethod(obj, MUIM_List_Select, i, seltype, NULL);
3327 else
3328 DoMethod(obj, MUIM_List_Select, MUIV_List_Select_Active, seltype, NULL);
3331 if (changing)
3332 set(obj, MUIA_Listview_SelectChange, FALSE);
3334 return result;
3337 /**************************************************************************
3338 Dispatcher
3339 **************************************************************************/
3340 BOOPSI_DISPATCHER(IPTR, List_Dispatcher, cl, obj, msg)
3342 switch (msg->MethodID)
3344 case OM_NEW:
3345 return List__OM_NEW(cl, obj, (struct opSet *)msg);
3346 case OM_DISPOSE:
3347 return List__OM_DISPOSE(cl, obj, msg);
3348 case OM_SET:
3349 return List__OM_SET(cl, obj, (struct opSet *)msg);
3350 case OM_GET:
3351 return List__OM_GET(cl, obj, (struct opGet *)msg);
3353 case MUIM_Setup:
3354 return List__MUIM_Setup(cl, obj, (struct MUIP_Setup *)msg);
3355 case MUIM_Cleanup:
3356 return List__MUIM_Cleanup(cl, obj, (struct MUIP_Cleanup *)msg);
3357 case MUIM_HandleEvent:
3358 return List__MUIM_HandleEvent(cl, obj, (struct MUIP_HandleEvent *)msg);
3359 case MUIM_AskMinMax:
3360 return List__MUIM_AskMinMax(cl, obj, (struct MUIP_AskMinMax *)msg);
3361 case MUIM_Show:
3362 return List__MUIM_Show(cl, obj, (struct MUIP_Show *)msg);
3363 case MUIM_Hide:
3364 return List__MUIM_Hide(cl, obj, (struct MUIP_Hide *)msg);
3365 case MUIM_Draw:
3366 return List__MUIM_Draw(cl, obj, (struct MUIP_Draw *)msg);
3367 case MUIM_Layout:
3368 return List__MUIM_Layout(cl, obj, (struct MUIP_Layout *)msg);
3369 case MUIM_List_Clear:
3370 return List__MUIM_Clear(cl, obj, (struct MUIP_List_Clear *)msg);
3371 case MUIM_List_Sort:
3372 return List__MUIM_Sort(cl, obj, (struct MUIP_List_Sort *)msg);
3373 case MUIM_List_Exchange:
3374 return List__MUIM_Exchange(cl, obj,
3375 (struct MUIP_List_Exchange *)msg);
3376 case MUIM_List_Insert:
3377 return List__MUIM_Insert(cl, obj, (APTR) msg);
3378 case MUIM_List_InsertSingle:
3379 return List__MUIM_InsertSingle(cl, obj, (APTR) msg);
3380 case MUIM_List_GetEntry:
3381 return List__MUIM_GetEntry(cl, obj, (APTR) msg);
3382 case MUIM_List_Redraw:
3383 return List__MUIM_Redraw(cl, obj, (APTR) msg);
3384 case MUIM_List_Remove:
3385 return List__MUIM_Remove(cl, obj, (APTR) msg);
3386 case MUIM_List_Select:
3387 return List__MUIM_Select(cl, obj, (APTR) msg);
3388 case MUIM_List_Construct:
3389 return List__MUIM_Construct(cl, obj, (APTR) msg);
3390 case MUIM_List_Destruct:
3391 return List__MUIM_Destruct(cl, obj, (APTR) msg);
3392 case MUIM_List_Compare:
3393 return List__MUIM_Compare(cl, obj, (APTR) msg);
3394 case MUIM_List_Display:
3395 return List__MUIM_Display(cl, obj, (APTR) msg);
3396 case MUIM_List_SelectChange:
3397 return List__MUIM_SelectChange(cl, obj, (APTR) msg);
3398 case MUIM_List_CreateImage:
3399 return List__MUIM_CreateImage(cl, obj, (APTR) msg);
3400 case MUIM_List_DeleteImage:
3401 return List__MUIM_DeleteImage(cl, obj, (APTR) msg);
3402 case MUIM_List_Jump:
3403 return List__MUIM_Jump(cl, obj, (APTR) msg);
3404 case MUIM_List_Move:
3405 return List__MUIM_Move(cl, obj, (struct MUIP_List_Move *)msg);
3406 case MUIM_List_NextSelected:
3407 return List__MUIM_NextSelected(cl, obj,
3408 (struct MUIP_List_NextSelected *)msg);
3409 case MUIM_List_TestPos:
3410 return List__MUIM_TestPos(cl, obj, (APTR) msg);
3411 case MUIM_DragQuery:
3412 return List__MUIM_DragQuery(cl, obj, (APTR) msg);
3413 case MUIM_DragFinish:
3414 return List__MUIM_DragFinish(cl, obj, (APTR) msg);
3415 case MUIM_DragReport:
3416 return List__MUIM_DragReport(cl, obj, (APTR) msg);
3417 case MUIM_DragDrop:
3418 return List__MUIM_DragDrop(cl, obj, (APTR) msg);
3419 case MUIM_CreateDragImage:
3420 return List__MUIM_CreateDragImage(cl, obj, (APTR) msg);
3423 return DoSuperMethodA(cl, obj, msg);
3425 BOOPSI_DISPATCHER_END
3428 * Class descriptor.
3430 const struct __MUIBuiltinClass _MUI_List_desc =
3432 MUIC_List,
3433 MUIC_Group,
3434 sizeof(struct MUI_ListData),
3435 (void *) List_Dispatcher