Experiment:
[AROS-Contrib.git] / bgui / listclass.c
blob704ef3fe063ff31acdb8ab59251e058516c4b141
1 /*
2 * @(#) $Header$
4 * BGUI library
5 * listclass.c
7 * (C) Copyright 1998 Manuel Lemos.
8 * (C) Copyright 1996-1997 Ian J. Einman.
9 * (C) Copyright 1993-1996 Jaba Development.
10 * (C) Copyright 1993-1996 Jan van den Baard.
11 * All Rights Reserved.
13 * $Log$
14 * Revision 42.4 2004/06/16 20:16:48 verhaegs
15 * Use METHODPROTO, METHOD_END and REGFUNCPROTOn where needed.
17 * Revision 42.3 2000/08/17 15:09:18 chodorowski
18 * Fixed compiler warnings.
20 * Revision 42.2 2000/05/15 19:27:01 stegerg
21 * another hundreds of REG() macro replacements in func headers/protos.
23 * Revision 42.1 2000/05/14 23:32:47 stegerg
24 * changed over 200 function headers which all use register
25 * parameters (oh boy ...), because the simple REG() macro
26 * doesn't work with AROS. And there are still hundreds
27 * of headers left to be fixed :(
29 * Many of these functions would also work with stack
30 * params, but since i have fixed every single one
31 * I encountered up to now, I guess will have to do
32 * the same for the rest.
34 * Revision 42.0 2000/05/09 22:09:28 mlemos
35 * Bumped to revision 42.0 before handing BGUI to AROS team
37 * Revision 41.11 2000/05/09 19:54:37 mlemos
38 * Merged with the branch Manuel_Lemos_fixes.
40 * Revision 41.10.2.17 1999/08/08 23:05:11 mlemos
41 * Assured that the list is redrawn when the list font or the default font
42 * changes.
43 * Made the entry height be always recomputed every time the list is rendered.
45 * Revision 41.10.2.16 1999/07/28 06:34:32 mlemos
46 * Added rendering damage list after the ScrollRaster() call.
48 * Revision 41.10.2.15 1999/07/18 06:15:42 mlemos
49 * Fixed the problem of very quick drop without dragging.
51 * Revision 41.10.2.14 1999/07/03 15:17:40 mlemos
52 * Replaced the calls to CallHookPkt to BGUI_CallHookPkt.
54 * Revision 41.10.2.13 1999/05/24 20:20:43 mlemos
55 * Added fix for attempt to free BaseInfo handle twice when dragging 10 or
56 * more entries.
58 * Revision 41.10.2.12 1998/12/20 01:37:42 mlemos
59 * Ensured that the list is fully redrawn when a change like adding a new
60 * entry that makes the list partially invisible, makes the list overhead
61 * increase.
62 * Fixed computation of the top overhead area of a list view with title to
63 * include the height of the separator line (2 pixels).
65 * Revision 41.10.2.11 1998/12/07 03:07:03 mlemos
66 * Replaced OpenFont and CloseFont calls by the respective BGUI debug macros.
68 * Revision 41.10.2.10 1998/12/07 00:49:52 mlemos
69 * Fixed range checking bug of new list top entry when the number of visible
70 * entries is larger than the total entries.
72 * Revision 41.10.2.9 1998/12/06 21:38:21 mlemos
73 * Ensured that when the parent view and window are passed to the listview
74 * scroller gadget on its creation or when are set by OM_SET OM_UPDATE.
76 * Revision 41.10.2.8 1998/12/06 21:20:41 mlemos
77 * Ensured that the key activation selects the previous or the next selected
78 * entries in Multi Selection lists.
79 * Ensured that selecting the previous or the next selected entries in Multi
80 * Selection lists always deselects any previously selected entries.
82 * Revision 41.10.2.7 1998/11/28 13:45:17 mlemos
83 * Made the default flag of the list to pre clear the entries.
85 * Revision 41.10.2.6 1998/11/12 16:23:33 mlemos
86 * Passed NULL rastport pointer to AllocBaseInfo in the methods
87 * BASE_DRAGACTIVE, BASE_DRAGINACTIVE and BASE_DRAGUPDATE to ensure that it
88 * legally obtains the Rastport.
90 * Revision 41.10.2.5 1998/10/11 15:28:58 mlemos
91 * Enforced the columns minimum width value.
93 * Revision 41.10.2.4 1998/10/11 14:50:21 mlemos
94 * Fixed the computation of the initial offset of the column bars when the
95 * user starts to drag them.
97 * Revision 41.10.2.3 1998/06/16 01:22:10 mlemos
98 * Passed NULL rastport pointer to AllocBaseInfo in DrawDragLine to ensure
99 * that it legally obtains the Rastport.
101 * Revision 41.10.2.2 1998/06/15 21:48:43 mlemos
102 * Passed NULL rastport pointer to AllocBaseInfo in NewTop to ensure that it
103 * legally obtains the Rastport.
105 * Revision 41.10.2.1 1998/03/01 15:38:05 mlemos
106 * Added support to track BaseInfo memory leaks.
108 * Revision 41.10 1998/02/25 21:12:31 mlemos
109 * Bumping to 41.10
111 * Revision 1.1 1998/02/25 17:09:00 mlemos
112 * Ian sources
118 * Incorporates MultiColumnListview by Nick Christie.
119 * New code, but the interface is the same.
122 #include "include/classdefs.h"
125 * Internal entry storage.
127 typedef struct lvEntry {
128 struct lvEntry *lve_Next; /* Next entry. */
129 struct lvEntry *lve_Prev; /* Previous entry. */
130 ULONG lve_Flags; /* See below. */
131 APTR lve_Entry; /* Entry data. */
132 } LVE;
134 #define LVEF_SELECTED (1<<0) /* Entry is selected. */
135 #define LVEF_DISABLED (1<<1) /* Entry is disabled. */
136 #define LVEF_HIDDEN (1<<2) /* Entry is hidden. */
137 #define LVEF_REFRESH (1<<31) /* Entry must be redrawn. */
139 typedef struct {
140 LVE *lvl_First; /* First entry. */
141 LVE *lvl_EndMarker; /* End marker. */
142 LVE *lvl_Last; /* Last entry. */
143 } LVL;
145 typedef struct cd_ {
146 UWORD cd_Offset; /* Left offset of column. */
147 UWORD cd_Width; /* Width of column. */
148 UWORD cd_MinWidth; /* Minimum column width. */
149 UWORD cd_MaxWidth; /* Maximum column width. */
150 UWORD cd_Weight; /* Relative Weight of column. */
151 ULONG cd_Flags; /* Column Flags. */
152 } CD;
154 #define LVCF_NOSEPARATOR (1<<0) /* No column separator. */
155 #define LVCF_HIDDEN (1<<1) /* Column is not visible. */
156 #define LVCF_DRAGGABLE (1<<2) /* Column is draggable. */
157 #define LVCF_PRECLEAR (1<<3) /* Column is pre-cleared. */
160 * Object instance data.
162 typedef struct ld_ {
163 ULONG ld_Flags; /* See below. */
164 Object *ld_Prop; /* Scroller. */
165 BC *ld_BC; /* Bounding boxes. */
166 struct IBox ld_ListArea; /* Area bounds. */
167 LVL ld_Entries; /* List of entries. */
168 // LVL ld_Hidden; /* Entries filtered out. */
169 LVE *ld_TopEntry; /* Top entry. */
170 ULONG ld_ActiveEntry; /* Active entry number. */
171 LVE *ld_LastActive; /* Last active entry. */
172 ULONG ld_LastNum; /* Last active entry number. */
173 UWORD ld_LastCol; /* Last active column. */
174 LVE *ld_LastAdded; /* Last added entry. */
176 UWORD ld_EntryHeight; /* Height of a single entry. */
177 UWORD ld_Overhead; /* View area overhead. */
178 ULONG ld_Top; /* Top entry visible. */
179 ULONG ld_Total; /* Number of entries. */
180 LONG ld_Visible; /* Number of visible entries. */
181 struct Hook *ld_Resource; /* Resource hook. */
182 struct Hook *ld_Display; /* Display hook. */
183 struct Hook *ld_Compare; /* Comparisson hook. */
184 struct Hook *ld_TitleHook; /* Title hook. */
185 UBYTE *ld_Title; /* Title. */
186 // struct Hook *ld_Filter; /* Filter hook. */
187 struct TextAttr *ld_ListFont; /* List font. */
188 struct TextFont *ld_Font; /* List font. */
189 APTR ld_MemoryPool; /* OS 3.0 memory pool. */
190 ULONG ld_MultiStart; /* Start of multi-(de)select. */
191 UWORD ld_MultiMode; /* Mode of multi-(de)select. */
192 UWORD ld_MinShown; /* Min # lines shown. */
193 ULONG ld_Secs[2]; /* Double-clicks. */
194 ULONG ld_Mics[2];
195 ULONG ld_NewPos; /* New entry position. */
196 APTR ld_ScanEntry; /* Last scanned entry. */
197 LVE *ld_ScanNode; /* And it's node. */
198 ULONG ld_DropSpot; /* Place where dropped. */
199 ULONG ld_DrawSpot; /* Place where line drawn. */
200 struct RastPort *ld_DragRP; /* Draggable bitmap+rport. */
201 struct RastPort *ld_LineBuffer; /* Place for drop line. */
203 UWORD ld_Columns; /* number of columns, > 0 */
204 UWORD ld_OneColumn; /* Render only one column. */
205 UWORD ld_DragColumn; /* column # being dragged, 0...n */
206 WORD ld_DragXLine; /* current x position of drag line */
207 CD *ld_CD; /* Column Descriptors. */
209 } LD;
211 #define ld_InnerBox ld_BC->bc_InnerBox
212 #define ld_OuterBox ld_BC->bc_OuterBox
213 #define ld_HitBox ld_BC->bc_HitBox
214 #define ld_Frame ld_BC->bc_Frame
216 #define LDF_READ_ONLY (1 << 0 ) /* Read-only list. */
217 #define LDF_MULTI_SELECT (1 << 1 ) /* Multi-select list. */
218 #define LDF_REFRESH_ALL (1 << 2 ) /* Refresh whole list. */
219 #define LDF_PROPACTIVE (1 << 4 ) /* Prop gadget is active. */
220 #define LDF_LIST_BUSY (1 << 5 ) /* We are busy with the list. */
221 #define LDF_NOSHIFT (1 << 6 ) /* Multi-select without shift */
222 #define LDF_CUSTOMDISABLE (1 << 7 ) /* Display hook renders disabled state. */
223 #define LDF_SHOWDROPSPOT (1 << 8 ) /* Show where we they drop. */
224 #define LDF_DRAGGABLE (1 << 9 ) /* We are in draggable mode. */
225 #define LDF_ALLOW_DRAG (1 << 10) /* Allow column dragging by user */
226 #define LDF_OFFSETS_VALID (1 << 11) /* Column offsets are valid */
227 #define LDF_DRAGGING_COLUMN (1 << 12) /* Currently dragging a column */
228 #define LDF_NEW_COLUMN_POS (1 << 13) /* Set when user releases column */
229 #define LDF_PRE_CLEAR (1 << 14) /* Pre-clear entry rectangle. */
230 #define LDF_THIN_FRAMES (1 << 15) /* 1:1 Aspect ratio frames. */
231 #define LDF_MOVE_DROPBOX (1 << 16) /* Move the dragbox around? */
232 #define LDF_ONE_COLUMN (1 << 17) /* Redraw only one column. */
234 #define LD_ENTRY(tag, offset, flags) PACK_ENTRY(LISTV_TAGSTART, tag, ld_, offset, flags)
235 #define LD_FLAG(tag, flag) PACK_LONGBIT(LISTV_TAGSTART, tag, ld_, ld_Flags, PKCTRL_BIT, flag)
237 static ULONG ListPackTable[] =
239 PACK_STARTTABLE(LISTV_TAGSTART),
241 LD_ENTRY(LISTV_ResourceHook, ld_Resource, PKCTRL_ULONG),
242 LD_ENTRY(LISTV_DisplayHook, ld_Display, PKCTRL_ULONG),
243 LD_ENTRY(LISTV_CompareHook, ld_Compare, PKCTRL_ULONG),
244 LD_ENTRY(LISTV_TitleHook, ld_TitleHook, PKCTRL_ULONG),
245 LD_ENTRY(LISTV_MinEntriesShown, ld_MinShown, PKCTRL_UWORD),
246 LD_ENTRY(LISTV_Top, ld_Top, PKCTRL_ULONG),
247 LD_ENTRY(LISTV_Columns, ld_Columns, PKCTRL_UWORD),
248 LD_ENTRY(LISTV_Title, ld_Title, PKCTRL_ULONG),
249 LD_ENTRY(LISTV_PropObject, ld_Prop, PKCTRL_ULONG),
250 LD_ENTRY(LISTV_ListFont, ld_ListFont, PKCTRL_ULONG),
252 LD_FLAG(LISTV_ReadOnly, LDF_READ_ONLY),
253 LD_FLAG(LISTV_MultiSelect, LDF_MULTI_SELECT),
254 LD_FLAG(LISTV_ThinFrames, LDF_THIN_FRAMES),
255 LD_FLAG(LISTV_MultiSelectNoShift, LDF_NOSHIFT),
256 LD_FLAG(LISTV_ShowDropSpot, LDF_SHOWDROPSPOT),
257 LD_FLAG(LISTV_CustomDisable, LDF_CUSTOMDISABLE),
258 LD_FLAG(LISTV_DragColumns, LDF_ALLOW_DRAG),
259 LD_FLAG(LISTV_PreClear, LDF_PRE_CLEAR),
261 PACK_ENDTABLE
264 #define LC_ENTRY(tag, offset, flags) PACK_ENTRY(LISTC_TAGSTART, tag, cd_, offset, flags)
265 #define LC_FLAG(tag, flag) PACK_LONGBIT(LISTC_TAGSTART, tag, cd_, cd_Flags, PKCTRL_BIT, flag)
267 static ULONG ColumnPackTable[] =
269 PACK_STARTTABLE(LISTV_TAGSTART),
271 LC_ENTRY(LISTC_MinWidth, cd_MinWidth, PKCTRL_UWORD),
272 LC_ENTRY(LISTC_MaxWidth, cd_MaxWidth, PKCTRL_UWORD),
273 LC_ENTRY(LISTC_Weight, cd_Weight, PKCTRL_UWORD),
275 LC_FLAG(LISTC_Draggable, LVCF_DRAGGABLE),
276 LC_FLAG(LISTC_Hidden, LVCF_HIDDEN),
277 LC_FLAG(LISTC_NoSeparator, LVCF_NOSEPARATOR),
278 LC_FLAG(LISTC_PreClear, LVCF_PRECLEAR),
280 PACK_ENDTABLE
284 * Prop map-list.
286 STATIC struct TagItem PGA2LISTV[] = {
287 PGA_Top, LISTV_Top,
288 TAG_END
291 STATIC VOID RenderEntry(Object *obj, LD *ld, struct BaseInfo *bi, LVE *lve, ULONG top);
292 #define REL_ZERO (0x80000000)
295 //VOID ASM ColumnSeparators(REG(a0) LD *ld, REG(a1) struct BaseInfo *bi, REG(d0) ULONG x, REG(d1) ULONG y, REG(d2) ULONG h)
296 ASM REGFUNC5(VOID, ColumnSeparators,
297 REGPARAM(A0, LD *, ld),
298 REGPARAM(A1, struct BaseInfo *, bi),
299 REGPARAM(D0, ULONG, x),
300 REGPARAM(D1, ULONG, y),
301 REGPARAM(D2, ULONG, h))
303 int col, pena, penb, x2, y2;
305 struct RastPort *rp = bi->bi_RPort;
307 x2 = x + ld->ld_InnerBox.Width - 4;
308 y2 = y + h - 1;
310 if (ld->ld_Flags & LDF_READ_ONLY)
312 pena = SHINEPEN;
313 penb = SHADOWPEN;
315 else
317 pena = SHADOWPEN;
318 penb = SHINEPEN;
321 for (col = 0; col < ld->ld_Columns; col++)
323 if (!(ld->ld_CD[col].cd_Flags & LVCF_HIDDEN))
325 x += ld->ld_CD[col].cd_Width - 2;
327 if (!(ld->ld_CD[col].cd_Flags & LVCF_NOSEPARATOR) && (x < x2))
329 BSetDPenA(bi, pena);
330 Move(rp, x, y);
331 Draw(rp, x++, y2);
333 BSetDPenA(bi, penb);
334 Move(rp, x, y);
335 Draw(rp, x++, y2);
340 REGFUNC_END
342 * Find a node by it's number (slow!).
344 //STATIC ASM LVE *FindNode( REG(a0) LD *ld, REG(d0) ULONG num )
345 STATIC ASM REGFUNC2(LVE *, FindNode,
346 REGPARAM(A0, LD *, ld),
347 REGPARAM(D0, ULONG, num))
349 LVE *lve;
350 ULONG lnum = 0L;
353 * List empty?
355 if ( ! ld->ld_Entries.lvl_First->lve_Next )
356 return( NULL );
359 * Scan the list top-down to find
360 * the correct entry.
362 for ( lve = ld->ld_Entries.lvl_First; lve->lve_Next; lve = lve->lve_Next, lnum++ ) {
363 if ( lnum == num )
364 break;
367 return( lve );
369 REGFUNC_END
372 * Find a node by it's number (quickly).
374 //STATIC ASM LVE *FindNodeQuick( REG(a0) LD *ld, REG(d0) ULONG num )
375 STATIC ASM REGFUNC2(LVE *, FindNodeQuick,
376 REGPARAM(A0, LD *, ld),
377 REGPARAM(D0, ULONG, num))
379 LVE *lve = ld->ld_TopEntry;
380 ULONG top = ld->ld_Top;
383 * List empty?
385 if ( ! ld->ld_Entries.lvl_First->lve_Next )
386 return( NULL );
389 * Scan the list from the current node
390 * to find the correct entry.
392 if ( num > top ) {
393 for ( ; lve->lve_Next; lve = lve->lve_Next, top++ ) {
394 if ( top == num )
395 break;
397 } else if ( num < top ) {
398 for ( ; lve->lve_Prev != ( LVE * )&ld->ld_Entries; lve = lve->lve_Prev, top-- ) {
399 if ( top == num )
400 break;
404 return( lve );
406 REGFUNC_END
409 * Add an entry in the list. Ugly code with
410 * lotsa goto's :)
412 //STATIC ASM VOID AddEntryInList( REG(a0) LD *ld, REG(a1) Object *obj, REG(a2) LVE *lve, REG(d0) ULONG how )
413 STATIC ASM REGFUNC4(VOID, AddEntryInList,
414 REGPARAM(A0, LD *, ld),
415 REGPARAM(A1, Object *, obj),
416 REGPARAM(A2, LVE *, lve),
417 REGPARAM(D0, ULONG, how))
419 LVE *tmp;
420 struct lvCompare lvc;
423 * Save it.
425 ld->ld_LastAdded = lve;
428 * Set top entry if not
429 * defined yet.
431 if (!ld->ld_TopEntry)
432 ld->ld_TopEntry = lve;
434 switch (how)
436 case LVAP_SORTED:
438 * Entries available?
440 if (!ld->ld_Entries.lvl_First->lve_Next)
442 * No. Simply AddTail the entry.
444 goto tailIt;
447 * Scan through the entries.
449 for (tmp = ld->ld_Entries.lvl_Last; ; tmp = tmp->lve_Prev)
452 * Comparrison hook?
454 if (!ld->ld_Compare)
457 * No. Simple string comparrison.
459 if (Stricmp((STRPTR)lve->lve_Entry, (STRPTR)tmp->lve_Entry) >= 0)
460 break;
462 else
464 lvc.lvc_EntryA = lve->lve_Entry;
465 lvc.lvc_EntryB = tmp->lve_Entry;
466 if ((LONG)BGUI_CallHookPkt(ld->ld_Compare, (VOID *)obj, (VOID *)&lvc) >= 0)
467 break;
470 * Done?
472 if (tmp == ld->ld_Entries.lvl_First)
474 ld->ld_TopEntry = lve;
476 * First entry is AddHead'ed.
478 goto headIt;
482 insertIt:
483 ld->ld_Flags |= LDF_REFRESH_ALL;
484 Insert((struct List *)&ld->ld_Entries, (struct Node *)lve, (struct Node *)tmp);
485 break;
487 case LVAP_HEAD:
488 headIt:
489 ld->ld_Flags |= LDF_REFRESH_ALL;
490 AddHead((struct List *)&ld->ld_Entries, (struct Node *)lve);
491 ld->ld_TopEntry = lve;
492 break;
494 case LVAP_TAIL:
495 default:
496 tailIt:
497 AddTail((struct List *)&ld->ld_Entries, (struct Node *)lve);
498 break;
501 REGFUNC_END
504 * Add entries to the list.
506 //STATIC ASM BOOL AddEntries(REG(a0) LD *ld, REG(a1) APTR *entries, REG(a2) Object *obj, REG(d0) ULONG how, REG(a3) LVE *pred)
507 STATIC ASM REGFUNC5(BOOL, AddEntries,
508 REGPARAM(A0, LD *, ld),
509 REGPARAM(A1, APTR *, entries),
510 REGPARAM(A2, Object *, obj),
511 REGPARAM(D0, ULONG, how),
512 REGPARAM(A3, LVE *, pred))
514 LVE *lve;
515 struct lvResource lvr;
516 BOOL success = TRUE;
519 * Entries valid?
521 if (!entries) return FALSE;
524 * Initialize lvResource structure to make entries.
526 lvr.lvr_Command = LVRC_MAKE;
528 ld->ld_Flags |= LDF_LIST_BUSY;
531 * Loop through the entries.
533 while (lvr.lvr_Entry = *entries++)
536 * Create a node.
538 if (lve = (LVE *)BGUI_AllocPoolMem(sizeof(LVE)))
541 * Do we have a resource hook?
543 if (ld->ld_Resource)
546 * Let the hook make the entry.
548 lve->lve_Entry = (APTR)BGUI_CallHookPkt(ld->ld_Resource, (void *)obj, (void *)&lvr);
550 else
553 * Simple string copy.
555 if (lve->lve_Entry = (APTR)BGUI_AllocPoolMem(strlen((STRPTR)lvr.lvr_Entry) + 1))
556 strcpy((STRPTR)lve->lve_Entry, (STRPTR)lvr.lvr_Entry);
559 * All ok?
561 if (lve->lve_Entry)
563 if (!pred)
565 AddEntryInList(ld, obj, lve, how);
566 if (how == LVAP_HEAD)
567 pred = lve;
569 else
571 Insert((struct List *)&ld->ld_Entries, (struct Node *)lve, (struct Node *)pred);
572 pred = lve;
574 ld->ld_LastAdded = lve;
575 ld->ld_Total++;
577 else
579 success = FALSE;
580 BGUI_FreePoolMem(lve);
584 ld->ld_Flags &= ~LDF_LIST_BUSY;
586 return success;
588 REGFUNC_END
591 * Make an entry visible.
593 //STATIC ASM ULONG MakeVisible(REG(a0) LD *ld, REG(d0) ULONG entry)
594 STATIC ASM REGFUNC2(ULONG, MakeVisible,
595 REGPARAM(A0, LD *, ld),
596 REGPARAM(D0, ULONG, entry))
598 ULONG new_top;
601 * # of visible items known?
603 if (!ld->ld_Visible)
604 ld->ld_Visible = 1;
607 * Otherwise adjust the top to
608 * make it visible.
610 if (entry < ld->ld_Top) new_top = entry;
611 else if (entry >= (ld->ld_Top + ld->ld_Visible)) new_top = max((LONG)(entry - (ld->ld_Visible - 1)), 0);
612 else if (entry >= ld->ld_Total) new_top = max((LONG)ld->ld_Total - (LONG)(ld->ld_Visible - 1), 0);
613 else new_top = ld->ld_Top;
615 return new_top;
617 REGFUNC_END
620 * De-select node list (slow!).
622 //STATIC ASM VOID DeSelect(REG(a0) LD *ld)
623 STATIC ASM REGFUNC1(VOID, DeSelect,
624 REGPARAM(A0, LD *, ld))
626 LVE *lve;
628 for (lve = ld->ld_Entries.lvl_First; lve->lve_Next; lve = lve->lve_Next)
630 if (lve->lve_Flags & LVEF_SELECTED)
632 lve->lve_Flags &= ~LVEF_SELECTED;
633 lve->lve_Flags |= LVEF_REFRESH;
637 REGFUNC_END
640 * Select node list (slow!).
642 //STATIC ASM VOID Select(REG(a0) LD *ld)
643 STATIC ASM REGFUNC1(VOID, Select,
644 REGPARAM(A0, LD *, ld))
646 LVE *lve;
648 for ( lve = ld->ld_Entries.lvl_First; lve->lve_Next; lve = lve->lve_Next ) {
649 if ( ! ( lve->lve_Flags & LVEF_SELECTED ))
650 lve->lve_Flags |= LVEF_SELECTED | LVEF_REFRESH;
653 REGFUNC_END
656 * Setup a new top-value.
658 //STATIC ASM VOID NewTop(REG(a0) LD *ld, REG(a1) struct GadgetInfo *gi, REG(a2) Object *obj, REG(d0) ULONG new_top)
659 STATIC ASM REGFUNC4(VOID, NewTop,
660 REGPARAM(A0, LD *, ld),
661 REGPARAM(A1, struct GadgetInfo *, gi),
662 REGPARAM(A2, Object *, obj),
663 REGPARAM(D0, ULONG, new_top))
665 struct BaseInfo *bi;
666 int i;
667 BC *bc = BASE_DATA(obj);
670 * How much do we need to go up?
672 int diff = new_top - ld->ld_Top;
675 * Gadget info present?
676 * New position different than old one?
678 if (gi && diff)
681 * Create rastport.
683 #ifdef DEBUG_BGUI
684 if (bi = AllocBaseInfoDebug(__FILE__,__LINE__,BI_GadgetInfo, gi, BI_RastPort, NULL, TAG_DONE))
685 #else
686 if (bi = AllocBaseInfo(BI_GadgetInfo, gi, BI_RastPort, NULL, TAG_DONE))
687 #endif
689 BOOL needs_refresh=FALSE;
692 * Setup font.
694 BSetFont(bi, ld->ld_Font);
697 * Set top position.
699 ld->ld_TopEntry = FindNodeQuick(ld, new_top);
700 ld->ld_Top = new_top;
702 if (diff <= -ld->ld_Visible)
705 * More than a single view?
707 diff = -ld->ld_Visible;
709 else if (diff >= +ld->ld_Visible)
712 * More than a single view?
714 diff = +ld->ld_Visible;
716 else
719 * Scroll view 'diff' lines.
721 ScrollRaster(bi->bi_RPort, 0, diff * ld->ld_EntryHeight,
722 ld->ld_ListArea.Left, ld->ld_ListArea.Top,
723 ld->ld_ListArea.Left + ld->ld_ListArea.Width - 1,
724 ld->ld_ListArea.Top + ld->ld_ListArea.Height - 1);
725 needs_refresh=(bi->bi_RPort->Layer->Flags & LAYERREFRESH);
728 ColumnSeparators(ld, bi, bc->bc_InnerBox.Left, bc->bc_InnerBox.Top, bc->bc_InnerBox.Height);
731 * Re-render first 'diff' lines.
733 if (diff < 0)
735 i = -diff;
737 else
739 i = diff;
740 new_top += ld->ld_Visible - diff;
743 while (i--)
745 RenderEntry(obj, ld, bi, FindNodeQuick(ld, new_top), new_top - ld->ld_Top);
746 new_top++;
750 * Dump BaseInfo.
752 FreeBaseInfo(bi);
754 if(needs_refresh)
756 BOOL update;
758 if(!(update=BeginUpdate(gi->gi_Layer)))
759 EndUpdate(gi->gi_Layer,FALSE);
760 DoRenderMethod( obj, gi, GREDRAW_REDRAW );
761 if(update)
762 EndUpdate(gi->gi_Layer,TRUE);
766 * Needed by RenderEntry().
768 ld->ld_Flags &= ~LDF_REFRESH_ALL;
771 else
773 ld->ld_TopEntry = FindNodeQuick(ld, new_top);
774 ld->ld_Top = new_top;
777 REGFUNC_END
781 /************************************************************************
782 ********************** MCLV_GETCOLUMNPOSITIONS() **********************
783 *************************************************************************
784 * Work out the left offset (relative to listview view area) of each
785 * column separator. Put the results in ld_Offsets array, starting
786 * with the first separator (ie. the left offset of the *second* column
787 * from the left). Returns TRUE or FALSE to indicate success. Currently
788 * this can only fail if the object is not displayed when this function
789 * is called. If all goes well, LDF_OFFSETS_VALID is set to TRUE.
791 *************************************************************************/
793 STATIC BOOL GetColumnPositions(Object *obj, LD *ld)
795 int w, dw, x;
796 int totalwidth, columnwidth, totalweight, lastwidth;
797 int listwidth = ld->ld_ListArea.Width;
798 CD *cd, *fcd = ld->ld_CD, *lcd = fcd + ld->ld_Columns - 1;
801 * Start widths at minimum and compute width of all columns.
803 columnwidth = 0;
804 for (cd = fcd; cd <= lcd; ++cd)
806 cd->cd_Width = cd->cd_MinWidth;
807 columnwidth += cd->cd_Width;
811 * Cycle through until all extra space is used.
813 while (columnwidth < listwidth)
815 lastwidth = columnwidth;
816 totalwidth = listwidth;
817 totalweight = 0;
818 for (cd = fcd; cd <= lcd; ++cd)
820 if (!(cd->cd_Flags & LVCF_HIDDEN))
822 if (cd->cd_Width < cd->cd_MaxWidth)
824 totalweight += cd->cd_Weight;
826 else
828 cd->cd_Width = cd->cd_MaxWidth;
829 totalwidth -= cd->cd_Width;
834 if (totalweight == 0) break;
836 for (cd = fcd; cd <= lcd; ++cd)
838 if (!(cd->cd_Flags & LVCF_HIDDEN))
840 if (cd->cd_Width < cd->cd_MaxWidth)
842 w = (cd->cd_Weight * totalwidth) / totalweight;
843 if (w > cd->cd_MaxWidth) w = cd->cd_MaxWidth;
844 if (w < cd->cd_MinWidth) w = cd->cd_MinWidth;
846 dw = w - cd->cd_Width;
847 if (dw > listwidth - columnwidth)
849 dw = listwidth - columnwidth;
850 cd->cd_Width += dw;
851 columnwidth += dw;
852 break;
854 cd->cd_Width += dw;
855 columnwidth += dw;
859 if (columnwidth == lastwidth) break;
862 x = 0;
863 for (cd = fcd; cd <= lcd; ++cd)
865 if (!(cd->cd_Flags & LVCF_HIDDEN))
867 cd->cd_Offset = x;
868 x += cd->cd_Width;
871 cd->cd_Offset = x; // Last "dummy" column needs right offset of list.
873 ld->ld_Flags |= LDF_OFFSETS_VALID|LDF_REFRESH_ALL;
875 return TRUE;
878 /************************************************************************
879 ************************* MCLV_DRAWDRAGLINE() *************************
880 *************************************************************************
881 * Draw, using complement mode, the line that shows the user the position
882 * s/he has dragged the column separator to within the mclv.
884 *************************************************************************/
886 //STATIC ASM VOID DrawDragLine(REG(a0) LD *ld, REG(a1) struct GadgetInfo *gi)
887 STATIC ASM REGFUNC2(VOID, DrawDragLine,
888 REGPARAM(A0, LD *, ld),
889 REGPARAM(A1, struct GadgetInfo *, gi))
891 WORD x1 = ld->ld_InnerBox.Left + ld->ld_DragXLine - 2;
892 WORD x2 = x1 + 1;
893 WORD y1 = ld->ld_InnerBox.Top;
894 WORD y2 = ld->ld_InnerBox.Top + ld->ld_InnerBox.Height - 1;
896 struct BaseInfo *bi;
898 #ifdef DEBUG_BGUI
899 if (bi = AllocBaseInfoDebug(__FILE__,__LINE__,BI_GadgetInfo, gi, BI_RastPort, NULL, TAG_DONE))
900 #else
901 if (bi = AllocBaseInfo(BI_GadgetInfo, gi, BI_RastPort, NULL, TAG_DONE))
902 #endif
904 BSetDrMd(bi, COMPLEMENT);
905 BRectFill(bi, x1, y1, x2, y2);
906 FreeBaseInfo(bi);
909 REGFUNC_END
911 /// OM_NEW
913 * Create a new object.
915 METHOD(ListClassNew, struct opSet *, ops)
917 LD *ld;
918 struct TagItem *tags, *tstate, *tag;
919 ULONG rc, sort = LVAP_TAIL, data;
920 ULONG *new_weights = NULL;
921 APTR *new_entries = NULL;
922 int i;
924 tags = DefTagList(BGUI_LISTVIEW_GADGET, ops->ops_AttrList);
927 * Let the superclass make the object.
929 if (rc = NewSuperObject(cl, obj, tags))
932 * Get the instance data.
934 ld = INST_DATA(cl, rc);
935 ld->ld_BC = BASE_DATA(rc);
937 ld->ld_Flags = LDF_REFRESH_ALL|LDF_PRE_CLEAR;
938 ld->ld_MinShown = 3;
939 ld->ld_Columns = 1;
940 ld->ld_Prop = (Object *)~0;
943 * Initialize the list of entries.
945 NewList((struct List *)&ld->ld_Entries);
946 // NewList((struct List *)&ld->ld_Hidden);
948 BGUI_PackStructureTags((APTR)ld, ListPackTable, tags);
950 tstate = tags;
952 while (tag = NextTagItem(&tstate))
954 data = tag->ti_Data;
955 switch (tag->ti_Tag)
957 case LISTV_EntryArray:
958 new_entries = (APTR *)data;
959 break;
961 case LISTV_SortEntryArray:
962 sort = LVAP_SORTED;
963 break;
965 case LISTV_ColumnWeights:
966 new_weights = (ULONG *)data;
967 break;
971 if (ld->ld_Columns < 1) ld->ld_Columns = 1;
973 ld->ld_CD = BGUI_AllocPoolMem((ld->ld_Columns + 1) * sizeof(CD));
976 * Verify array allocated.
978 if (ld->ld_CD)
980 for (i = 0; i <= ld->ld_Columns; i++)
982 ld->ld_CD[i].cd_MinWidth = 24;
983 ld->ld_CD[i].cd_MaxWidth = 0xFFFF;
984 ld->ld_CD[i].cd_Weight = new_weights ? (new_weights[i] ? new_weights[i] : 1) : DEFAULT_WEIGHT;
987 if (ld->ld_Prop == (Object *)~0)
990 * Filter out frame and label attributes.
992 tstate = tags;
994 while (tag = NextTagItem(&tstate))
996 switch (tag->ti_Tag)
999 * Don't disable prop!
1001 case GA_Disabled:
1003 * No drag'n'drop on the prop.
1005 case BT_DragObject:
1006 case BT_DropObject:
1007 tag->ti_Tag = TAG_IGNORE;
1008 break;
1010 default:
1011 if (FRM_TAG(tag->ti_Tag) || LAB_TAG(tag->ti_Tag))
1012 tag->ti_Tag = TAG_IGNORE;
1013 break;
1018 * Create a scroller.
1020 ld->ld_Prop = BGUI_NewObject(BGUI_PROP_GADGET, GA_ID, GADGET(rc)->GadgetID,
1021 PGA_DontTarget, TRUE, BT_ParentView, ld->ld_BC->bc_View, BT_ParentWindow, ld->ld_BC->bc_Window,TAG_MORE, tags);
1024 if (ld->ld_Prop)
1027 * Setup scroller notification.
1029 AsmDoMethod(ld->ld_Prop, BASE_ADDMAP, rc, PGA2LISTV);
1032 if (ld->ld_Frame)
1035 * Set frame attributes to match list attributes.
1037 DoSetMethodNG(ld->ld_Frame, FRM_Recessed, ld->ld_Flags & LDF_READ_ONLY,
1038 FRM_ThinFrame, ld->ld_Flags & LDF_THIN_FRAMES,
1039 TAG_DONE);
1043 * Add entries.
1045 AddEntries(ld, new_entries, (Object *)rc, sort, NULL);
1047 else
1049 AsmCoerceMethod(cl, (Object *)rc, OM_DISPOSE);
1050 rc = 0;
1053 FreeTagItems(tags);
1055 return rc;
1057 METHOD_END
1059 /// OM_SET, OM_UPDATE
1061 * Set or update some attributes.
1063 METHOD(ListClassSetUpdate, struct opUpdate *, opu)
1065 LD *ld = INST_DATA( cl, obj );
1066 struct TagItem *tstate = opu->opu_AttrList, *tag;
1067 LVE *lve;
1068 ULONG data, otop = ld->ld_Top, ntop = otop, num, oldcol = ld->ld_Columns;
1069 WORD dis = GADGET(obj)->Flags & GFLG_DISABLED;
1070 BOOL vc = FALSE;
1071 ULONG *new_weights;
1073 int i;
1076 * First the superclass...
1078 AsmDoSuperMethodA(cl, obj, (Msg)opu);
1080 BGUI_PackStructureTags((APTR)ld, ListPackTable, tstate);
1083 * Scan for known attributes.
1085 while (tag = NextTagItem(&tstate))
1087 data = tag->ti_Data;
1088 switch (tag->ti_Tag)
1090 case LISTV_PropObject:
1091 if (ld->ld_Prop) DisposeObject(ld->ld_Prop);
1092 ld->ld_Flags &= ~LDF_PROPACTIVE;
1094 if (ld->ld_Prop = (Object *)data)
1097 * Setup scroller notification.
1099 AsmDoMethod(ld->ld_Prop, BASE_ADDMAP, obj, PGA2LISTV);
1101 vc = TRUE;
1102 break;
1104 case FRM_ThinFrame:
1106 * Set frame thickness.
1108 if (ld->ld_Frame) DoSetMethodNG(ld->ld_Frame, FRM_ThinFrame, data, TAG_END);
1109 if (ld->ld_Prop) DoSetMethodNG(ld->ld_Prop, FRM_ThinFrame, data, TAG_END);
1110 break;
1112 case BT_TextAttr:
1114 * Pickup superclass font.
1116 if (ld->ld_Font)
1118 BGUI_CloseFont(ld->ld_Font);
1119 ld->ld_Font = NULL;
1121 vc=TRUE;
1122 break;
1124 case LISTV_ListFont:
1125 ld->ld_ListFont = (struct TextAttr *)data;
1126 if (ld->ld_Font)
1128 BGUI_CloseFont(ld->ld_Font);
1129 ld->ld_Font = NULL;
1131 vc=TRUE;
1132 break;
1134 case LISTV_Top:
1135 ld->ld_Top = otop; // Needed to circumvent PackStructureTags.
1137 * Make sure we stay in range.
1139 ntop = range(data, 0, (ld->ld_Total > ld->ld_Visible) ? ld->ld_Total - ld->ld_Visible : 0);
1140 break;
1142 case LISTV_MakeVisible:
1144 * Stay in range.
1146 if ((num = data) >= ld->ld_Total)
1147 num = ld->ld_Total - 1;
1150 * Make it visible.
1152 ntop = MakeVisible( ld, num );
1153 vc = TRUE;
1154 break;
1156 case LISTV_DeSelect:
1158 * Get the node number.
1160 if (( num = data ) == ~0 ) {
1162 * A value of -1 means deselect
1163 * all entries.
1165 DeSelect( ld );
1166 vc = TRUE;
1167 } else {
1169 * Stay in range.
1171 num = num >= ld->ld_Total ? ld->ld_Total - 1 : num;
1174 * Find the node to deselect.
1176 if ( lve = FindNodeQuick( ld, num )) {
1178 * Set it up.
1180 ld->ld_LastActive = lve;
1181 ld->ld_LastNum = num;
1184 * Already unselected?
1186 if (( lve->lve_Flags & LVEF_SELECTED ) && ( ! ( ld->ld_Flags & LDF_READ_ONLY ))) {
1188 * Mark it for a refresh.
1190 lve->lve_Flags &= ~LVEF_SELECTED;
1191 lve->lve_Flags |= LVEF_REFRESH;
1194 * Notify change.
1196 DoNotifyMethod( obj, opu->opu_GInfo, opu->MethodID == OM_UPDATE ? opu->opu_Flags : 0L, GA_ID, GADGET( obj )->GadgetID, LISTV_Entry, lve->lve_Entry, LISTV_EntryNumber, ld->ld_LastNum, TAG_END );
1197 vc = TRUE;
1201 break;
1203 case LISTV_SelectMulti:
1204 case LISTV_SelectMultiNotVisible:
1206 * Must be a multi-select object.
1208 if ( ! ( ld->ld_Flags & LDF_MULTI_SELECT ))
1209 break;
1212 * LISTV_Select_All?
1214 if (data == LISTV_Select_All)
1216 Select(ld);
1217 vc = TRUE;
1218 break;
1221 /* Fall through. */
1223 case LISTV_Select:
1224 case LISTV_SelectNotVisible:
1226 * Do some Magic?
1228 switch (data)
1230 case LISTV_Select_First:
1231 num = 0;
1232 goto doIt;
1234 case LISTV_Select_Last:
1235 num = ld->ld_Total - 1;
1236 goto doIt;
1238 case LISTV_Select_Next:
1239 if (!ld->ld_LastActive) num = ld->ld_Top;
1240 else num = ld->ld_LastNum + 1;
1241 goto doIt;
1243 case LISTV_Select_Previous:
1244 if (!ld->ld_LastActive) num = ld->ld_Top;
1245 else num = max((LONG)ld->ld_LastNum - 1, 0);
1246 goto doIt;
1248 case LISTV_Select_Top:
1249 num = ld->ld_Top;
1250 goto doIt;
1252 case LISTV_Select_Page_Up:
1253 if (!ld->ld_LastActive) num = ld->ld_Top;
1254 else
1256 num = ld->ld_LastNum;
1257 if (num > ld->ld_Top) num = ld->ld_Top;
1258 else num = max((LONG)(num - ld->ld_Visible + 1), 0);
1260 goto doIt;
1262 case LISTV_Select_Page_Down:
1263 if (!ld->ld_LastActive ) num = ld->ld_Top;
1264 else
1266 num = ld->ld_LastNum;
1267 if (num < (ld->ld_Top + ld->ld_Visible - 1)) num = ld->ld_Top + ld->ld_Visible - 1;
1268 else num = min((LONG)(num + (ld->ld_Visible - 1)), ld->ld_Total - 1);
1270 goto doIt;
1272 default:
1273 num = data;
1275 doIt:
1278 * Make sure we stay in range.
1280 num = (num >= ld->ld_Total) ? ld->ld_Total - 1 : num;
1283 * Find the node to select.
1285 if (lve = ld->ld_LastActive = FindNodeQuick(ld, num))
1288 * Setup the number as the last
1289 * selected one.
1291 ld->ld_LastNum = num;
1294 * Already selected?
1296 if (((( tag->ti_Tag != LISTV_SelectMulti ) && ( tag->ti_Tag != LISTV_SelectMultiNotVisible )) || ! ( lve->lve_Flags & LVEF_SELECTED )) && ( ! ( ld->ld_Flags & LDF_READ_ONLY ))) {
1298 * No? DeSelect all other labels if we are not
1299 * multi-selecting.
1301 if (( tag->ti_Tag != LISTV_SelectMulti ) && ( tag->ti_Tag != LISTV_SelectMultiNotVisible ))
1302 DeSelect( ld );
1305 * Mark this entry as selected.
1307 lve->lve_Flags |= LVEF_SELECTED | LVEF_REFRESH;
1310 * Notify change.
1312 DoNotifyMethod( obj, opu->opu_GInfo, opu->MethodID == OM_UPDATE ? opu->opu_Flags : 0L, GA_ID, GADGET( obj )->GadgetID, LISTV_Entry, lve->lve_Entry, LISTV_EntryNumber, ld->ld_LastNum, TAG_END );
1313 vc = TRUE;
1316 * Make the entry visible if requested.
1318 if (( tag->ti_Tag != LISTV_SelectNotVisible ) && ( tag->ti_Tag != LISTV_SelectMultiNotVisible ))
1319 ntop = MakeVisible( ld, num );
1321 break;
1323 break;
1325 case LISTV_ColumnWeights:
1326 if (!(ld->ld_Flags & LDF_DRAGGING_COLUMN))
1328 new_weights = (ULONG *)data;
1329 for (i = 0; i < ld->ld_Columns; i++)
1331 ld->ld_CD[i].cd_Weight = new_weights ? (new_weights[i] ? new_weights[i] : 1) : DEFAULT_WEIGHT;
1333 ld->ld_Flags &= ~LDF_OFFSETS_VALID;
1334 vc = TRUE;
1336 break;
1338 case LISTV_Columns:
1339 ld->ld_Columns = oldcol; // can't change yet
1340 break;
1342 case BT_ParentWindow:
1343 case BT_ParentView:
1344 DoSetMethodNG(ld->ld_Prop, tag->ti_Tag, data, TAG_DONE);
1345 break;
1350 * Disabled state changed?
1352 if ((GADGET(obj)->Flags & GFLG_DISABLED) != dis)
1354 if ( opu->opu_GInfo ) {
1355 ld->ld_Flags |= LDF_REFRESH_ALL;
1356 DoRenderMethod( obj, opu->opu_GInfo, GREDRAW_REDRAW );
1357 vc = FALSE;
1362 * Top value changed?
1364 if (vc)
1366 DoRenderMethod(obj, opu->opu_GInfo, GREDRAW_UPDATE);
1367 if (ntop != otop)
1369 NewTop(ld, opu->opu_GInfo, obj, ntop);
1370 if (ld->ld_Prop) DoSetMethod(ld->ld_Prop, opu->opu_GInfo, PGA_Top, ntop, TAG_DONE);
1373 else if (ntop != otop)
1375 if (!FindTagItem(GA_ID, opu->opu_AttrList))
1376 if (ld->ld_Prop) DoSetMethod(ld->ld_Prop, opu->opu_GInfo, PGA_Top, ntop, TAG_END);
1377 NewTop(ld, opu->opu_GInfo, obj, ntop);
1380 return 1;
1382 METHOD_END
1384 /// OM_GET
1386 * They want to know something.
1388 METHOD(ListClassGet, struct opGet *, opg)
1390 LD *ld = INST_DATA( cl, obj );
1391 ULONG rc = TRUE, num = ~0;
1392 ULONG tag = opg->opg_AttrID, *store = opg->opg_Storage;
1394 switch (tag)
1396 case LISTV_LastClicked:
1397 if (ld->ld_LastActive) STORE ld->ld_LastActive->lve_Entry;
1398 else STORE 0;
1399 break;
1401 case LISTV_LastClickedNum:
1402 if (ld->ld_LastActive)
1403 num = ld->ld_LastNum;
1404 STORE num;
1405 break;
1407 case LISTV_NumEntries:
1408 STORE ld->ld_Total;
1409 break;
1411 case LISTV_DropSpot:
1412 STORE ld->ld_DropSpot;
1413 break;
1415 case LISTV_NewPosition:
1416 STORE ld->ld_NewPos;
1417 break;
1419 case LISTV_ViewBounds:
1420 STORE &ld->ld_InnerBox;
1421 break;
1423 case LISTV_LastColumn:
1424 STORE ld->ld_LastCol;
1425 break;
1427 default:
1428 rc = BGUI_UnpackStructureTag((UBYTE *)ld, ListPackTable, tag, store);
1429 if (!rc) rc = AsmDoSuperMethodA(cl, obj, (Msg)opg);
1430 break;
1432 return rc;
1434 METHOD_END
1436 /// OM_DISPOSE
1438 * Dispose of the object.
1440 METHOD(ListClassDispose, Msg, msg)
1442 LD *ld = INST_DATA(cl, obj);
1443 LVE *lve;
1444 struct lvResource lvr;
1447 * Free all entries.
1449 while (lve = (LVE *)RemHead((struct List *)&ld->ld_Entries))
1452 * Do we have a resource hook?
1454 if (ld->ld_Resource)
1457 * Initialize lvResource structure to kill the entry.
1459 lvr.lvr_Command = LVRC_KILL;
1460 lvr.lvr_Entry = lve->lve_Entry;
1463 * Call the hook.
1465 BGUI_CallHookPkt(ld->ld_Resource, (VOID *)obj, (VOID *)&lvr);
1467 else
1470 * Simple deallocation.
1472 BGUI_FreePoolMem(lve->lve_Entry);
1476 * Free the entry node.
1478 BGUI_FreePoolMem(lve);
1482 * Kill the scroller.
1484 if (ld->ld_Prop) AsmDoMethod(ld->ld_Prop, OM_DISPOSE);
1486 if (ld->ld_CD) BGUI_FreePoolMem(ld->ld_CD);
1488 if (ld->ld_Font) BGUI_CloseFont(ld->ld_Font);
1491 * The superclass takes care of the rest.
1493 return AsmDoSuperMethodA(cl, obj, msg);
1495 METHOD_END
1498 /// ListAreaBounds
1500 * Setup the list area bounds.
1502 //STATIC ASM VOID ListAreaBounds(REG(a0) Object *obj, REG(a1) LD *ld)
1503 STATIC ASM REGFUNC2(VOID, ListAreaBounds,
1504 REGPARAM(A0, Object *, obj),
1505 REGPARAM(A1, LD *, ld))
1507 int fh = ld->ld_EntryHeight;
1508 int overhead;
1510 if (ld->ld_ListArea.Width != ld->ld_InnerBox.Width)
1511 ld->ld_Flags &= ~LDF_OFFSETS_VALID;
1514 * Setup list display area and view bounds.
1516 ld->ld_ListArea = ld->ld_InnerBox;
1519 * Title?
1521 if (ld->ld_Title || ld->ld_TitleHook)
1523 ld->ld_ListArea.Top += (fh + 2);
1524 ld->ld_ListArea.Height -= (fh + 2);
1528 * Setup the amount of visible entries
1529 * and the amount of pixels to adjust.
1531 ld->ld_Visible = ld->ld_ListArea.Height / fh;
1532 overhead = ld->ld_ListArea.Height % fh;
1535 * If the list area is larger than the
1536 * total amount of entries we set overhead
1537 * to 0.
1539 if (ld->ld_Total <= ld->ld_Visible)
1541 overhead = 0;
1543 else
1545 ld->ld_ListArea.Top += overhead / 2;
1546 ld->ld_ListArea.Height -= overhead;
1550 * Any change in the overhead?
1552 if (overhead != ld->ld_Overhead)
1554 ld->ld_Overhead = overhead;
1555 ld->ld_Flags |= LDF_REFRESH_ALL;
1559 * Recompute the columns.
1561 if (!(ld->ld_Flags & LDF_OFFSETS_VALID))
1562 GetColumnPositions(obj, ld);
1565 * Check top setting.
1567 ld->ld_Top = max(0, min(ld->ld_Top, ld->ld_Total - ld->ld_Visible));
1570 * Get top entry.
1572 ld->ld_TopEntry = FindNode(ld, ld->ld_Top);
1575 REGFUNC_END
1577 /// RenderColumn
1579 //STATIC ASM SAVEDS VOID RenderColumn(REG(a0) char *text, REG(a2) Object *obj, REG(a1) struct lvRender *lvr)
1580 STATIC ASM SAVEDS REGFUNC3(VOID, RenderColumn,
1581 REGPARAM(A0, char *, text),
1582 REGPARAM(A2, Object *, obj),
1583 REGPARAM(A1, struct lvRender *, lvr))
1585 int col = lvr->lvr_Column;
1586 struct BaseInfo *bi;
1587 struct IBox area;
1588 char *term = NULL;
1591 * Setup rendering area.
1593 area.Left = lvr->lvr_Bounds.MinX + 2;
1594 area.Width = lvr->lvr_Bounds.MaxX - lvr->lvr_Bounds.MinX - 3;
1595 area.Top = lvr->lvr_Bounds.MinY;
1596 area.Height = lvr->lvr_Bounds.MaxY - lvr->lvr_Bounds.MinY + 1;
1598 if (strchr(text, '\t'))
1600 while (col--)
1602 text = strchr(text, '\t');
1603 if (text) text++;
1604 else break;
1606 if (text)
1608 term = strchr(text, '\t');
1609 if (term) *term = 0;
1613 #ifdef DEBUG_BGUI
1614 if (bi = AllocBaseInfoDebug(__FILE__,__LINE__,BI_DrawInfo, lvr->lvr_DrawInfo, BI_RastPort, lvr->lvr_RPort, TAG_DONE))
1615 #else
1616 if (bi = AllocBaseInfo(BI_DrawInfo, lvr->lvr_DrawInfo, BI_RastPort, lvr->lvr_RPort, TAG_DONE))
1617 #endif
1620 * Render entry.
1622 if (text)
1624 RenderText(bi, text, &area);
1625 if (term) *term = '\t';
1628 if (GADGET(obj)->Flags & GFLG_DISABLED)
1630 area.Left -= 2;
1631 area.Width += 4;
1633 * Disable it.
1635 BDisableBox(bi, &area);
1637 FreeBaseInfo(bi);
1640 REGFUNC_END
1642 /// RenderEntry
1644 * Render a single entry.
1646 STATIC VOID RenderEntry(Object *obj, LD *ld, struct BaseInfo *bi, LVE *lve, ULONG top)
1648 BC *bc = BASE_DATA(obj);
1649 struct lvRender lvr;
1650 UBYTE *txt;
1651 UWORD col;
1652 int cx, cw, xoff, yoff;
1653 BOOL sel, clear;
1654 struct Hook *hook;
1655 struct RastPort *rp = bi->bi_RPort;
1657 if (top & REL_ZERO)
1659 top &= ~REL_ZERO;
1660 xoff = yoff = 0;
1662 else
1664 xoff = ld->ld_ListArea.Left;
1665 yoff = ld->ld_ListArea.Top;
1669 * Initialize the lvRender structure.
1671 lvr.lvr_RPort = rp;
1672 lvr.lvr_DrawInfo = bi->bi_DrInfo;
1674 if (lve)
1676 sel = lve->lve_Flags & LVEF_SELECTED;
1677 hook = ld->ld_Display;
1679 lvr.lvr_Entry = lve->lve_Entry;
1680 lvr.lvr_Bounds.MinY = yoff + ld->ld_EntryHeight * top;
1681 lvr.lvr_Bounds.MaxY = lvr.lvr_Bounds.MinY + ld->ld_EntryHeight - 1;
1683 else
1686 * A NULL entry means render the title.
1688 sel = FALSE;
1689 hook = ld->ld_TitleHook;
1691 lvr.lvr_Entry = ld->ld_Title;
1692 lvr.lvr_Bounds.MinY = bc->bc_InnerBox.Top;
1693 lvr.lvr_Bounds.MaxY = bc->bc_InnerBox.Top + ld->ld_EntryHeight - 1;
1697 * Setup state information.
1699 if (GADGET(obj)->Flags & GFLG_DISABLED)
1700 lvr.lvr_State = sel ? LVRS_SELECTED_DISABLED : LVRS_NORMAL_DISABLED;
1701 else
1702 lvr.lvr_State = sel ? LVRS_SELECTED : LVRS_NORMAL;
1704 for (col = 0, cx = xoff; col < ld->ld_Columns; col++)
1706 if (!(ld->ld_CD[col].cd_Flags & LVCF_HIDDEN))
1708 cw = ld->ld_CD[col].cd_Width;
1710 lvr.lvr_Bounds.MinX = cx;
1711 lvr.lvr_Bounds.MaxX = cx + cw - ((col < (ld->ld_Columns - 1)) ? 3 : 1);
1712 lvr.lvr_Column = col;
1714 cx += cw;
1716 if (!(ld->ld_Flags & LDF_ONE_COLUMN) || (col == ld->ld_OneColumn))
1719 * Do we have a display hook?
1721 if (hook)
1723 clear = (ld->ld_Flags & LDF_PRE_CLEAR) || (ld->ld_CD[col].cd_Flags & LVCF_PRECLEAR);
1725 if (clear)
1727 AsmDoMethod(ld->ld_Frame, FRAMEM_BACKFILL, bi, &lvr.lvr_Bounds, sel ? IDS_SELECTED : IDS_NORMAL);
1728 clear = FALSE;
1731 * Call the hook.
1733 txt = (UBYTE *)BGUI_CallHookPkt(hook, (void *)obj, (void *)&lvr);
1735 else
1738 * Pick up the entry text.
1740 clear = TRUE;
1741 txt = lvr.lvr_Entry;
1743 if (txt)
1745 if (clear)
1747 AsmDoMethod(ld->ld_Frame, FRAMEM_BACKFILL, bi, &lvr.lvr_Bounds, sel ? IDS_SELECTED : IDS_NORMAL);
1749 BSetDPenA(bi, sel ? FILLTEXTPEN : TEXTPEN);
1750 RenderColumn(txt, obj, &lvr);
1756 * Done this one.
1758 if (lve) lve->lve_Flags &= ~LVEF_REFRESH;
1761 /// BASE_LAYOUT
1763 * Render the list.
1765 METHOD(ListClassLayout, struct bmLayout *, bml)
1767 LD *ld = INST_DATA(cl, obj);
1768 BC *bc = BASE_DATA(obj);
1769 struct TextAttr *lf;
1770 int sw = 0;
1771 ULONG rc;
1773 if (ld->ld_Prop)
1775 lf = ld->ld_ListFont ? ld->ld_ListFont : bc->bc_TextAttr;
1776 if (lf)
1778 sw = max((lf->ta_YSize * 2) / 3, 16);
1780 else
1782 sw = 16;
1785 * Setup offset.
1787 bml->bml_Bounds->Width -= sw;
1791 * First the superclass.
1793 rc = AsmDoSuperMethodA(cl, obj, (Msg)bml);
1795 if (sw) bml->bml_Bounds->Width += sw;
1797 return rc;
1799 METHOD_END
1801 /// BASE_RENDER
1803 * Render the list.
1805 METHOD(ListClassRender, struct bmRender *, bmr)
1807 LD *ld = INST_DATA(cl, obj);
1808 BC *bc = BASE_DATA(obj);
1809 struct BaseInfo *bi = bmr->bmr_BInfo;
1810 struct RastPort *rp = bi->bi_RPort;
1811 struct TextFont *tf = ld->ld_Font, *of;
1812 struct TextAttr *lf = ld->ld_ListFont;
1813 struct Rectangle rect;
1814 ULONG num, a;
1815 LVE *lve;
1816 int x, y, w, h, sw;
1817 UWORD overhead;
1820 * Render the baseclass.
1822 AsmDoSuperMethodA(cl, obj, (Msg)bmr);
1824 if (!lf) lf = bc->bc_TextAttr;
1826 sw = lf ? ((lf->ta_YSize * 2) / 3) : 0;
1827 if (sw < 16) sw = 16;
1830 * Setup the font.
1832 of = rp->Font;
1833 if (!tf)
1835 if (lf)
1837 if (tf = BGUI_OpenFont(lf))
1839 ld->ld_Font = tf;
1840 BSetFont(bi, tf);
1842 else
1844 tf = of;
1847 else
1849 tf = of;
1854 * Setup entry height always even.
1856 ld->ld_EntryHeight = (tf->tf_YSize + 1) & (ULONG)~1;
1858 overhead=ld->ld_Overhead;
1861 * Setup the list area bounds.
1863 ListAreaBounds(obj, ld);
1865 x = bc->bc_InnerBox.Left;
1866 w = bc->bc_InnerBox.Width;
1869 * Complete redraw?
1871 if (overhead<ld->ld_Overhead
1872 || bmr->bmr_Flags == GREDRAW_REDRAW)
1874 if(overhead<ld->ld_Overhead)
1875 overhead=ld->ld_Overhead;
1878 * Clear the overhead.
1880 if (h = overhead >> 1)
1882 y = bc->bc_InnerBox.Top;
1883 if (ld->ld_Title || ld->ld_TitleHook) y += ld->ld_EntryHeight+2;
1885 rect.MinX = x; rect.MaxX = x + w - 1;
1886 rect.MinY = y; rect.MaxY = y + h - 1;
1888 AsmDoMethod(bc->bc_Frame, FRAMEM_BACKFILL, bi, &rect, IDS_NORMAL);
1891 if (h = overhead - h)
1893 y = bc->bc_InnerBox.Top + bc->bc_InnerBox.Height - h;
1895 rect.MinX = x; rect.MaxX = x + w - 1;
1896 rect.MinY = y; rect.MaxY = y + h - 1;
1898 AsmDoMethod(bc->bc_Frame, FRAMEM_BACKFILL, bi, &rect, IDS_NORMAL);
1902 * Draw the separators.
1904 ColumnSeparators(ld, bi, bc->bc_InnerBox.Left, bc->bc_InnerBox.Top, bc->bc_InnerBox.Height);
1907 * If we have a title render it.
1909 if (ld->ld_Title || ld->ld_TitleHook)
1912 * Just in case the font changed.
1914 if (tf) BSetFont(bi, tf);
1916 RenderEntry(obj, ld, bi, NULL, 0);
1918 if (ld->ld_Columns > 1)
1920 y = bc->bc_InnerBox.Top + ld->ld_EntryHeight;
1921 BSetDPenA(bi, ld->ld_Flags & LDF_READ_ONLY ? SHINEPEN : SHADOWPEN);
1922 HLine(rp, x, y++, x + w - 1);
1923 BSetDPenA(bi, ld->ld_Flags & LDF_READ_ONLY ? SHADOWPEN : SHINEPEN);
1924 HLine(rp, x, y, x + w - 1);
1929 * Just do it.
1931 ld->ld_Flags |= LDF_REFRESH_ALL;
1935 * Just in case the font changed.
1937 if (tf) BSetFont(bi, tf);
1940 * Loop through the entries.
1942 for (num = ld->ld_Top, a = 0; (a < ld->ld_Visible) && (num < ld->ld_Total); num++, a++)
1945 * Render the entry.
1947 if (lve = FindNodeQuick(ld, num))
1950 * Only render when necessary.
1952 if ((ld->ld_Flags & LDF_REFRESH_ALL) || (lve->lve_Flags & LVEF_REFRESH))
1954 RenderEntry(obj, ld, bi, lve, a);
1960 * Disabled?
1962 if ((!(ld->ld_Flags & LDF_CUSTOMDISABLE)) && ld->ld_Display)
1964 if (GADGET(obj)->Flags & GFLG_DISABLED)
1966 BDisableBox(bi, &bc->bc_HitBox);
1971 * Done.
1973 ld->ld_Flags &= ~LDF_REFRESH_ALL;
1976 * Replace the font.
1978 BSetFont(bi, of);
1980 if (ld->ld_Prop)
1983 * Setup scroller bounds.
1985 GADGETBOX(ld->ld_Prop)->Left = ld->ld_HitBox.Left + ld->ld_HitBox.Width;
1986 GADGETBOX(ld->ld_Prop)->Top = ld->ld_HitBox.Top;
1987 GADGETBOX(ld->ld_Prop)->Width = sw;
1988 GADGETBOX(ld->ld_Prop)->Height = ld->ld_HitBox.Height;
1991 * Set top, total etc.
1993 DoSetMethodNG(ld->ld_Prop, PGA_Top, ld->ld_Top, PGA_Total, ld->ld_Total,
1994 PGA_Visible, ld->ld_Visible, TAG_DONE);
1997 * Re-render the scroller.
1999 AsmDoMethod(ld->ld_Prop, GM_RENDER, bi, rp, bmr->bmr_Flags);
2001 return 1;
2003 METHOD_END
2007 * Find out over which entry the mouse is located.
2009 //STATIC ASM LONG MouseOverEntry(REG(a0) LD *ld, REG(d0) LONG t)
2010 STATIC ASM REGFUNC2(LONG, MouseOverEntry,
2011 REGPARAM(A0, LD *, ld),
2012 REGPARAM(D0, LONG, t))
2014 t -= ld->ld_ListArea.Top;
2015 if (t < 0) return -1;
2017 t = (t / ld->ld_EntryHeight) + ld->ld_Top;
2019 if (t > ld->ld_Total) t = ld->ld_Total;
2021 return t;
2023 REGFUNC_END
2026 * Perform multi-(de)selection.
2028 //STATIC ASM BOOL MultiSelect( REG(a0) LD *ld, REG(d0) ULONG active )
2029 STATIC ASM REGFUNC2(BOOL, MultiSelect,
2030 REGPARAM(A0, LD *, ld),
2031 REGPARAM(D0, ULONG, active))
2033 LVE *node = FindNodeQuick( ld, ld->ld_MultiStart ), *anode = FindNodeQuick( ld, active );
2034 BOOL rc = FALSE;
2037 * Backward?
2039 if ( ld->ld_MultiStart > active ) {
2041 * Loop through the entries.
2043 for ( ; ; node = node->lve_Prev ) {
2045 * Select entries?
2047 if ( ! ld->ld_MultiMode ) {
2049 * Yes.
2051 if ( ! ( node->lve_Flags & LVEF_SELECTED )) {
2052 node->lve_Flags |= LVEF_SELECTED | LVEF_REFRESH;
2053 rc = TRUE;
2056 else
2058 if (node->lve_Flags & LVEF_SELECTED)
2060 node->lve_Flags &= ~LVEF_SELECTED;
2061 node->lve_Flags |= LVEF_REFRESH;
2062 rc = TRUE;
2066 * Done?
2068 if ( node == anode )
2069 return( rc );
2071 } else {
2073 * Loop through the entries.
2075 for ( ; ; node = node->lve_Next ) {
2077 * Select entries?
2079 if ( ! ld->ld_MultiMode ) {
2081 * Yes.
2083 if ( ! ( node->lve_Flags & LVEF_SELECTED )) {
2084 node->lve_Flags |= LVEF_SELECTED | LVEF_REFRESH;
2085 rc = TRUE;
2087 } else {
2088 if ( node->lve_Flags & LVEF_SELECTED ) {
2089 node->lve_Flags &= ~LVEF_SELECTED;
2090 node->lve_Flags |= LVEF_REFRESH;
2091 rc = TRUE;
2095 * Done?
2097 if ( node == anode )
2098 return( rc );
2101 return rc;
2103 REGFUNC_END
2104 /// GM_HITTEST
2106 * Test if the gadget was hit.
2108 METHOD(ListClassHitTest, struct gpHitTest *, gph)
2110 LD *ld = INST_DATA(cl, obj);
2111 BC *bc = BASE_DATA(obj);
2112 ULONG rc = 0;
2115 * Get absolute click position.
2117 WORD l = GADGET(obj)->LeftEdge + gph->gpht_Mouse.X;
2118 WORD t = GADGET(obj)->TopEdge + gph->gpht_Mouse.Y;
2120 if (PointInBox(&bc->bc_InnerBox, l, t))
2122 * Hit inside the list area?
2124 return GMR_GADGETHIT;
2126 if (ld->ld_Prop)
2129 * Route the message.
2131 rc = ForwardMsg(obj, ld->ld_Prop, (Msg)gph);
2133 if (rc == GMR_GADGETHIT)
2135 * Mark the scroller active.
2137 ld->ld_Flags |= LDF_PROPACTIVE;
2139 return rc;
2141 METHOD_END
2143 /// GM_GOACTIVE
2145 * They want us to go active.
2147 METHOD(ListClassGoActive, struct gpInput *, gpi)
2149 LD *ld = INST_DATA(cl, obj);
2150 LVE *lve;
2151 ULONG rc = GMR_NOREUSE, last = ld->ld_LastNum;
2152 int col, dx;
2153 struct GadgetInfo *gi = gpi->gpi_GInfo;
2156 * Get hit position.
2158 int x = gpi->gpi_Mouse.X;
2159 int y = gpi->gpi_Mouse.Y;
2160 int l = x + GADGET(obj)->LeftEdge;
2161 int t = y + GADGET(obj)->TopEdge;
2164 * We do not go active if we were activated by ActivateGadget().
2166 if (!gpi->gpi_IEvent)
2167 return GMR_NOREUSE;
2170 * Check for left mouse button down event
2172 if ((gpi->gpi_IEvent->ie_Class == IECLASS_RAWMOUSE) && (gpi->gpi_IEvent->ie_Code == SELECTDOWN))
2175 * Update column positions if required
2177 if (!(ld->ld_Flags & LDF_OFFSETS_VALID))
2178 GetColumnPositions(obj, ld);
2181 * Step through column positions array, marking a hit if
2182 * mouse is within 4 pixels either side of column separator.
2184 col = ld->ld_Columns - 1;
2185 while (col--)
2187 if (!(ld->ld_CD[col].cd_Flags & LVCF_HIDDEN))
2189 dx = x - ld->ld_CD[col + 1].cd_Offset;
2191 * If hit column separator, set Dragging to TRUE, record
2192 * drag column and initial drag position, draw first
2193 * drag line and return GMR_MEACTIVE.
2195 if ((dx >= -4) && (dx <= 4))
2198 * Check for column dragging enabled.
2200 if ((ld->ld_Flags & LDF_ALLOW_DRAG) || (ld->ld_CD[col].cd_Flags & LVCF_DRAGGABLE))
2202 ld->ld_Flags |= LDF_DRAGGING_COLUMN;
2203 ld->ld_DragColumn = col;
2204 ld->ld_DragXLine = ld->ld_CD[col+1].cd_Offset;
2205 DrawDragLine(ld, gi);
2207 return GMR_MEACTIVE;
2214 if (ld->ld_Prop)
2217 * Prop hit?
2219 if (ld->ld_Flags & LDF_PROPACTIVE)
2222 * Adjust coordinates and re-direct message.
2224 return ForwardMsg(obj, ld->ld_Prop, (Msg)gpi) & ~GMR_VERIFY;
2229 * Can we go active?
2231 if (( GADGET( obj )->Flags & GFLG_DISABLED ) || ( ld->ld_Flags & LDF_READ_ONLY ) || ( ld->ld_Flags & LDF_LIST_BUSY ))
2232 return( GMR_NOREUSE );
2235 * Get the entry which lays under the mouse.
2237 ld->ld_ActiveEntry = (ULONG)MouseOverEntry(ld, t);
2239 col = ld->ld_Columns;
2240 while (--col)
2242 if (x >= ld->ld_CD[col].cd_Offset)
2243 break;
2245 ld->ld_LastCol = col;
2248 * Is it in range?
2250 if (ld->ld_ActiveEntry < ld->ld_Total)
2252 if (lve = ld->ld_LastActive = FindNodeQuick(ld, ld->ld_ActiveEntry))
2254 ld->ld_LastNum = ld->ld_ActiveEntry;
2256 * If we are not a multi-select object we
2257 * de-select all entries. Otherwise we mark the
2258 * entry which initiated the multi-(de)select.
2260 if (!(ld->ld_Flags & LDF_MULTI_SELECT)) DeSelect(ld);
2261 else {
2263 * De-select entries if shift isn't down.
2265 if (!(ld->ld_Flags & LDF_NOSHIFT))
2267 if (!(gpi->gpi_IEvent->ie_Qualifier & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT)))
2268 DeSelect(ld);
2272 * Multi-selection. When MultiMode is 1 we need
2273 * to multi-deselect. Otherwise we multi-select.
2275 if (lve->lve_Flags & LVEF_SELECTED) ld->ld_MultiMode = 1;
2276 else ld->ld_MultiMode = 0;
2279 * Setup starting position.
2281 ld->ld_MultiStart = ld->ld_ActiveEntry;
2284 * Select entry if necessary.
2286 if ( ! ld->ld_MultiMode ) {
2288 * Note the time we got clicked.
2290 CurrentTime( &ld->ld_Secs[ 0 ], &ld->ld_Mics[ 0 ] );
2291 lve->lve_Flags |= LVEF_SELECTED | LVEF_REFRESH;
2292 } else {
2294 * De-selection only in multi-mode.
2296 if ( ld->ld_Flags & LDF_MULTI_SELECT ) {
2298 * The same selection as the previous?
2300 if ( ld->ld_ActiveEntry == last ) {
2302 * Yes, time it.
2304 CurrentTime( &ld->ld_Secs[ 1 ], &ld->ld_Mics[ 1 ] );
2307 * Double clicked the selection?
2309 if ( ! DoubleClick( ld->ld_Secs[ 0 ], ld->ld_Mics[ 0 ], ld->ld_Secs[ 1 ], ld->ld_Mics[ 1 ] )) {
2311 * No, deselect it.
2313 lve->lve_Flags &= ~LVEF_SELECTED;
2314 lve->lve_Flags |= LVEF_REFRESH;
2316 } else {
2318 * Deselect the entry.
2320 lve->lve_Flags &= ~LVEF_SELECTED;
2321 lve->lve_Flags |= LVEF_REFRESH;
2323 } else
2324 lve->lve_Flags |= LVEF_SELECTED | LVEF_REFRESH;
2327 * Notify & Re-render.
2329 DoNotifyMethod(obj, gi, OPUF_INTERIM, GA_ID, GADGET(obj)->GadgetID, LISTV_Entry, lve->lve_Entry,
2330 LISTV_EntryNumber, ld->ld_LastNum, LISTV_LastColumn, ld->ld_LastCol, TAG_DONE);
2331 DoRenderMethod(obj, gi, GREDRAW_UPDATE );
2334 * Setup any drag and drop buffers we may need.
2336 if ( AsmDoSuperMethodA( cl, obj, ( Msg )gpi ) == GMR_MEACTIVE )
2337 ld->ld_Flags |= LDF_DRAGGABLE;
2339 rc = GMR_MEACTIVE;
2341 else if (ld->ld_ActiveEntry == (ULONG)-1)
2344 * Notify.
2346 DoNotifyMethod(obj, gi, OPUF_INTERIM, GA_ID, GADGET(obj)->GadgetID, LISTV_Entry, -1,
2347 LISTV_EntryNumber, -1, LISTV_LastColumn, ld->ld_LastCol, TAG_DONE);
2349 return rc;
2351 METHOD_END
2353 /// GM_HANDLEINPUT
2355 * Handle user input.
2357 METHOD(ListClassHandleInput, struct gpInput *, gpi)
2359 LD *ld = INST_DATA(cl, obj);
2360 LVE *lve;
2361 struct gpInput hit;
2362 int vc, xmin, xmax, dx;
2363 ULONG rc = GMR_MEACTIVE, otop = ld->ld_Top, ntop = ld->ld_Top;
2364 LONG nc;
2365 int dcol = ld->ld_DragColumn;
2366 int totalwidth, totalweight;
2367 struct GadgetInfo *gi = gpi->gpi_GInfo;
2368 CD *cd, *cd2, *cd3;
2372 * Get mouse position.
2374 int x = gpi->gpi_Mouse.X;
2375 int y = gpi->gpi_Mouse.Y;
2376 int l = x + GADGET(obj)->LeftEdge;
2377 int t = y + GADGET(obj)->TopEdge;
2380 * List in use?
2382 if (ld->ld_Flags & LDF_LIST_BUSY)
2383 return rc;
2386 * Dragging column?
2388 if (ld->ld_Flags & LDF_DRAGGING_COLUMN)
2390 cd = ld->ld_CD + dcol;
2391 cd2 = cd + 1;
2392 cd3 = cd + 2;
2395 * Return this by default, implying we want to
2396 * remain the active gadget.
2398 rc = GMR_MEACTIVE;
2401 * If this is a mouse event (movement or button)
2404 if (gpi->gpi_IEvent->ie_Class == IECLASS_RAWMOUSE)
2407 * Update column offsets if required
2409 if (!(ld->ld_Flags & LDF_OFFSETS_VALID))
2410 GetColumnPositions(obj, ld);
2413 * gpi_Mouse.X has mouse-x relative to left side of gadget
2414 * hitbox. Set minimum and maximum values for x at positions
2415 * of columns either side of the one being dragged. Add a
2416 * minimum column width to those limits as well.
2418 xmin = cd->cd_Offset + cd->cd_MinWidth;
2419 xmax = min(cd->cd_Offset + cd->cd_MaxWidth, cd3->cd_Offset - cd2->cd_MinWidth);
2421 if (xmax < xmin) /* just in case column is */
2422 { /* already very narrow, */
2423 xmin = xmax = cd2->cd_Offset; /* stop user from adjusting it */
2427 * Prevent dragline from wandering outside of limits.
2429 if (x < xmin) x = xmin;
2430 if (x > xmax) x = xmax;
2433 * Don't update if dragline position hasn't changed.
2436 if (x != ld->ld_DragXLine)
2439 * Reposition dragline by drawing again at old position
2440 * using complement mode, then drawing at new position.
2443 DrawDragLine(ld, gi);
2444 ld->ld_DragXLine = x;
2445 DrawDragLine(ld, gi);
2449 * Check for left mouse button release: implies user
2450 * has stopped dragging column and it's time to recalculate
2451 * the column weights.
2453 if (gpi->gpi_IEvent->ie_Code == SELECTUP)
2455 rc = GMR_NOREUSE; /* we will use LMB-up event */
2458 * No need to do anything if column position not changed.
2460 if (dx = x - cd2->cd_Offset)
2463 * Set new column position at x.
2465 cd2->cd_Offset = x;
2466 cd2->cd_Width -= dx;
2467 cd->cd_Width += dx;
2470 * Set new weights for dragged column and the one to the
2471 * right of it, by redistributing the total of their
2472 * original weights in the ratio of their new positions
2473 * over the original total distance between them. Both
2474 * total weight and total distance remain unchanged.
2476 totalweight = cd->cd_Weight + cd2->cd_Weight;
2477 totalwidth = cd3->cd_Offset - cd->cd_Offset;
2479 cd2->cd_Weight = ((cd3->cd_Offset - x) * totalweight) / totalwidth;
2480 cd->cd_Weight = totalweight - cd2->cd_Weight;
2483 * If we have a GadgetInfo, invoke GM_RENDER
2484 * to update gadget visuals.
2487 if (gi)
2489 DoRenderMethod(obj, gi, GREDRAW_REDRAW);
2492 * No need for GoInactive() to erase dragline:
2494 ld->ld_Flags |= LDF_NEW_COLUMN_POS;
2497 } /* endif posn changed */
2499 } /* endif LMB down */
2501 * If event was menu button down, abandon dragging
2502 * and let Intuition activate menus.
2505 if (gpi->gpi_IEvent->ie_Code == MENUDOWN)
2506 rc = GMR_REUSE;
2508 } /* endif mouse event */
2511 * If event was either shift key going down, abandon
2512 * dragging as per BGUI standard.
2515 if (gpi->gpi_IEvent->ie_Class == IECLASS_RAWKEY)
2517 if ((gpi->gpi_IEvent->ie_Code == 0x60) || (gpi->gpi_IEvent->ie_Code == 0x61))
2518 rc = GMR_REUSE;
2520 return rc;
2524 * Prop gadget active?
2526 if (ld->ld_Prop && (ld->ld_Flags & LDF_PROPACTIVE))
2529 * Adjust coordinates and route message.
2531 return ForwardMsg(obj, ld->ld_Prop, (Msg)gpi) & ~GMR_VERIFY;
2534 hit = *gpi;
2535 hit.MethodID = BASE_DRAGGING;
2537 nc = AsmDoMethodA(obj, (Msg)&hit);
2539 switch (nc)
2541 case BDR_DROP:
2542 DoNotifyMethod(obj, gi, 0, GA_ID, GADGET(obj)->GadgetID, LISTV_Entry, ld->ld_LastActive ? ld->ld_LastActive->lve_Entry : NULL, LISTV_EntryNumber, ld->ld_LastNum, TAG_END);
2543 rc = GMR_VERIFY;
2544 break;
2546 case BDR_CANCEL:
2547 rc = GMR_NOREUSE;
2548 break;
2550 case BDR_NONE:
2552 * Get the entry which lies under the mouse.
2554 nc = MouseOverEntry(ld, t);
2557 * There are only so much entries...
2559 if (nc < 0) nc = ld->ld_Top - 1;
2560 if (nc < 0) nc = 0;
2561 if (nc >= ld->ld_Total) nc = ld->ld_Total - 1;
2564 * Let's see what we got...
2566 if (gpi->gpi_IEvent->ie_Class == IECLASS_RAWMOUSE)
2569 * We do not support drag-selection when we
2570 * are in drag and drop mode.
2572 if (!(ld->ld_Flags & LDF_DRAGGABLE))
2575 * We only respond when:
2576 * A) The entry under the mouse changed.
2577 * B) The entry under the mouse is in the visible area.
2579 if ((nc != ld->ld_ActiveEntry) && (nc >= ld->ld_Top) && (nc < (ld->ld_Top + ld->ld_Visible)))
2582 * Mark the new entry.
2584 ld->ld_ActiveEntry = nc;
2587 * Get the node.
2589 if (lve = ld->ld_LastActive = FindNodeQuick(ld, nc))
2591 ld->ld_LastNum = nc;
2593 * Are we a multi-select object?
2595 if (!(ld->ld_Flags & LDF_MULTI_SELECT))
2598 * No. Deselect other entries.
2600 DeSelect(ld);
2603 * We need a visual change.
2605 vc = TRUE;
2608 * Select entry.
2610 lve->lve_Flags |= LVEF_SELECTED | LVEF_REFRESH;
2611 } else
2613 * Do a multi-(de)select.
2615 vc = MultiSelect( ld, nc );
2618 * Update visuals if necessary.
2620 if (vc) DoRenderMethod(obj, gi, GREDRAW_UPDATE);
2621 DoNotifyMethod(obj, gi, OPUF_INTERIM, GA_ID, GADGET(obj)->GadgetID, LISTV_Entry, lve->lve_Entry, LISTV_EntryNumber, ld->ld_LastNum, TAG_END );
2627 * What code do we have...
2629 switch (gpi->gpi_IEvent->ie_Code)
2631 case SELECTUP:
2633 * Releasing the left button
2634 * de-activates the object.
2636 rc = GMR_NOREUSE | GMR_VERIFY;
2637 CurrentTime(&ld->ld_Secs[0], &ld->ld_Mics[0]);
2638 DoNotifyMethod(obj, gi, 0, GA_ID, GADGET(obj)->GadgetID, LISTV_Entry, ld->ld_LastActive->lve_Entry, LISTV_EntryNumber, ld->ld_LastNum, TAG_END );
2639 break;
2641 case MENUDOWN:
2643 * Reuse menu events.
2645 rc = GMR_REUSE | GMR_VERIFY;
2646 DoNotifyMethod(obj, gi, 0, GA_ID, GADGET(obj)->GadgetID, LISTV_Entry, ld->ld_LastActive->lve_Entry, LISTV_EntryNumber, ld->ld_LastNum, TAG_END );
2647 break;
2650 else if (gpi->gpi_IEvent->ie_Class == IECLASS_TIMER)
2653 * When the mouse is moved above or below
2654 * the visible area the entries scroll up
2655 * or down using timer events for a delay.
2657 if ((nc != ld->ld_ActiveEntry) && (!(ld->ld_Flags & LDF_DRAGGABLE)))
2660 * When the active entry is located before
2661 * the top entry we scroll up one entry. When the
2662 * entry is located after the last visible entry
2663 * we scroll down one entry.
2665 vc = FALSE;
2666 if (nc >= ntop + ld->ld_Visible)
2668 nc = ntop++ + ld->ld_Visible;
2669 vc = TRUE;
2671 else if (nc < ntop)
2673 nc = --ntop;
2674 vc = TRUE;
2677 if (vc)
2680 * Set the new entry.
2682 ld->ld_LastNum = ld->ld_ActiveEntry = nc;
2685 * Find the entry.
2687 if (lve = ld->ld_LastActive = FindNodeQuick(ld, nc))
2690 * Are we a multi-select object?
2692 if (ld->ld_Flags & LDF_MULTI_SELECT)
2695 * Do a multi-(de)select.
2697 vc = MultiSelect(ld, nc);
2699 else
2702 * No. Deselect all entries.
2704 DeSelect(ld);
2706 * Select the entry.
2708 lve->lve_Flags |= LVEF_SELECTED | LVEF_REFRESH;
2710 * We need a visual change.
2712 vc = TRUE;
2716 * Update visuals when necessary.
2718 if (vc || (otop != ntop))
2721 * Top changed?
2723 DoRenderMethod(obj, gi, GREDRAW_UPDATE);
2724 if (otop != ntop)
2726 NewTop(ld, gi, obj, ntop);
2727 if (ld->ld_Prop)
2728 DoSetMethod(ld->ld_Prop, gi, PGA_Top, ntop, TAG_END);
2730 DoNotifyMethod(obj, gi, OPUF_INTERIM, GA_ID, GADGET(obj)->GadgetID, LISTV_Entry, lve->lve_Entry, LISTV_EntryNumber, ld->ld_LastNum, TAG_END);
2736 break;
2738 return rc;
2740 METHOD_END
2742 /// GM_GOINACTIVE
2744 * Go inactive.
2746 METHOD(ListClassGoInActive, struct gpGoInactive *, ggi)
2748 LD *ld = INST_DATA(cl, obj);
2750 if (ld->ld_Flags & LDF_DRAGGING_COLUMN)
2752 if (!(ld->ld_Flags & LDF_NEW_COLUMN_POS))
2753 DrawDragLine(ld, ggi->gpgi_GInfo);
2755 ld->ld_Flags &= ~(LDF_DRAGGING_COLUMN|LDF_NEW_COLUMN_POS);
2756 return 0;
2758 else
2761 * Clear draggable bit.
2763 ld->ld_Flags &= ~LDF_DRAGGABLE;
2765 if (ld->ld_Prop)
2768 * If the scroller was active pass this message on for compatibility reasons.
2770 if (ld->ld_Flags & LDF_PROPACTIVE)
2773 * Mark the scroller as not active.
2775 ld->ld_Flags &= ~LDF_PROPACTIVE;
2777 return AsmDoMethodA(ld->ld_Prop, (Msg)ggi);
2780 return AsmDoSuperMethodA(cl, obj, (Msg)ggi);
2783 METHOD_END
2785 /// BASE_DIMENSIONS
2787 * They want our minimum dimensions.
2789 METHOD(ListClassDimensions, struct bmDimensions *, bmd)
2791 LD *ld = INST_DATA(cl, obj);
2792 struct BaseInfo *bi = bmd->bmd_BInfo;
2793 UWORD my, mx = 0, mpx = 0, mpy = 0;
2794 int th = ld->ld_EntryHeight, col;
2795 struct TextAttr *lf = ld->ld_ListFont;
2798 * First the prop...
2800 if (ld->ld_Prop) AsmDoMethod(ld->ld_Prop, GRM_DIMENSIONS, bi, bi->bi_RPort, &mpx, &mpy, 0);
2802 if (!lf)
2805 * Pickup superclass font.
2807 Get_SuperAttr(cl, obj, BT_TextAttr, &lf);
2810 * Setup entry height always even.
2812 ld->ld_EntryHeight = (lf->ta_YSize + 1) & (ULONG)~1;
2815 * Calculate minimum x&y size.
2817 my = ld->ld_EntryHeight * ld->ld_MinShown;
2819 for (col = 0; col < ld->ld_Columns; col++)
2821 mx += ld->ld_CD[col].cd_MinWidth;
2825 * The listview grows when a title
2826 * hook is specified.
2828 if (ld->ld_Title)
2829 my += th + 4;
2831 mpx += mx;
2832 if (my > mpy) mpy = my;
2835 * Set it.
2837 return CalcDimensions(cl, obj, bmd, mpx, mpy);
2839 METHOD_END
2841 /// WM_KEYACTIVE
2843 * Key activation.
2845 //STATIC ASM ULONG ListClassKeyActive( REG(a0) Class *cl, REG(a2) Object *obj, REG(a1) struct wmKeyInput *wmki )
2846 STATIC ASM REGFUNC3(ULONG, ListClassKeyActive,
2847 REGPARAM(A0, Class *, cl),
2848 REGPARAM(A2, Object *, obj),
2849 REGPARAM(A1, struct wmKeyInput *, wmki))
2851 LD *ld = ( LD * )INST_DATA( cl, obj );
2852 UWORD qual = wmki->wmki_IEvent->ie_Qualifier;
2853 LONG nnum = 0, otop = ld->ld_Top, newtop = otop;
2854 LVE *node;
2855 BOOL sel = FALSE;
2858 * Read-only?
2860 if ( ld->ld_Flags & LDF_READ_ONLY ) {
2862 * Shifted scrolls up.
2864 if ( qual & ( IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT )) {
2865 if ( newtop ) newtop--;
2866 } else {
2867 if ( newtop < ( ld->ld_Total - ld->ld_Visible )) newtop++;
2871 * Top changed?
2873 if ( newtop != otop )
2874 DoSetMethod( obj, wmki->wmki_GInfo, LISTV_Top, newtop, TAG_END );
2877 * Ignore this.
2879 *( wmki->wmki_ID ) = WMHI_IGNORE;
2880 } else {
2882 * Find the selected node.
2884 for ( node = ld->ld_Entries.lvl_First; node->lve_Next; node = node->lve_Next, nnum++ ) {
2885 if ( node->lve_Flags & LVEF_SELECTED ) {
2886 sel = TRUE;
2887 break;
2892 * Found?
2894 if ( ! sel )
2896 * Select first entry.
2898 DoSetMethod( obj, wmki->wmki_GInfo, LISTV_Select, 0L, TAG_END );
2899 else {
2900 if ( qual & ( IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT )) {
2902 * Shifted selectes the previous entry.
2904 nnum=LISTV_Select_Previous;
2905 } else {
2907 * Normal the next entry.
2909 nnum=LISTV_Select_Next;
2913 * Select the new entry.
2915 DoSetMethod( obj, wmki->wmki_GInfo, LISTV_Select, nnum, TAG_END );
2918 * Ignore this..
2920 *( wmki->wmki_ID ) = WMHI_IGNORE;
2923 return( WMKF_VERIFY );
2925 REGFUNC_END
2928 * Get entry predecessor, position and add method.
2930 //STATIC ASM VOID EntryPosHow( REG(a0) LD *ld, REG(a1) LVE **lve, REG(d0) ULONG pos, REG(a2) ULONG *how )
2931 STATIC ASM REGFUNC4(VOID, EntryPosHow,
2932 REGPARAM(A0, LD *, ld),
2933 REGPARAM(A1, LVE **, lve),
2934 REGPARAM(D0, ULONG, pos),
2935 REGPARAM(A2, ULONG, *how))
2937 if ( ! pos ) {
2939 * Position of 0 means add to the head.
2941 *how = LVAP_HEAD;
2942 *lve = NULL;
2943 } else if ( pos >= ld->ld_Total ) {
2945 * A position larger or equal to the
2946 * number of available entries means
2947 * add to the tail.
2949 *how = LVAP_TAIL;
2950 *lve = NULL;
2951 } else {
2953 * Look up the entry to insert after.
2955 *how = 0L;
2956 *lve = FindNodeQuick( ld, pos - 1 );
2959 REGFUNC_END
2963 * (Multi)Select entry and/or make it visible.
2965 //SAVEDS ASM VOID DoEntry(REG(a0) struct GadgetInfo *gi, REG(a1) Object *obj, REG(a2) LD *ld, REG(d0) ULONG flags, REG(d1) ULONG number)
2966 SAVEDS ASM REGFUNC5(VOID, DoEntry,
2967 REGPARAM(A0, struct GadgetInfo *, gi),
2968 REGPARAM(A1, Object *, obj),
2969 REGPARAM(A2, LD *, ld),
2970 REGPARAM(D0, ULONG, flags),
2971 REGPARAM(D1, ULONG, number))
2973 ULONG tag;
2976 * Make visible? Select?
2978 if (flags & LVASF_SELECT)
2981 * Visible or not?
2983 tag = (flags & LVASF_NOT_VISIBLE) ? LISTV_SelectNotVisible : LISTV_Select;
2985 DoSetMethod(obj, gi, tag, number, TAG_DONE);
2987 else if (flags & LVASF_MULTISELECT)
2990 * Visible or not?
2992 tag = (flags & LVASF_NOT_VISIBLE) ? LISTV_SelectMultiNotVisible : LISTV_SelectMulti;
2994 DoSetMethod(obj, gi, tag, number, TAG_DONE);
2996 else if (flags & LVASF_MAKEVISIBLE)
2999 * Just make it visible.
3001 DoSetMethod(obj, gi, LISTV_MakeVisible, number, TAG_DONE);
3003 else
3006 * Re-render list.
3008 ld->ld_Flags |= LDF_REFRESH_ALL;
3009 DoRenderMethod(obj, gi, GREDRAW_UPDATE);
3012 REGFUNC_END
3013 /// LVM_INSERTENTRIES
3015 * Insert entries.
3017 METHOD(ListClassInsertEntries, struct lvmInsertEntries *, lvmi)
3019 LD *ld = INST_DATA( cl, obj );
3020 LVE *lve;
3021 ULONG rc, pos = lvmi->lvmi_Pos, how;
3024 * Where do we insert them.
3026 EntryPosHow(ld, &lve, pos, &how);
3029 * Insert the entries.
3031 rc = AddEntries(ld, lvmi->lvmi_Entries, obj, how, lve);
3034 * We want the list completely refreshed.
3036 ld->ld_Flags |= LDF_REFRESH_ALL;
3039 * Render the object.
3041 DoRenderMethod(obj, lvmi->lvmi_GInfo, GREDRAW_UPDATE);
3043 return rc;
3045 METHOD_END
3047 /// LVM_INSERTSINGLE
3049 * Insert a single entry.
3051 METHOD(ListClassInsertSingle, struct lvmInsertSingle *, lvis)
3053 LD *ld = INST_DATA(cl, obj);
3054 struct lvmInsertEntries lvmi;
3055 APTR entries[2];
3056 ULONG rc;
3059 * Set it up.
3061 entries[0] = lvis->lvis_Entry;
3062 entries[1] = NULL;
3064 lvmi.MethodID = LVM_INSERTENTRIES;
3065 lvmi.lvmi_GInfo = lvis->lvis_GInfo;
3066 lvmi.lvmi_Entries = entries;
3067 lvmi.lvmi_Pos = lvis->lvis_Pos;
3070 * Insert them.
3072 rc = ListClassInsertEntries( cl, obj, &lvmi );
3075 * Select the entry or make it visible
3076 * or just redraw the list.
3078 ld->ld_LastAdded->lve_Flags |= LVEF_REFRESH;
3081 * Make visible? Select?
3083 DoEntry(lvis->lvis_GInfo, obj, ld, lvis->lvis_Flags, lvis->lvis_Pos >= ld->ld_Total ? ld->ld_Total - 1 : lvis->lvis_Pos );
3085 return rc;
3087 METHOD_END
3089 /// LVM_ADDENTRIES
3091 * Add entries.
3093 METHOD(ListClassAddEntries, struct lvmAddEntries *, lva)
3095 LD *ld = INST_DATA( cl, obj );
3096 ULONG rc;
3099 * Add the entries.
3101 rc = AddEntries(ld, lva->lvma_Entries, obj, lva->lvma_How, NULL);
3104 * We want the list completely refreshed.
3106 ld->ld_Flags |= LDF_REFRESH_ALL;
3109 * Render the object.
3111 DoRenderMethod(obj, lva->lvma_GInfo, GREDRAW_UPDATE);
3113 return rc;
3115 METHOD_END
3117 /// LVM_ADDSINGLE
3119 * Add a single entry.
3121 METHOD(ListClassAddSingle, struct lvmAddSingle *, lva)
3123 LD *ld = INST_DATA(cl, obj);
3124 APTR entries[2];
3125 ULONG number, rc;
3126 LVE *tmp;
3129 * Set it up.
3131 entries[0] = lva->lvma_Entry;
3132 entries[1] = NULL;
3135 * Add it.
3137 ld->ld_LastAdded = NULL;
3138 rc = AddEntries(ld, entries, obj, lva->lvma_How, NULL);
3141 * No need to compute the number of the
3142 * entry when it is added to the start or
3143 * the end of the list.
3145 if (lva->lvma_How == LVAP_HEAD) number = 0;
3146 else if (lva->lvma_How == LVAP_TAIL) number = ld->ld_Total - 1;
3147 else
3150 * Get it's number.
3152 for (tmp = ld->ld_Entries.lvl_First, number = 0; tmp->lve_Next; tmp = tmp->lve_Next, number++)
3154 if (tmp == ld->ld_LastAdded)
3155 break;
3158 * Select the entry or make it visible
3159 * or just redraw the list.
3161 tmp->lve_Flags |= LVEF_REFRESH;
3165 * New top visible entry?
3167 if (number == ld->ld_Top)
3168 ld->ld_TopEntry = ld->ld_LastAdded;
3171 * Make visible? Select?
3173 DoEntry(lva->lvma_GInfo, obj, ld, lva->lvma_Flags, number);
3175 return rc;
3177 METHOD_END
3179 /// LVM_CLEAR
3181 * Clear the entire list.
3183 METHOD(ListClassClear, struct lvmCommand *, lvc)
3185 LD *ld = INST_DATA(cl, obj);
3186 LVE *lve;
3187 struct lvResource lvr;
3190 * Initialize lvResource structure to kill the entry.
3192 lvr.lvr_Command = LVRC_KILL;
3195 * Say were busy.
3197 ld->ld_Flags |= LDF_LIST_BUSY;
3200 * Free all entries.
3202 while (lve = (LVE *)RemHead((struct List *)&ld->ld_Entries))
3205 * Do we have a resource hook?
3207 if (ld->ld_Resource)
3210 * Setup the entry.
3212 lvr.lvr_Entry = lve->lve_Entry;
3215 * Call the hook.
3217 BGUI_CallHookPkt(ld->ld_Resource, (VOID *)obj, (VOID *)&lvr);
3219 else
3222 * Simple deallocation.
3224 BGUI_FreePoolMem(lve->lve_Entry);
3227 * Free the entry node.
3229 BGUI_FreePoolMem(lve);
3233 * Zero entries.
3235 ld->ld_Total = ld->ld_Top = 0;
3236 ld->ld_TopEntry = NULL;
3237 ld->ld_LastActive = NULL;
3238 ld->ld_LastNum = 0;
3239 ld->ld_LastAdded = NULL;
3242 * We ain't busy no more.
3244 ld->ld_Flags &= ~LDF_LIST_BUSY;
3247 * Notify a NULL entry.
3249 DoNotifyMethod(obj, lvc->lvmc_GInfo, 0, GA_ID, GADGET(obj)->GadgetID, LISTV_Entry, NULL, TAG_END);
3252 * We need a complete refresh.
3254 DoRenderMethod(obj, lvc->lvmc_GInfo, GREDRAW_REDRAW);
3256 return 1;
3258 METHOD_END
3261 * Find a node by it's entry data (slow!).
3263 //STATIC ASM LVE *FindEntryData(REG(a0) LD *ld, REG(a1) APTR data, REG(a2) ULONG *number)
3264 STATIC ASM REGFUNC3(LVE *, FindEntryData,
3265 REGPARAM(A0, LD *, ld),
3266 REGPARAM(A1, APTR, data),
3267 REGPARAM(A2, ULONG *, number))
3269 LVE *lve;
3270 ULONG num = 0;
3272 for (lve = ld->ld_Entries.lvl_First; lve->lve_Next; lve = lve->lve_Next, num++)
3274 if (lve->lve_Entry == data)
3276 if (number) *number = num;
3277 return lve;
3280 return NULL;
3282 REGFUNC_END
3285 * Find a node by it's entry data (can be fast!).
3287 //STATIC ASM LVE *FindEntryDataF(REG(a0) LD *ld, REG(a1) APTR data)
3288 STATIC ASM REGFUNC2(LVE *, FindEntryDataF,
3289 REGPARAM(A0, LD *, ld),
3290 REGPARAM(A1, APTR, data))
3292 LVE *lve;
3294 if (data == ld->ld_ScanEntry)
3295 return ld->ld_ScanNode;
3297 for (lve = ld->ld_Entries.lvl_First; lve->lve_Next; lve = lve->lve_Next)
3299 if (lve->lve_Entry == data)
3300 return lve;
3302 return NULL;
3304 REGFUNC_END
3306 /// LVM_GETENTRY
3308 * Get an entry.
3310 //STATIC ASM ULONG ListClassGetEntry( REG(a0) Class *cl, REG(a2) Object *obj, REG(a1) struct lvmGetEntry *lvg )
3311 STATIC ASM REGFUNC3(ULONG, ListClassGetEntry,
3312 REGPARAM(A0, Class *, cl),
3313 REGPARAM(A2, Object *, obj),
3314 REGPARAM(A1, struct lvmGetEntry *, lvg))
3316 LD *ld = ( LD * )INST_DATA( cl, obj );
3317 LVE *lve;
3318 ULONG rc = 0L;
3321 * Were busy.
3323 ld->ld_Flags |= LDF_LIST_BUSY;
3326 * What do they want?
3328 switch ( lvg->MethodID ) {
3330 case LVM_FIRSTENTRY:
3332 * The first entry.
3334 if ( ld->ld_Total ) {
3336 * Selected entry?
3338 if ( lvg->lvmg_Flags & LVGEF_SELECTED ) {
3340 * Scan the list.
3342 for ( lve = ld->ld_Entries.lvl_First; lve->lve_Next; lve = lve->lve_Next ) {
3344 * Found?
3346 if ( lve->lve_Flags & LVEF_SELECTED ) {
3347 ld->ld_ScanNode = lve;
3348 ld->ld_ScanEntry = lve->lve_Entry;
3349 rc = ( ULONG )lve->lve_Entry;
3350 break;
3353 } else {
3355 * Normal first entry.
3357 if ( ld->ld_Entries.lvl_First->lve_Next ) {
3358 ld->ld_ScanNode = ld->ld_Entries.lvl_First;
3359 ld->ld_ScanEntry = ld->ld_ScanNode->lve_Entry;
3360 rc = ( ULONG )ld->ld_ScanEntry;
3364 break;
3366 case LVM_LASTENTRY:
3368 * The last entry.
3370 if ( ld->ld_Total ) {
3372 * Selected entry?
3374 if ( lvg->lvmg_Flags & LVGEF_SELECTED ) {
3376 * Scan the list.
3378 for ( lve = ld->ld_Entries.lvl_Last; ; lve = lve->lve_Prev ) {
3380 * Found?
3382 if ( lve->lve_Flags & LVEF_SELECTED ) {
3383 ld->ld_ScanNode = lve;
3384 ld->ld_ScanEntry = lve->lve_Entry;
3385 rc = ( ULONG )lve->lve_Entry;
3386 break;
3389 * Done?
3391 if ( lve == ld->ld_Entries.lvl_First )
3392 break;
3394 } else {
3396 * Normal last entry.
3398 if ( ld->ld_Entries.lvl_First->lve_Next ) {
3399 ld->ld_ScanNode = ld->ld_Entries.lvl_Last;
3400 ld->ld_ScanEntry = ld->ld_ScanNode->lve_Entry;
3401 rc = ( ULONG )ld->ld_ScanEntry;
3405 break;
3407 case LVM_NEXTENTRY:
3409 * Valid entry?
3411 if ( lve = FindEntryDataF( ld, lvg->lvmg_Previous )) {
3413 * Is there a next one?
3415 if ( lve != ld->ld_Entries.lvl_Last ) {
3417 * Selected entry?
3419 if ( lvg->lvmg_Flags & LVGEF_SELECTED ) {
3421 * Scan the list.
3423 for ( lve = lve->lve_Next; lve->lve_Next; lve = lve->lve_Next ) {
3425 * Found?
3427 if ( lve->lve_Flags & LVEF_SELECTED ) {
3428 ld->ld_ScanNode = lve;
3429 ld->ld_ScanEntry = lve->lve_Entry;
3430 rc = ( ULONG )lve->lve_Entry;
3431 break;
3434 } else {
3436 * Normal next entry.
3438 ld->ld_ScanNode = lve->lve_Next;
3439 ld->ld_ScanEntry = lve->lve_Next->lve_Entry;
3440 rc = ( ULONG )ld->ld_ScanEntry;
3444 break;
3446 case LVM_PREVENTRY:
3448 * Valid entry?
3450 if ( lve = FindEntryDataF( ld, lvg->lvmg_Previous )) {
3452 * Is there a previous one?
3454 if ( lve != ld->ld_Entries.lvl_First ) {
3456 * Selected entry?
3458 if ( lvg->lvmg_Flags & LVGEF_SELECTED ) {
3460 * Scan the list.
3462 for ( lve = lve->lve_Prev; ; lve = lve->lve_Prev ) {
3464 * Found?
3466 if ( lve->lve_Flags & LVEF_SELECTED ) {
3467 ld->ld_ScanNode = lve;
3468 ld->ld_ScanEntry = lve->lve_Entry;
3469 rc = ( ULONG )lve->lve_Entry;
3470 break;
3473 * Done?
3475 if ( lve == ld->ld_Entries.lvl_First )
3476 break;
3478 } else {
3480 * Normal previous entry.
3482 ld->ld_ScanNode = lve->lve_Prev;
3483 ld->ld_ScanEntry = lve->lve_Prev->lve_Entry;
3484 rc = ( ULONG )ld->ld_ScanEntry;
3488 break;
3492 * Were not busy anymore.
3494 ld->ld_Flags &= ~LDF_LIST_BUSY;
3495 return rc;
3497 REGFUNC_END
3499 /// LVM_REMENTRY
3501 * Remove an entry from the list.
3503 //STATIC ASM ULONG ListClassRemEntry( REG(a0) Class *cl, REG(a2) Object *obj, REG(a1) struct lvmRemEntry *lvmr )
3504 STATIC ASM REGFUNC3(ULONG, ListClassRemEntry,
3505 REGPARAM(A0, Class *, cl),
3506 REGPARAM(A2, Object *, obj),
3507 REGPARAM(A1, struct lvmRemEntry *, lvmr))
3509 LD *ld = INST_DATA(cl, obj);
3510 LVE *lve;
3511 struct lvResource lvr;
3512 ULONG rc = 0L;
3515 * Mark us as busy.
3517 ld->ld_Flags |= LDF_LIST_BUSY;
3520 * Find the entry node.
3522 if (lve = FindEntryData(ld, lvmr->lvmr_Entry, NULL))
3525 * Remove the node.
3527 Remove(( struct Node * )lve );
3530 * The last clicked one?
3532 if ( lve == ld->ld_LastActive ) {
3533 ld->ld_LastActive = NULL;
3534 ld->ld_LastNum = 0L;
3538 * Send NULL notification when this is
3539 * a single-select listview and the entry
3540 * is selected.
3542 if (( ! ( ld->ld_Flags & LDF_MULTI_SELECT )) && ( lve->lve_Flags & LVEF_SELECTED ))
3543 DoNotifyMethod( obj, lvmr->lvmr_GInfo, 0L, GA_ID, GADGET( obj )->GadgetID, LISTV_Entry, NULL, TAG_END );
3546 * Resource hook?
3548 if ( ld->ld_Resource ) {
3550 * Init structure.
3552 lvr.lvr_Command = LVRC_KILL;
3553 lvr.lvr_Entry = lve->lve_Entry;
3556 * Call the hook.
3558 rc = ( ULONG )BGUI_CallHookPkt(( void * )ld->ld_Resource, ( void * )obj, ( void * )&lvr );
3559 } else {
3561 * Simple de-allocation
3563 BGUI_FreePoolMem( lve->lve_Entry );
3564 rc = 1L;
3567 * Free node.
3569 BGUI_FreePoolMem( lve );
3572 * One less entry.
3574 ld->ld_Total--;
3577 * Not busy anymore.
3579 ld->ld_Flags &= ~LDF_LIST_BUSY;
3582 * Refresh list.
3584 DoRenderMethod( obj, lvmr->lvmr_GInfo, GREDRAW_REDRAW );
3586 ld->ld_Flags &= ~LDF_LIST_BUSY;
3587 return rc;
3589 REGFUNC_END
3591 /// LVM_REMSELECTED
3593 * Remove the selected entry from the list and
3594 * select the next/previous one.
3596 METHOD(ListClassRemSelected, struct lvmCommand *, lvmc)
3598 LD *ld = INST_DATA(cl, obj);
3599 LVE *lve, *sel = NULL;
3600 ULONG rc = 0;
3603 * Scan the selected entry.
3605 while (FirstSelected(obj))
3608 * Pick up it's node.
3610 lve = ld->ld_ScanNode;
3613 * Was it the last one?
3615 if (lve == ld->ld_Entries.lvl_Last)
3618 * Also the first one?
3620 if (lve != ld->ld_Entries.lvl_First)
3623 * No. Deselect entry and select
3624 * it's predecessor.
3626 lve->lve_Flags &= ~LVEF_SELECTED;
3627 sel = lve->lve_Prev;
3630 * Setup selection data.
3632 if (lve == ld->ld_LastActive)
3634 ld->ld_LastActive = sel;
3635 ld->ld_LastNum--;
3638 else
3640 sel = NULL;
3643 else
3646 * Deselect entry and select it's successor.
3648 lve->lve_Flags &= ~LVEF_SELECTED;
3649 sel = lve->lve_Next;
3652 * Setup selection data.
3654 if (lve == ld->ld_LastActive)
3655 ld->ld_LastActive = sel;
3659 * Remove entry.
3661 AsmDoMethod(obj, LVM_REMENTRY, NULL, lve->lve_Entry);
3663 rc++;
3665 if (sel)
3668 * Notify new entry.
3670 sel->lve_Flags |= (LVEF_SELECTED | LVEF_REFRESH);
3671 DoNotifyMethod(obj, lvmc->lvmc_GInfo, 0L, GA_ID, GADGET(obj)->GadgetID, LISTV_Entry, sel->lve_Entry, TAG_END);
3673 if (rc)
3675 AsmDoMethod(obj, LVM_REFRESH, lvmc->lvmc_GInfo);
3677 return rc;
3679 METHOD_END
3681 /// LVM_REFRESH
3683 * Refresh the listview.
3685 METHOD(ListClassRefresh, struct lvmCommand *, lvmc)
3687 return DoRenderMethod(obj, lvmc->lvmc_GInfo, GREDRAW_REDRAW);
3689 METHOD_END
3691 /// LVM_REDRAW
3693 * Redraw the listview entries.
3695 METHOD(ListClassRedraw, struct lvmCommand *, lvmc)
3697 LD *ld = INST_DATA(cl, obj);
3699 ld->ld_Flags |= LDF_REFRESH_ALL;
3700 return DoRenderMethod(obj, lvmc->lvmc_GInfo, GREDRAW_UPDATE);
3702 METHOD_END
3704 /// LVM_REDRAWSINGLE
3705 METHOD(ListClassRedrawSingle, struct lvmRedrawSingle *, lvrs)
3707 LD *ld = INST_DATA(cl, obj);
3708 LVE *lve;
3709 ULONG rc;
3711 if (!(lvrs->lvrs_Flags & LVRF_ALL_COLUMNS))
3713 ld->ld_OneColumn = lvrs->lvrs_Column;
3714 ld->ld_Flags |= LDF_ONE_COLUMN;
3717 if (!(lvrs->lvrs_Flags & LVRF_ALL_ENTRIES))
3719 if (lve = FindEntryDataF(ld, lvrs->lvrs_Entry))
3720 lve->lve_Flags |= LVEF_REFRESH;
3722 else
3724 ld->ld_Flags |= LDF_REFRESH_ALL;
3726 rc = DoRenderMethod(obj, lvrs->lvrs_GInfo, GREDRAW_UPDATE);
3728 ld->ld_Flags &= ~LDF_ONE_COLUMN;
3730 return rc;
3732 METHOD_END
3734 /// LVM_SORT
3736 * Sort the list.
3738 //STATIC ASM ULONG ListClassSort( REG(a0) Class *cl, REG(a2) Object *obj, REG(a1) struct lvmCommand *lvmc )
3739 STATIC ASM REGFUNC3(ULONG, ListClassSort,
3740 REGPARAM(A0, Class *, cl),
3741 REGPARAM(A2, Object *, obj),
3742 REGPARAM(A1, struct lvmCommand *, lvmc))
3744 LD *ld = ( LD * )INST_DATA( cl, obj );
3745 LVE *lve;
3746 LVL buffer;
3749 * Do we have entries?
3751 if ( ld->ld_Total ) {
3753 * Were busy.
3755 ld->ld_Flags |= LDF_LIST_BUSY;
3758 * Initialize buffer.
3760 NewList(( struct List * )&buffer );
3763 * Attach all entries to the buffer.
3765 while ( lve = ( LVE * )RemHead(( struct List * )&ld->ld_Entries ))
3766 AddTail(( struct List * )&buffer, ( struct Node * )lve );
3769 * And put them back again sorted.
3771 while ( lve = ( LVE * )RemHead(( struct List * )&buffer ))
3772 AddEntryInList( ld, obj, lve, LVAP_SORTED );
3775 * Were not busy anymore.
3777 ld->ld_Flags &= ~LDF_LIST_BUSY;
3780 * Refresh the list.
3782 ld->ld_Flags |= LDF_REFRESH_ALL;
3783 DoRenderMethod( obj, lvmc->lvmc_GInfo, GREDRAW_UPDATE );
3785 return 1;
3787 REGFUNC_END
3789 /// LVM_LOCK, LVM_UNLOCK
3791 * (Un)lock the list.
3793 //STATIC ASM ULONG ListClassLock( REG(a0) Class *cl, REG(a2) Object *obj, REG(a1) struct lvmCommand *lvmc )
3794 STATIC ASM REGFUNC3(ULONG, ListClassLock,
3795 REGPARAM(A0, Class *, cl),
3796 REGPARAM(A2, Object *, obj),
3797 REGPARAM(A1, struct lvmCommand *, lvmc))
3799 LD *ld = INST_DATA(cl, obj);
3801 switch (lvmc->MethodID)
3803 case LVM_LOCKLIST:
3804 ld->ld_Flags |= LDF_LIST_BUSY;
3805 break;
3807 case LVM_UNLOCKLIST:
3808 ld->ld_Flags &= ~LDF_LIST_BUSY;
3809 DoRenderMethod(obj, lvmc->lvmc_GInfo, GREDRAW_REDRAW);
3810 break;
3812 return 1;
3814 REGFUNC_END
3816 /// LVM_MOVE
3818 * Move an entry.
3820 //STATIC ASM ULONG ListClassMove( REG(a0) Class *cl, REG(a2) Object *obj, REG(a1) struct lvmMove *lvm )
3821 STATIC ASM REGFUNC3(ULONG, ListClassMove,
3822 REGPARAM(A0, Class *, cl),
3823 REGPARAM(A2, Object *, obj),
3824 REGPARAM(A1, struct lvmMove *, lvm))
3826 LD *ld = ( LD * )INST_DATA( cl, obj );
3827 LVE *lve, *tmp;
3828 ULONG rc = 0L, num = 0L, cpos;
3831 * Look up the entry.
3833 if ( ! lvm->lvmm_Entry ) lve = ld->ld_LastActive;
3834 else lve = FindEntryData( ld, lvm->lvmm_Entry, &cpos );
3836 if ( lve ) {
3838 * Lock the list.
3840 ld->ld_Flags |= LDF_LIST_BUSY;
3843 * Move to?
3845 switch ( lvm->lvmm_Direction ) {
3847 case LVMOVE_UP:
3849 * Already at the top?
3851 if ( lve != ld->ld_Entries.lvl_First ) {
3853 * Pick up new predeccessor.
3855 tmp = lve->lve_Prev->lve_Prev;
3858 * Do it.
3860 goto insertIt;
3862 break;
3864 case LVMOVE_DOWN:
3866 * Already at the bottom?
3868 if ( lve != ld->ld_Entries.lvl_Last ) {
3870 * Pick up new predeccessor.
3872 tmp = lve->lve_Next;
3874 insertIt:
3877 * Remove the node.
3879 Remove(( struct Node * )lve );
3882 * Insert it into it's new spot.
3884 Insert(( struct List * )&ld->ld_Entries, ( struct Node * )lve, ( struct Node * )tmp );
3885 rc = 1L;
3887 break;
3889 case LVMOVE_TOP:
3891 * Already at the top?
3893 if ( lve != ld->ld_Entries.lvl_First ) {
3895 * Remove the node.
3897 Remove(( struct Node * )lve );
3900 * Insert it into it's new spot.
3902 AddHead(( struct List * )&ld->ld_Entries, ( struct Node * )lve );
3905 * The number is known.
3907 num = 0;
3908 rc = 1L;
3909 ld->ld_Flags &= ~LDF_LIST_BUSY;
3910 goto gotNum;
3912 break;
3914 case LVMOVE_BOTTOM:
3916 * Already at the bottom?
3918 if ( lve != ld->ld_Entries.lvl_Last ) {
3920 * Remove the node.
3922 Remove(( struct Node * )lve );
3925 * Insert it into it's new spot.
3927 AddTail(( struct List * )&ld->ld_Entries, ( struct Node * )lve );
3930 * The number is known.
3932 num = ld->ld_Total - 1;
3933 rc = 1L;
3934 ld->ld_Flags &= ~LDF_LIST_BUSY;
3935 goto gotNum;
3937 break;
3939 case LVMOVE_NEWPOS:
3941 * Current position changed?
3943 if ( cpos != lvm->lvmm_NewPos ) {
3945 * New position 0?
3947 if ( lvm->lvmm_NewPos == 0 ) {
3948 Remove(( struct Node * )lve );
3949 AddHead(( struct List * )&ld->ld_Entries, ( struct Node * )lve );
3950 num = 0;
3951 } else if ( lvm->lvmm_NewPos >= ld->ld_Total ) {
3952 Remove(( struct Node * )lve );
3953 AddTail(( struct List * )&ld->ld_Entries, ( struct Node * )lve );
3954 num = ld->ld_Total - 1;
3955 } else {
3957 * Not at the start and not at the end. Find the predecessor
3958 * of the place we drop the this node.
3960 tmp = FindNodeQuick( ld, lvm->lvmm_NewPos - 1 );
3963 * If we are our precedecessor ourselves
3964 * we take the one before us.
3966 if ( tmp == lve ) tmp = tmp->lve_Prev;
3969 * Remove the node from it's current location
3970 * and insert back in it's new place.
3972 Remove(( struct Node * )lve );
3973 Insert(( struct List * )&ld->ld_Entries, ( struct Node * )lve, ( struct Node * )tmp );
3976 * The number is known.
3978 num = lvm->lvmm_NewPos;
3980 rc = 1L;
3981 ld->ld_Flags &= ~LDF_LIST_BUSY;
3982 goto gotNum;
3984 break;
3987 * List changed?
3989 if ( rc ) {
3991 * Not busy anymore.
3993 ld->ld_Flags &= ~LDF_LIST_BUSY;
3996 * Find out it's number.
3998 for ( tmp = ld->ld_Entries.lvl_First; tmp->lve_Next; tmp = tmp->lve_Next, num++ ) {
4000 * Is this the one?
4002 if ( tmp == lve ) {
4003 gotNum:
4005 * Was it selected?
4007 if (lve->lve_Flags & LVEF_SELECTED)
4009 * Setup it's number.
4011 ld->ld_LastNum = num;
4014 * Make the moved entry visible.
4016 ld->ld_Flags |= LDF_REFRESH_ALL;
4017 DoSetMethod( obj, lvm->lvmm_GInfo, LISTV_MakeVisible, num, TAG_END );
4020 * Notify and setup the new position.
4022 DoNotifyMethod( obj, lvm->lvmm_GInfo, 0L, GA_ID, GADGET( obj )->GadgetID, LISTV_NewPosition, num, TAG_END );
4023 ld->ld_NewPos = num;
4024 return rc;
4030 * Not busy anymore.
4032 ld->ld_Flags &= ~LDF_LIST_BUSY;
4033 return rc;
4035 REGFUNC_END
4037 /// LVM_REPLACE
4039 * Replace an entry.
4041 //STATIC ASM ULONG ListClassReplace( REG(a0) Class *cl, REG(a2) Object *obj, REG(a1) struct lvmReplace *lvmr )
4042 STATIC ASM REGFUNC3(ULONG, ListClassReplace,
4043 REGPARAM(A0, Class *, cl),
4044 REGPARAM(A2, Object *, obj),
4045 REGPARAM(A1, struct lvmReplace *, lvmr))
4047 LD *ld = ( LD * )INST_DATA( cl, obj );
4048 LVE *lvo;
4049 struct lvResource lvr;
4050 APTR newdata;
4051 ULONG rc = 0L;
4054 * Data OK?
4056 if (( ! lvmr->lvmr_OldEntry ) || ( ! lvmr->lvmr_NewEntry ))
4057 return( 0L );
4060 * Were busy.
4062 ld->ld_Flags |= LDF_LIST_BUSY;
4065 * Find the old entry.
4067 if (lvo = FindEntryData(ld, lvmr->lvmr_OldEntry, NULL))
4070 * Create the new entry.
4072 if ( ld->ld_Resource ) {
4074 * Init structure.
4076 lvr.lvr_Command = LVRC_MAKE;
4077 lvr.lvr_Entry = lvmr->lvmr_NewEntry;
4080 * Call the hook.
4082 if ( newdata = ( APTR )BGUI_CallHookPkt(( void * )ld->ld_Resource, ( void * )obj, ( void * )&lvr )) {
4084 * Free the old entry and setup the new one.
4086 lvr.lvr_Command = LVRC_KILL;
4087 lvr.lvr_Entry = lvmr->lvmr_OldEntry;
4088 BGUI_CallHookPkt(( void * )ld->ld_Resource, ( void * )obj, ( void * )&lvr );
4089 lvo->lve_Entry = newdata;
4090 lvo->lve_Flags |= LVEF_REFRESH;
4091 rc = ( ULONG )newdata;
4093 } else {
4095 * Allocate a string copy of the new data.
4097 if ( newdata = ( APTR )BGUI_AllocPoolMem( strlen(( UBYTE * )lvmr->lvmr_NewEntry ) + 1 )) {
4099 * Copy it.
4101 strcpy(( UBYTE * )newdata, ( UBYTE * )lvmr->lvmr_NewEntry );
4104 * Free the old entry, and setup the new one.
4106 BGUI_FreePoolMem( lvmr->lvmr_OldEntry );
4107 lvo->lve_Entry = newdata;
4108 lvo->lve_Flags |= LVEF_REFRESH;
4109 rc = ( ULONG )newdata;
4115 * Were not busy anymore.
4117 ld->ld_Flags &= ~LDF_LIST_BUSY;
4119 if ( rc ) DoRenderMethod( obj, lvmr->lvmr_GInfo, GREDRAW_UPDATE );
4120 return( rc );
4122 REGFUNC_END
4124 /// LVM_SETCOLUMNATTRS
4126 METHOD(ListClassSetColumnAttrs, struct lvmColumnAttrs *, lvca)
4128 LD *ld = INST_DATA(cl, obj);
4129 ULONG rc;
4131 rc = BGUI_PackStructureTags(&ld->ld_CD[lvca->lvca_Column], ColumnPackTable, (struct TagItem *)&lvca->lvca_AttrList);
4133 if (rc) AsmDoMethod(obj, LVM_REDRAW, lvca->lvca_GInfo);
4135 return rc;
4137 METHOD_END
4139 /// LVM_GETCOLUMNATTRS
4141 METHOD(ListClassGetColumnAttrs, struct lvmColumnAttrs *, lvca)
4143 LD *ld = INST_DATA(cl, obj);
4145 return BGUI_UnpackStructureTags(&ld->ld_CD[lvca->lvca_Column], ColumnPackTable, (struct TagItem *)&lvca->lvca_AttrList);
4147 METHOD_END
4149 /// BASE_DRAGQUERY
4151 * Query if we accept data from the dragged object. We only accept when:
4153 * A) The querying object is us.
4154 * B) We are in LISTV_ShowDropSpot mode.
4155 * C) The mouse is located inside the list view area.
4157 * All other instances are refused.
4159 METHOD(ListClassDragQuery, struct bmDragPoint *, bmdp)
4161 LD *ld = INST_DATA( cl, obj );
4163 if (bmdp->bmdp_Source == obj && ld->ld_Flags & LDF_SHOWDROPSPOT)
4165 if (bmdp->bmdp_Mouse.X >= 0 &&
4166 bmdp->bmdp_Mouse.Y >= 0 &&
4167 bmdp->bmdp_Mouse.X < ld->ld_InnerBox.Width &&
4168 bmdp->bmdp_Mouse.Y < ld->ld_InnerBox.Height)
4169 return BQR_ACCEPT;
4171 return BQR_REJECT;
4173 METHOD_END
4175 /// BASE_DRAGACTIVE
4177 * Show us being the active drop object.
4179 METHOD(ListClassDragActive, struct bmDragMsg *, bmdm)
4181 LD *ld = INST_DATA(cl, obj);
4182 struct BaseInfo *bi;
4184 ld->ld_DropSpot=ld->ld_DrawSpot = ~0;
4187 * Drop anywhere or do we have to mark the spot?
4189 if ((!(ld->ld_Flags & LDF_SHOWDROPSPOT)) || (!ld->ld_Entries.lvl_First->lve_Next))
4192 * Anywhere or the list is empty. Simply place a dotted line around the view area.
4194 #ifdef DEBUG_BGUI
4195 if (bi = AllocBaseInfoDebug(__FILE__,__LINE__,BI_GadgetInfo, bmdm->bmdm_GInfo, BI_RastPort, NULL, TAG_DONE))
4196 #else
4197 if (bi = AllocBaseInfo(BI_GadgetInfo, bmdm->bmdm_GInfo, BI_RastPort, NULL, TAG_DONE))
4198 #endif
4200 ld->ld_Flags |= LDF_MOVE_DROPBOX;
4202 * Draw the box.
4204 DottedBox(bi, &ld->ld_InnerBox);
4205 FreeBaseInfo(bi);
4208 return 1;
4210 METHOD_END
4212 /// BASE_DRAGINACTIVE
4214 * Deactivate.
4216 //STATIC ASM ULONG ListClassDragInactive( REG(a0) Class *cl, REG(a2) Object *obj, REG(a1) struct bmDragMsg *bmdm )
4217 STATIC ASM REGFUNC3(ULONG, ListClassDragInactive,
4218 REGPARAM(A0, Class *, cl),
4219 REGPARAM(A2, Object *, obj),
4220 REGPARAM(A1, struct bmDragMsg *, bmdm))
4222 LD *ld = INST_DATA(cl, obj);
4225 * Clear drop spot.
4227 if (ld->ld_LineBuffer)
4229 BGUI_FreeRPortBitMap(ld->ld_LineBuffer);
4230 ld->ld_LineBuffer = NULL;
4232 ld->ld_DropSpot = ~0;
4233 ld->ld_Flags &= ~LDF_MOVE_DROPBOX;
4235 return AsmDoSuperMethodA(cl, obj, (Msg)bmdm);
4237 REGFUNC_END
4239 /// BASE_DRAGUPDATE
4241 * Update drop position.
4243 METHOD(ListClassDragUpdate, struct bmDragPoint *, bmdp)
4245 LD *ld = INST_DATA(cl, obj);
4246 struct IBox *ib;
4247 struct GadgetInfo *gi = bmdp->bmdp_GInfo;
4248 struct BaseInfo *bi;
4250 int dpos, otop = ld->ld_Top, ntop = otop;
4252 int x = bmdp->bmdp_Mouse.X;
4253 int y = bmdp->bmdp_Mouse.Y;
4254 int x1, y1, w, h;
4256 if (ld->ld_Flags & LDF_MOVE_DROPBOX)
4258 #ifdef DEBUG_BGUI
4259 if (bi = AllocBaseInfoDebug(__FILE__,__LINE__,BI_GadgetInfo, gi, BI_RastPort, NULL, TAG_DONE))
4260 #else
4261 if (bi = AllocBaseInfo(BI_GadgetInfo, gi, BI_RastPort, NULL, TAG_DONE))
4262 #endif
4264 DottedBox(bi, &ld->ld_InnerBox);
4265 FreeBaseInfo(bi);
4270 * Keep track of the drop position?
4272 if (ld->ld_Flags & LDF_SHOWDROPSPOT && ld->ld_Entries.lvl_First->lve_Next)
4275 * Reject when we are out of the list bounds.
4277 if ((x < 0) || (x >= ld->ld_InnerBox.Width))
4280 * We deactivate when the mouse has left us out of the hitbox.
4282 ld->ld_DropSpot = ld->ld_DrawSpot = ~0;
4283 return BUR_ABORT;
4287 * Make the y position relative to the window.
4289 Get_SuperAttr(cl, obj, BT_HitBox, &ib);
4290 y += ib->Top + (ld->ld_EntryHeight >> 1);
4293 * Get the entry under the mouse.
4295 dpos = MouseOverEntry(ld, y);
4298 * New position above or below the
4299 * visible entries?
4301 if (dpos < ntop)
4303 dpos = --ntop;
4305 else if (dpos > (ntop + ld->ld_Visible))
4307 dpos = ++ntop + ld->ld_Visible;
4310 if (ntop > ld->ld_Total - ld->ld_Visible)
4311 ntop = ld->ld_Total - ld->ld_Visible;
4312 if (ntop < 0) ntop = 0;
4314 if (dpos < 0) dpos = 0;
4315 if (dpos > ld->ld_Total) dpos = ld->ld_Total;
4318 * Position changed?
4320 if (dpos != ld->ld_DropSpot)
4323 * Yes. Get RastPort.
4325 if (gi)
4327 x1 = ld->ld_ListArea.Left;
4328 y1 = ld->ld_ListArea.Top;
4329 w = ld->ld_ListArea.Width;
4330 h = ld->ld_ListArea.Height;
4333 * Re-render the current entry.
4335 if ((ld->ld_DrawSpot != (UWORD)~0) && ld->ld_LineBuffer)
4337 #ifdef DEBUG_BGUI
4338 if (bi = AllocBaseInfoDebug(__FILE__,__LINE__,BI_GadgetInfo, gi, BI_RastPort, NULL, TAG_DONE))
4339 #else
4340 if (bi = AllocBaseInfo(BI_GadgetInfo, gi, BI_RastPort, NULL, TAG_DONE))
4341 #endif
4344 * Fix line at the old position.
4346 ClipBlit(ld->ld_LineBuffer, 0, 0, bi->bi_RPort, x1, ld->ld_DrawSpot, w, 1, 0xC0);
4347 FreeBaseInfo(bi);
4352 * Scroll if necessary.
4354 if (ntop != otop)
4356 DoSetMethod(obj, gi, LISTV_Top, ntop, TAG_DONE);
4360 * Mark new position.
4362 ld->ld_DropSpot = dpos;
4365 * Setup y position.
4367 y = range((dpos - ld->ld_Top) * ld->ld_EntryHeight, 0, h - 1) + y1;
4369 ld->ld_DrawSpot = y;
4371 #ifdef DEBUG_BGUI
4372 if (bi = AllocBaseInfoDebug(__FILE__,__LINE__,BI_GadgetInfo, gi, BI_RastPort, NULL, TAG_DONE))
4373 #else
4374 if (bi = AllocBaseInfo(BI_GadgetInfo, gi, BI_RastPort, NULL, TAG_DONE))
4375 #endif
4377 if (!ld->ld_LineBuffer) ld->ld_LineBuffer = BGUI_CreateRPortBitMap(bi->bi_RPort, w, 1, 0);
4379 if (ld->ld_LineBuffer)
4382 * Copy the old line to a buffer.
4384 ClipBlit(bi->bi_RPort, x1, y, ld->ld_LineBuffer, 0, 0, w, 1, 0xC0);
4387 * Render line at the new position.
4389 SetDashedLine(bi, 0);
4390 HLine(bi->bi_RPort, x1, y, x1 + w - 1);
4392 FreeBaseInfo(bi);
4397 else
4400 * Reject when we are out of the list bounds.
4402 if ((x < 0) || (x >= ld->ld_InnerBox.Width) ||
4403 (y < 0) || (y >= ld->ld_InnerBox.Height))
4406 * We deactivate when the mouse has left us out of the hitbox.
4408 return BUR_ABORT;
4411 return BUR_CONTINUE;
4413 METHOD_END
4415 /// BASE_DROPPED
4417 * We have been dropped upon.
4419 //STATIC ASM ULONG ListClassDropped( REG(a0) Class *cl, REG(a2) Object *obj, REG(a1) struct bmDropped *bmd )
4420 STATIC ASM REGFUNC3(ULONG, ListClassDropped,
4421 REGPARAM(A0, Class *, cl),
4422 REGPARAM(A2, Object *, obj),
4423 REGPARAM(A1, struct bmDropped *, bmd))
4425 LD *ld = ( LD * )INST_DATA( cl, obj );
4426 struct MinList buffer;
4427 LVE *lve, *tmp, *pred = NULL;
4428 ULONG spot = ld->ld_DropSpot, pos = 0;
4430 if(spot==~0)
4431 return(1);
4433 * Initialize buffer list.
4435 NewList(( struct List * )&buffer );
4438 * Were busy buddy.
4440 ld->ld_Flags |= LDF_LIST_BUSY;
4443 * Were we dropped in the list?
4445 if ( spot ) {
4447 * Find the drop-position node.
4449 if ( spot == 1 ) {
4450 pos = 0;
4451 pred = ld->ld_Entries.lvl_First;
4453 * We do make this the predecessor
4454 * if it's selected.
4456 if ( pred->lve_Flags & LVEF_SELECTED )
4457 pred = NULL;
4458 } else if ( spot >= ld->ld_Total ) {
4459 pos = ld->ld_Total - 1;
4460 pred = ld->ld_Entries.lvl_Last;
4461 } else {
4462 pred = FindNodeQuick( ld, spot - 1 );
4463 if ( spot > ld->ld_LastNum ) pos = spot - 1;
4464 else pos = spot;
4468 * Now we have to scan back from the drop
4469 * position to find the first not selected
4470 * entry node.
4472 if ( pred ) {
4473 while ( pred->lve_Flags & LVEF_SELECTED ) {
4475 * Get previous entry.
4477 pred = pred->lve_Prev;
4480 * Is it the first one and still selected?
4482 if ( pred == ld->ld_Entries.lvl_First && pred->lve_Flags & LVEF_SELECTED ) {
4483 pred = NULL;
4484 break;
4491 * Remove all selected entries and append them to the buffer.
4493 lve = ld->ld_Entries.lvl_First;
4496 * Clear the last-active data.
4498 ld->ld_LastActive = NULL;
4499 ld->ld_LastNum = 0;
4501 while ( lve->lve_Next ) {
4503 * Is this a selected entry?
4505 if ( lve->lve_Flags & LVEF_SELECTED ) {
4507 * Mark successor.
4509 tmp = lve->lve_Next;
4512 * Remove from the list and
4513 * append it to the buffer.
4515 Remove(( struct Node * )lve );
4516 AddTail(( struct List * )&buffer, ( struct Node * )lve );
4519 * Make the successor current.
4521 lve = tmp;
4522 } else
4524 * Next please.
4526 lve = lve->lve_Next;
4530 * Move 'm back into their new position.
4532 while ( lve = ( LVE * )RemHead(( struct List * )&buffer )) {
4533 if ( ! ld->ld_LastActive ) {
4534 ld->ld_LastActive = lve;
4535 ld->ld_LastNum = pos;
4537 if ( ! pred ) AddHead(( struct List * )&ld->ld_Entries, ( struct Node * )lve );
4538 else Insert(( struct List * )&ld->ld_Entries, ( struct Node * )lve, ( struct Node * )pred );
4539 pred = lve;
4543 * We are not busy anymore.
4545 ld->ld_Flags &= ~LDF_LIST_BUSY;
4546 ld->ld_DropSpot = ~0;
4548 return 1;
4550 REGFUNC_END
4552 /// BASE_GETOBJECT
4554 * Create a rastport in which the selected entries(s) are rendered.
4556 METHOD(ListClassGetObject, struct bmGetDragObject *, bmgo)
4558 LD *ld = INST_DATA(cl, obj);
4559 struct GadgetInfo *gi = bmgo->bmgo_GInfo;
4560 struct RastPort *drag_rp, *rp = &gi->gi_Screen->RastPort;
4561 APTR entry;
4562 struct IBox box;
4563 ULONG rc = 0, num, i = 0;
4564 int depth = gi->gi_DrInfo->dri_Depth;
4565 struct BaseInfo *bi;
4567 int lx = ld->ld_ListArea.Left;
4568 int ly = ld->ld_ListArea.Top;
4569 int lw = ld->ld_ListArea.Width;
4570 int lh = ld->ld_ListArea.Height;
4571 int eh = ld->ld_EntryHeight;
4572 int mx = gi->gi_Window->MouseX;
4573 int my = gi->gi_Window->MouseY;
4576 * Do we have any selected entries?
4578 if (entry = (APTR)FirstSelected(obj))
4581 * Count the number of selected entries.
4583 for (num = 0; entry && (num <= 10); num++)
4585 entry = (APTR)NextSelected(obj, entry);
4589 * Less than the maximum?
4591 if (num <= 10)
4594 * Allocate the rastport.
4596 if (drag_rp = BGUI_CreateRPortBitMap(rp, lw, num * lh, depth))
4598 #ifdef DEBUG_BGUI
4599 if (bi = AllocBaseInfoDebug(__FILE__,__LINE__,BI_GadgetInfo, gi, BI_RastPort, drag_rp, TAG_DONE))
4600 #else
4601 if (bi = AllocBaseInfo(BI_GadgetInfo, gi, BI_RastPort, drag_rp, TAG_DONE))
4602 #endif
4604 BSetFont(bi, ld->ld_Font);
4605 ColumnSeparators(ld, bi, 0, 0, num * ld->ld_EntryHeight);
4608 * Render them...
4610 entry = (APTR)FirstSelected(obj);
4613 RenderEntry(obj, ld, bi, ld->ld_ScanNode, REL_ZERO | i++);
4614 } while (entry = (APTR)NextSelected(obj, entry));
4615 setIt:
4618 * Setup the rastport so we can
4619 * deallocate it later.
4621 ld->ld_DragRP = drag_rp;
4624 * Setup bounds.
4626 bmgo->bmgo_Bounds->Left = lx;
4627 bmgo->bmgo_Bounds->Top = my - ((num * eh) >> 1);
4628 bmgo->bmgo_Bounds->Width = lw;
4629 bmgo->bmgo_Bounds->Height = num * eh;
4632 * Return a pointer to the bitmap.
4634 rc = (ULONG)drag_rp->BitMap;
4636 if(bi) FreeBaseInfo(bi);
4640 else
4643 * More than 10 entries is a special case.
4645 if (drag_rp = BGUI_CreateRPortBitMap(rp, lw, 3 * eh, depth))
4647 #ifdef DEBUG_BGUI
4648 if (bi = AllocBaseInfoDebug(__FILE__,__LINE__,BI_GadgetInfo, gi, BI_RastPort, drag_rp, TAG_DONE))
4649 #else
4650 if (bi = AllocBaseInfo(BI_GadgetInfo, gi, BI_RastPort, drag_rp, TAG_DONE))
4651 #endif
4653 BSetFont(bi, ld->ld_Font);
4654 ColumnSeparators(ld, bi, 0, 0, 3 * ld->ld_EntryHeight);
4657 * Render the first entry...
4659 FirstSelected(obj);
4660 RenderEntry(obj, ld, bi, ld->ld_ScanNode, REL_ZERO | 0);
4663 * Setup rendering bounds.
4665 box.Left = 0;
4666 box.Top = eh;
4667 box.Width = lw;
4668 box.Height = eh;
4671 * Jam special text...
4673 RenderText(bi, "\33d3\33D5--->", &box);
4676 * Render the last entry...
4678 LastSelected(obj);
4679 RenderEntry(obj, ld, bi, ld->ld_ScanNode, REL_ZERO | 2);
4681 FreeBaseInfo(bi); bi=NULL;
4684 num = 3;
4685 goto setIt;
4689 return rc;
4691 METHOD_END
4693 /// BASE_FREEOBJECT
4695 * Free the dragged object.
4697 //STATIC ASM ULONG ListClassFreeObject( REG(a0) Class *cl, REG(a2) Object *obj, REG(a1) struct bmFreeDragObject *bmfo )
4698 STATIC ASM REGFUNC3(ULONG, ListClassFreeObject,
4699 REGPARAM(A0, Class *, cl),
4700 REGPARAM(A2, Object *, obj),
4701 REGPARAM(A1, struct bmFreeDragObject *, bmfo))
4703 LD *ld = INST_DATA( cl, obj );
4706 * Simply deallocate the bitmap and rastport.
4708 BGUI_FreeRPortBitMap( ld->ld_DragRP );
4709 ld->ld_DragRP = NULL;
4711 return 1;
4713 REGFUNC_END
4716 /// Class initialization.
4718 * Function table.
4720 STATIC DPFUNC ClassFunc[] = {
4721 BASE_RENDER, (FUNCPTR)ListClassRender,
4722 BASE_LAYOUT, (FUNCPTR)ListClassLayout,
4723 BASE_DIMENSIONS, (FUNCPTR)ListClassDimensions,
4725 OM_NEW, (FUNCPTR)ListClassNew,
4726 OM_SET, (FUNCPTR)ListClassSetUpdate,
4727 OM_UPDATE, (FUNCPTR)ListClassSetUpdate,
4728 OM_GET, (FUNCPTR)ListClassGet,
4729 OM_DISPOSE, (FUNCPTR)ListClassDispose,
4730 GM_HITTEST, (FUNCPTR)ListClassHitTest,
4731 GM_GOACTIVE, (FUNCPTR)ListClassGoActive,
4732 GM_HANDLEINPUT, (FUNCPTR)ListClassHandleInput,
4733 GM_GOINACTIVE, (FUNCPTR)ListClassGoInActive,
4734 WM_KEYACTIVE, (FUNCPTR)ListClassKeyActive,
4735 LVM_ADDENTRIES, (FUNCPTR)ListClassAddEntries,
4736 LVM_INSERTENTRIES, (FUNCPTR)ListClassInsertEntries,
4737 LVM_ADDSINGLE, (FUNCPTR)ListClassAddSingle,
4738 LVM_INSERTSINGLE, (FUNCPTR)ListClassInsertSingle,
4739 LVM_CLEAR, (FUNCPTR)ListClassClear,
4740 LVM_FIRSTENTRY, (FUNCPTR)ListClassGetEntry,
4741 LVM_LASTENTRY, (FUNCPTR)ListClassGetEntry,
4742 LVM_NEXTENTRY, (FUNCPTR)ListClassGetEntry,
4743 LVM_PREVENTRY, (FUNCPTR)ListClassGetEntry,
4744 LVM_REMENTRY, (FUNCPTR)ListClassRemEntry,
4745 LVM_REFRESH, (FUNCPTR)ListClassRefresh,
4746 LVM_REDRAW, (FUNCPTR)ListClassRedraw,
4747 LVM_REDRAWSINGLE, (FUNCPTR)ListClassRedrawSingle,
4748 LVM_SORT, (FUNCPTR)ListClassSort,
4749 LVM_LOCKLIST, (FUNCPTR)ListClassLock,
4750 LVM_UNLOCKLIST, (FUNCPTR)ListClassLock,
4751 LVM_REMSELECTED, (FUNCPTR)ListClassRemSelected,
4752 LVM_MOVE, (FUNCPTR)ListClassMove,
4753 LVM_REPLACE, (FUNCPTR)ListClassReplace,
4754 LVM_SETCOLUMNATTRS, (FUNCPTR)ListClassSetColumnAttrs,
4755 LVM_GETCOLUMNATTRS, (FUNCPTR)ListClassGetColumnAttrs,
4756 BASE_DRAGQUERY, (FUNCPTR)ListClassDragQuery,
4757 BASE_DRAGACTIVE, (FUNCPTR)ListClassDragActive,
4758 BASE_DRAGINACTIVE, (FUNCPTR)ListClassDragInactive,
4759 BASE_DRAGUPDATE, (FUNCPTR)ListClassDragUpdate,
4760 BASE_DROPPED, (FUNCPTR)ListClassDropped,
4761 BASE_GETDRAGOBJECT, (FUNCPTR)ListClassGetObject,
4762 BASE_FREEDRAGOBJECT, (FUNCPTR)ListClassFreeObject,
4763 DF_END, NULL
4767 * Simple class initialization.
4769 makeproto Class *InitListClass(void)
4771 return BGUI_MakeClass(CLASS_SuperClassBGUI, BGUI_BASE_GADGET,
4772 CLASS_ObjectSize, sizeof(LD),
4773 CLASS_DFTable, ClassFunc,
4774 TAG_DONE);