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.
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
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
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
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
111 * Revision 1.1 1998/02/25 17:09:00 mlemos
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. */
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. */
140 LVE
*lvl_First
; /* First entry. */
141 LVE
*lvl_EndMarker
; /* End marker. */
142 LVE
*lvl_Last
; /* Last entry. */
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. */
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.
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. */
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. */
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
),
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
),
286 STATIC
struct TagItem PGA2LISTV
[] = {
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;
310 if (ld
->ld_Flags
& LDF_READ_ONLY
)
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
))
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
))
355 if ( ! ld
->ld_Entries
.lvl_First
->lve_Next
)
359 * Scan the list top-down to find
362 for ( lve
= ld
->ld_Entries
.lvl_First
; lve
->lve_Next
; lve
= lve
->lve_Next
, lnum
++ ) {
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
;
385 if ( ! ld
->ld_Entries
.lvl_First
->lve_Next
)
389 * Scan the list from the current node
390 * to find the correct entry.
393 for ( ; lve
->lve_Next
; lve
= lve
->lve_Next
, top
++ ) {
397 } else if ( num
< top
) {
398 for ( ; lve
->lve_Prev
!= ( LVE
* )&ld
->ld_Entries
; lve
= lve
->lve_Prev
, top
-- ) {
409 * Add an entry in the list. Ugly code with
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
))
420 struct lvCompare lvc
;
425 ld
->ld_LastAdded
= lve
;
428 * Set top entry if not
431 if (!ld
->ld_TopEntry
)
432 ld
->ld_TopEntry
= lve
;
440 if (!ld
->ld_Entries
.lvl_First
->lve_Next
)
442 * No. Simply AddTail the entry.
447 * Scan through the entries.
449 for (tmp
= ld
->ld_Entries
.lvl_Last
; ; tmp
= tmp
->lve_Prev
)
457 * No. Simple string comparrison.
459 if (Stricmp((STRPTR
)lve
->lve_Entry
, (STRPTR
)tmp
->lve_Entry
) >= 0)
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)
472 if (tmp
== ld
->ld_Entries
.lvl_First
)
474 ld
->ld_TopEntry
= lve
;
476 * First entry is AddHead'ed.
483 ld
->ld_Flags
|= LDF_REFRESH_ALL
;
484 Insert((struct List
*)&ld
->ld_Entries
, (struct Node
*)lve
, (struct Node
*)tmp
);
489 ld
->ld_Flags
|= LDF_REFRESH_ALL
;
490 AddHead((struct List
*)&ld
->ld_Entries
, (struct Node
*)lve
);
491 ld
->ld_TopEntry
= lve
;
497 AddTail((struct List
*)&ld
->ld_Entries
, (struct Node
*)lve
);
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
))
515 struct lvResource lvr
;
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
++)
538 if (lve
= (LVE
*)BGUI_AllocPoolMem(sizeof(LVE
)))
541 * Do we have a resource hook?
546 * Let the hook make the entry.
548 lve
->lve_Entry
= (APTR
)BGUI_CallHookPkt(ld
->ld_Resource
, (void *)obj
, (void *)&lvr
);
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
);
565 AddEntryInList(ld
, obj
, lve
, how
);
566 if (how
== LVAP_HEAD
)
571 Insert((struct List
*)&ld
->ld_Entries
, (struct Node
*)lve
, (struct Node
*)pred
);
574 ld
->ld_LastAdded
= lve
;
580 BGUI_FreePoolMem(lve
);
584 ld
->ld_Flags
&= ~LDF_LIST_BUSY
;
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
))
601 * # of visible items known?
607 * Otherwise adjust the top to
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
;
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
))
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
;
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
))
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
;
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
))
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?
684 if (bi
= AllocBaseInfoDebug(__FILE__
,__LINE__
,BI_GadgetInfo
, gi
, BI_RastPort
, NULL
, TAG_DONE
))
686 if (bi
= AllocBaseInfo(BI_GadgetInfo
, gi
, BI_RastPort
, NULL
, TAG_DONE
))
689 BOOL needs_refresh
=FALSE
;
694 BSetFont(bi
, ld
->ld_Font
);
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
;
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.
740 new_top
+= ld
->ld_Visible
- diff
;
745 RenderEntry(obj
, ld
, bi
, FindNodeQuick(ld
, new_top
), new_top
- ld
->ld_Top
);
758 if(!(update
=BeginUpdate(gi
->gi_Layer
)))
759 EndUpdate(gi
->gi_Layer
,FALSE
);
760 DoRenderMethod( obj
, gi
, GREDRAW_REDRAW
);
762 EndUpdate(gi
->gi_Layer
,TRUE
);
766 * Needed by RenderEntry().
768 ld
->ld_Flags
&= ~LDF_REFRESH_ALL
;
773 ld
->ld_TopEntry
= FindNodeQuick(ld
, new_top
);
774 ld
->ld_Top
= new_top
;
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
)
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.
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
;
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
;
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
;
859 if (columnwidth
== lastwidth
) break;
863 for (cd
= fcd
; cd
<= lcd
; ++cd
)
865 if (!(cd
->cd_Flags
& LVCF_HIDDEN
))
871 cd
->cd_Offset
= x
; // Last "dummy" column needs right offset of list.
873 ld
->ld_Flags
|= LDF_OFFSETS_VALID
|LDF_REFRESH_ALL
;
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;
893 WORD y1
= ld
->ld_InnerBox
.Top
;
894 WORD y2
= ld
->ld_InnerBox
.Top
+ ld
->ld_InnerBox
.Height
- 1;
899 if (bi
= AllocBaseInfoDebug(__FILE__
,__LINE__
,BI_GadgetInfo
, gi
, BI_RastPort
, NULL
, TAG_DONE
))
901 if (bi
= AllocBaseInfo(BI_GadgetInfo
, gi
, BI_RastPort
, NULL
, TAG_DONE
))
904 BSetDrMd(bi
, COMPLEMENT
);
905 BRectFill(bi
, x1
, y1
, x2
, y2
);
913 * Create a new object.
915 METHOD(ListClassNew
, struct opSet
*, ops
)
918 struct TagItem
*tags
, *tstate
, *tag
;
919 ULONG rc
, sort
= LVAP_TAIL
, data
;
920 ULONG
*new_weights
= NULL
;
921 APTR
*new_entries
= NULL
;
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
;
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
);
952 while (tag
= NextTagItem(&tstate
))
957 case LISTV_EntryArray
:
958 new_entries
= (APTR
*)data
;
961 case LISTV_SortEntryArray
:
965 case LISTV_ColumnWeights
:
966 new_weights
= (ULONG
*)data
;
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.
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.
994 while (tag
= NextTagItem(&tstate
))
999 * Don't disable prop!
1003 * No drag'n'drop on the prop.
1007 tag
->ti_Tag
= TAG_IGNORE
;
1011 if (FRM_TAG(tag
->ti_Tag
) || LAB_TAG(tag
->ti_Tag
))
1012 tag
->ti_Tag
= TAG_IGNORE
;
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
);
1027 * Setup scroller notification.
1029 AsmDoMethod(ld
->ld_Prop
, BASE_ADDMAP
, rc
, PGA2LISTV
);
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
,
1045 AddEntries(ld
, new_entries
, (Object
*)rc
, sort
, NULL
);
1049 AsmCoerceMethod(cl
, (Object
*)rc
, OM_DISPOSE
);
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
;
1068 ULONG data
, otop
= ld
->ld_Top
, ntop
= otop
, num
, oldcol
= ld
->ld_Columns
;
1069 WORD dis
= GADGET(obj
)->Flags
& GFLG_DISABLED
;
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
);
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
);
1114 * Pickup superclass font.
1118 BGUI_CloseFont(ld
->ld_Font
);
1124 case LISTV_ListFont
:
1125 ld
->ld_ListFont
= (struct TextAttr
*)data
;
1128 BGUI_CloseFont(ld
->ld_Font
);
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);
1142 case LISTV_MakeVisible
:
1146 if ((num
= data
) >= ld
->ld_Total
)
1147 num
= ld
->ld_Total
- 1;
1152 ntop
= MakeVisible( ld
, num
);
1156 case LISTV_DeSelect
:
1158 * Get the node number.
1160 if (( num
= data
) == ~0 ) {
1162 * A value of -1 means deselect
1171 num
= num
>= ld
->ld_Total
? ld
->ld_Total
- 1 : num
;
1174 * Find the node to deselect.
1176 if ( lve
= FindNodeQuick( ld
, num
)) {
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
;
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
);
1203 case LISTV_SelectMulti
:
1204 case LISTV_SelectMultiNotVisible
:
1206 * Must be a multi-select object.
1208 if ( ! ( ld
->ld_Flags
& LDF_MULTI_SELECT
))
1214 if (data
== LISTV_Select_All
)
1224 case LISTV_SelectNotVisible
:
1230 case LISTV_Select_First
:
1234 case LISTV_Select_Last
:
1235 num
= ld
->ld_Total
- 1;
1238 case LISTV_Select_Next
:
1239 if (!ld
->ld_LastActive
) num
= ld
->ld_Top
;
1240 else num
= ld
->ld_LastNum
+ 1;
1243 case LISTV_Select_Previous
:
1244 if (!ld
->ld_LastActive
) num
= ld
->ld_Top
;
1245 else num
= max((LONG
)ld
->ld_LastNum
- 1, 0);
1248 case LISTV_Select_Top
:
1252 case LISTV_Select_Page_Up
:
1253 if (!ld
->ld_LastActive
) num
= ld
->ld_Top
;
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);
1262 case LISTV_Select_Page_Down
:
1263 if (!ld
->ld_LastActive
) num
= ld
->ld_Top
;
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);
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
1291 ld
->ld_LastNum
= num
;
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
1301 if (( tag
->ti_Tag
!= LISTV_SelectMulti
) && ( tag
->ti_Tag
!= LISTV_SelectMultiNotVisible
))
1305 * Mark this entry as selected.
1307 lve
->lve_Flags
|= LVEF_SELECTED
| LVEF_REFRESH
;
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
);
1316 * Make the entry visible if requested.
1318 if (( tag
->ti_Tag
!= LISTV_SelectNotVisible
) && ( tag
->ti_Tag
!= LISTV_SelectMultiNotVisible
))
1319 ntop
= MakeVisible( ld
, num
);
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
;
1339 ld
->ld_Columns
= oldcol
; // can't change yet
1342 case BT_ParentWindow
:
1344 DoSetMethodNG(ld
->ld_Prop
, tag
->ti_Tag
, data
, TAG_DONE
);
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
);
1362 * Top value changed?
1366 DoRenderMethod(obj
, opu
->opu_GInfo
, GREDRAW_UPDATE
);
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
);
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
;
1396 case LISTV_LastClicked
:
1397 if (ld
->ld_LastActive
) STORE ld
->ld_LastActive
->lve_Entry
;
1401 case LISTV_LastClickedNum
:
1402 if (ld
->ld_LastActive
)
1403 num
= ld
->ld_LastNum
;
1407 case LISTV_NumEntries
:
1411 case LISTV_DropSpot
:
1412 STORE ld
->ld_DropSpot
;
1415 case LISTV_NewPosition
:
1416 STORE ld
->ld_NewPos
;
1419 case LISTV_ViewBounds
:
1420 STORE
&ld
->ld_InnerBox
;
1423 case LISTV_LastColumn
:
1424 STORE ld
->ld_LastCol
;
1428 rc
= BGUI_UnpackStructureTag((UBYTE
*)ld
, ListPackTable
, tag
, store
);
1429 if (!rc
) rc
= AsmDoSuperMethodA(cl
, obj
, (Msg
)opg
);
1438 * Dispose of the object.
1440 METHOD(ListClassDispose
, Msg
, msg
)
1442 LD
*ld
= INST_DATA(cl
, obj
);
1444 struct lvResource lvr
;
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
;
1465 BGUI_CallHookPkt(ld
->ld_Resource
, (VOID
*)obj
, (VOID
*)&lvr
);
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
);
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
;
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
;
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
1539 if (ld
->ld_Total
<= ld
->ld_Visible
)
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
));
1572 ld
->ld_TopEntry
= FindNode(ld
, ld
->ld_Top
);
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
;
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'))
1602 text
= strchr(text
, '\t');
1608 term
= strchr(text
, '\t');
1609 if (term
) *term
= 0;
1614 if (bi
= AllocBaseInfoDebug(__FILE__
,__LINE__
,BI_DrawInfo
, lvr
->lvr_DrawInfo
, BI_RastPort
, lvr
->lvr_RPort
, TAG_DONE
))
1616 if (bi
= AllocBaseInfo(BI_DrawInfo
, lvr
->lvr_DrawInfo
, BI_RastPort
, lvr
->lvr_RPort
, TAG_DONE
))
1624 RenderText(bi
, text
, &area
);
1625 if (term
) *term
= '\t';
1628 if (GADGET(obj
)->Flags
& GFLG_DISABLED
)
1635 BDisableBox(bi
, &area
);
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
;
1652 int cx
, cw
, xoff
, yoff
;
1655 struct RastPort
*rp
= bi
->bi_RPort
;
1664 xoff
= ld
->ld_ListArea
.Left
;
1665 yoff
= ld
->ld_ListArea
.Top
;
1669 * Initialize the lvRender structure.
1672 lvr
.lvr_DrawInfo
= bi
->bi_DrInfo
;
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;
1686 * A NULL entry means render the title.
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
;
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
;
1716 if (!(ld
->ld_Flags
& LDF_ONE_COLUMN
) || (col
== ld
->ld_OneColumn
))
1719 * Do we have a display hook?
1723 clear
= (ld
->ld_Flags
& LDF_PRE_CLEAR
) || (ld
->ld_CD
[col
].cd_Flags
& LVCF_PRECLEAR
);
1727 AsmDoMethod(ld
->ld_Frame
, FRAMEM_BACKFILL
, bi
, &lvr
.lvr_Bounds
, sel
? IDS_SELECTED
: IDS_NORMAL
);
1733 txt
= (UBYTE
*)BGUI_CallHookPkt(hook
, (void *)obj
, (void *)&lvr
);
1738 * Pick up the entry text.
1741 txt
= lvr
.lvr_Entry
;
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
);
1758 if (lve
) lve
->lve_Flags
&= ~LVEF_REFRESH
;
1765 METHOD(ListClassLayout
, struct bmLayout
*, bml
)
1767 LD
*ld
= INST_DATA(cl
, obj
);
1768 BC
*bc
= BASE_DATA(obj
);
1769 struct TextAttr
*lf
;
1775 lf
= ld
->ld_ListFont
? ld
->ld_ListFont
: bc
->bc_TextAttr
;
1778 sw
= max((lf
->ta_YSize
* 2) / 3, 16);
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
;
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
;
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;
1837 if (tf
= BGUI_OpenFont(lf
))
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
;
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);
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
++)
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
);
1962 if ((!(ld
->ld_Flags
& LDF_CUSTOMDISABLE
)) && ld
->ld_Display
)
1964 if (GADGET(obj
)->Flags
& GFLG_DISABLED
)
1966 BDisableBox(bi
, &bc
->bc_HitBox
);
1973 ld
->ld_Flags
&= ~LDF_REFRESH_ALL
;
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
);
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
;
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
);
2039 if ( ld
->ld_MultiStart
> active
) {
2041 * Loop through the entries.
2043 for ( ; ; node
= node
->lve_Prev
) {
2047 if ( ! ld
->ld_MultiMode
) {
2051 if ( ! ( node
->lve_Flags
& LVEF_SELECTED
)) {
2052 node
->lve_Flags
|= LVEF_SELECTED
| LVEF_REFRESH
;
2058 if (node
->lve_Flags
& LVEF_SELECTED
)
2060 node
->lve_Flags
&= ~LVEF_SELECTED
;
2061 node
->lve_Flags
|= LVEF_REFRESH
;
2068 if ( node
== anode
)
2073 * Loop through the entries.
2075 for ( ; ; node
= node
->lve_Next
) {
2079 if ( ! ld
->ld_MultiMode
) {
2083 if ( ! ( node
->lve_Flags
& LVEF_SELECTED
)) {
2084 node
->lve_Flags
|= LVEF_SELECTED
| LVEF_REFRESH
;
2088 if ( node
->lve_Flags
& LVEF_SELECTED
) {
2089 node
->lve_Flags
&= ~LVEF_SELECTED
;
2090 node
->lve_Flags
|= LVEF_REFRESH
;
2097 if ( node
== anode
)
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
);
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
;
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
;
2145 * They want us to go active.
2147 METHOD(ListClassGoActive
, struct gpInput
*, gpi
)
2149 LD
*ld
= INST_DATA(cl
, obj
);
2151 ULONG rc
= GMR_NOREUSE
, last
= ld
->ld_LastNum
;
2153 struct GadgetInfo
*gi
= gpi
->gpi_GInfo
;
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
)
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;
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
;
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
;
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
;
2242 if (x
>= ld
->ld_CD
[col
].cd_Offset
)
2245 ld
->ld_LastCol
= col
;
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
);
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
)))
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
;
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
) {
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 ] )) {
2313 lve
->lve_Flags
&= ~LVEF_SELECTED
;
2314 lve
->lve_Flags
|= LVEF_REFRESH
;
2318 * Deselect the entry.
2320 lve
->lve_Flags
&= ~LVEF_SELECTED
;
2321 lve
->lve_Flags
|= LVEF_REFRESH
;
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
;
2341 else if (ld
->ld_ActiveEntry
== (ULONG
)-1)
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
);
2355 * Handle user input.
2357 METHOD(ListClassHandleInput
, struct gpInput
*, gpi
)
2359 LD
*ld
= INST_DATA(cl
, obj
);
2362 int vc
, xmin
, xmax
, dx
;
2363 ULONG rc
= GMR_MEACTIVE
, otop
= ld
->ld_Top
, ntop
= ld
->ld_Top
;
2365 int dcol
= ld
->ld_DragColumn
;
2366 int totalwidth
, totalweight
;
2367 struct GadgetInfo
*gi
= gpi
->gpi_GInfo
;
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
;
2382 if (ld
->ld_Flags
& LDF_LIST_BUSY
)
2388 if (ld
->ld_Flags
& LDF_DRAGGING_COLUMN
)
2390 cd
= ld
->ld_CD
+ dcol
;
2395 * Return this by default, implying we want to
2396 * remain the active gadget.
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.
2466 cd2
->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.
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
)
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))
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
;
2535 hit
.MethodID
= BASE_DRAGGING
;
2537 nc
= AsmDoMethodA(obj
, (Msg
)&hit
);
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
);
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;
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
;
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.
2603 * We need a visual change.
2610 lve
->lve_Flags
|= LVEF_SELECTED
| LVEF_REFRESH
;
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
)
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
);
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
);
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.
2666 if (nc
>= ntop
+ ld
->ld_Visible
)
2668 nc
= ntop
++ + ld
->ld_Visible
;
2680 * Set the new entry.
2682 ld
->ld_LastNum
= ld
->ld_ActiveEntry
= nc
;
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
);
2702 * No. Deselect all entries.
2708 lve
->lve_Flags
|= LVEF_SELECTED
| LVEF_REFRESH
;
2710 * We need a visual change.
2716 * Update visuals when necessary.
2718 if (vc
|| (otop
!= ntop
))
2723 DoRenderMethod(obj
, gi
, GREDRAW_UPDATE
);
2726 NewTop(ld
, gi
, obj
, ntop
);
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
);
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
);
2761 * Clear draggable bit.
2763 ld
->ld_Flags
&= ~LDF_DRAGGABLE
;
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
);
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
;
2800 if (ld
->ld_Prop
) AsmDoMethod(ld
->ld_Prop
, GRM_DIMENSIONS
, bi
, bi
->bi_RPort
, &mpx
, &mpy
, 0);
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.
2832 if (my
> mpy
) mpy
= my
;
2837 return CalcDimensions(cl
, obj
, bmd
, mpx
, mpy
);
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
;
2860 if ( ld
->ld_Flags
& LDF_READ_ONLY
) {
2862 * Shifted scrolls up.
2864 if ( qual
& ( IEQUALIFIER_LSHIFT
| IEQUALIFIER_RSHIFT
)) {
2865 if ( newtop
) newtop
--;
2867 if ( newtop
< ( ld
->ld_Total
- ld
->ld_Visible
)) newtop
++;
2873 if ( newtop
!= otop
)
2874 DoSetMethod( obj
, wmki
->wmki_GInfo
, LISTV_Top
, newtop
, TAG_END
);
2879 *( wmki
->wmki_ID
) = WMHI_IGNORE
;
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
) {
2896 * Select first entry.
2898 DoSetMethod( obj
, wmki
->wmki_GInfo
, LISTV_Select
, 0L, TAG_END
);
2900 if ( qual
& ( IEQUALIFIER_LSHIFT
| IEQUALIFIER_RSHIFT
)) {
2902 * Shifted selectes the previous entry.
2904 nnum
=LISTV_Select_Previous
;
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
);
2920 *( wmki
->wmki_ID
) = WMHI_IGNORE
;
2923 return( WMKF_VERIFY
);
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
))
2939 * Position of 0 means add to the head.
2943 } else if ( pos
>= ld
->ld_Total
) {
2945 * A position larger or equal to the
2946 * number of available entries means
2953 * Look up the entry to insert after.
2956 *lve
= FindNodeQuick( ld
, pos
- 1 );
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
))
2976 * Make visible? Select?
2978 if (flags
& LVASF_SELECT
)
2983 tag
= (flags
& LVASF_NOT_VISIBLE
) ? LISTV_SelectNotVisible
: LISTV_Select
;
2985 DoSetMethod(obj
, gi
, tag
, number
, TAG_DONE
);
2987 else if (flags
& LVASF_MULTISELECT
)
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
);
3008 ld
->ld_Flags
|= LDF_REFRESH_ALL
;
3009 DoRenderMethod(obj
, gi
, GREDRAW_UPDATE
);
3013 /// LVM_INSERTENTRIES
3017 METHOD(ListClassInsertEntries
, struct lvmInsertEntries
*, lvmi
)
3019 LD
*ld
= INST_DATA( cl
, obj
);
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
);
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
;
3061 entries
[0] = lvis
->lvis_Entry
;
3064 lvmi
.MethodID
= LVM_INSERTENTRIES
;
3065 lvmi
.lvmi_GInfo
= lvis
->lvis_GInfo
;
3066 lvmi
.lvmi_Entries
= entries
;
3067 lvmi
.lvmi_Pos
= lvis
->lvis_Pos
;
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
);
3093 METHOD(ListClassAddEntries
, struct lvmAddEntries
*, lva
)
3095 LD
*ld
= INST_DATA( cl
, obj
);
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
);
3119 * Add a single entry.
3121 METHOD(ListClassAddSingle
, struct lvmAddSingle
*, lva
)
3123 LD
*ld
= INST_DATA(cl
, obj
);
3131 entries
[0] = lva
->lvma_Entry
;
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;
3152 for (tmp
= ld
->ld_Entries
.lvl_First
, number
= 0; tmp
->lve_Next
; tmp
= tmp
->lve_Next
, number
++)
3154 if (tmp
== ld
->ld_LastAdded
)
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
);
3181 * Clear the entire list.
3183 METHOD(ListClassClear
, struct lvmCommand
*, lvc
)
3185 LD
*ld
= INST_DATA(cl
, obj
);
3187 struct lvResource lvr
;
3190 * Initialize lvResource structure to kill the entry.
3192 lvr
.lvr_Command
= LVRC_KILL
;
3197 ld
->ld_Flags
|= LDF_LIST_BUSY
;
3202 while (lve
= (LVE
*)RemHead((struct List
*)&ld
->ld_Entries
))
3205 * Do we have a resource hook?
3207 if (ld
->ld_Resource
)
3212 lvr
.lvr_Entry
= lve
->lve_Entry
;
3217 BGUI_CallHookPkt(ld
->ld_Resource
, (VOID
*)obj
, (VOID
*)&lvr
);
3222 * Simple deallocation.
3224 BGUI_FreePoolMem(lve
->lve_Entry
);
3227 * Free the entry node.
3229 BGUI_FreePoolMem(lve
);
3235 ld
->ld_Total
= ld
->ld_Top
= 0;
3236 ld
->ld_TopEntry
= NULL
;
3237 ld
->ld_LastActive
= NULL
;
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
);
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
))
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
;
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
))
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
)
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
);
3323 ld
->ld_Flags
|= LDF_LIST_BUSY
;
3326 * What do they want?
3328 switch ( lvg
->MethodID
) {
3330 case LVM_FIRSTENTRY
:
3334 if ( ld
->ld_Total
) {
3338 if ( lvg
->lvmg_Flags
& LVGEF_SELECTED
) {
3342 for ( lve
= ld
->ld_Entries
.lvl_First
; lve
->lve_Next
; lve
= lve
->lve_Next
) {
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
;
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
;
3370 if ( ld
->ld_Total
) {
3374 if ( lvg
->lvmg_Flags
& LVGEF_SELECTED
) {
3378 for ( lve
= ld
->ld_Entries
.lvl_Last
; ; lve
= lve
->lve_Prev
) {
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
;
3391 if ( lve
== ld
->ld_Entries
.lvl_First
)
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
;
3411 if ( lve
= FindEntryDataF( ld
, lvg
->lvmg_Previous
)) {
3413 * Is there a next one?
3415 if ( lve
!= ld
->ld_Entries
.lvl_Last
) {
3419 if ( lvg
->lvmg_Flags
& LVGEF_SELECTED
) {
3423 for ( lve
= lve
->lve_Next
; lve
->lve_Next
; lve
= lve
->lve_Next
) {
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
;
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
;
3450 if ( lve
= FindEntryDataF( ld
, lvg
->lvmg_Previous
)) {
3452 * Is there a previous one?
3454 if ( lve
!= ld
->ld_Entries
.lvl_First
) {
3458 if ( lvg
->lvmg_Flags
& LVGEF_SELECTED
) {
3462 for ( lve
= lve
->lve_Prev
; ; lve
= lve
->lve_Prev
) {
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
;
3475 if ( lve
== ld
->ld_Entries
.lvl_First
)
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
;
3492 * Were not busy anymore.
3494 ld
->ld_Flags
&= ~LDF_LIST_BUSY
;
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
);
3511 struct lvResource lvr
;
3517 ld
->ld_Flags
|= LDF_LIST_BUSY
;
3520 * Find the entry node.
3522 if (lve
= FindEntryData(ld
, lvmr
->lvmr_Entry
, NULL
))
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
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
);
3548 if ( ld
->ld_Resource
) {
3552 lvr
.lvr_Command
= LVRC_KILL
;
3553 lvr
.lvr_Entry
= lve
->lve_Entry
;
3558 rc
= ( ULONG
)BGUI_CallHookPkt(( void * )ld
->ld_Resource
, ( void * )obj
, ( void * )&lvr
);
3561 * Simple de-allocation
3563 BGUI_FreePoolMem( lve
->lve_Entry
);
3569 BGUI_FreePoolMem( lve
);
3579 ld
->ld_Flags
&= ~LDF_LIST_BUSY
;
3584 DoRenderMethod( obj
, lvmr
->lvmr_GInfo
, GREDRAW_REDRAW
);
3586 ld
->ld_Flags
&= ~LDF_LIST_BUSY
;
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
;
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
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
;
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
;
3661 AsmDoMethod(obj
, LVM_REMENTRY
, NULL
, lve
->lve_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
);
3675 AsmDoMethod(obj
, LVM_REFRESH
, lvmc
->lvmc_GInfo
);
3683 * Refresh the listview.
3685 METHOD(ListClassRefresh
, struct lvmCommand
*, lvmc
)
3687 return DoRenderMethod(obj
, lvmc
->lvmc_GInfo
, GREDRAW_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
);
3704 /// LVM_REDRAWSINGLE
3705 METHOD(ListClassRedrawSingle
, struct lvmRedrawSingle
*, lvrs
)
3707 LD
*ld
= INST_DATA(cl
, obj
);
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
;
3724 ld
->ld_Flags
|= LDF_REFRESH_ALL
;
3726 rc
= DoRenderMethod(obj
, lvrs
->lvrs_GInfo
, GREDRAW_UPDATE
);
3728 ld
->ld_Flags
&= ~LDF_ONE_COLUMN
;
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
);
3749 * Do we have entries?
3751 if ( ld
->ld_Total
) {
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
;
3782 ld
->ld_Flags
|= LDF_REFRESH_ALL
;
3783 DoRenderMethod( obj
, lvmc
->lvmc_GInfo
, GREDRAW_UPDATE
);
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
)
3804 ld
->ld_Flags
|= LDF_LIST_BUSY
;
3807 case LVM_UNLOCKLIST
:
3808 ld
->ld_Flags
&= ~LDF_LIST_BUSY
;
3809 DoRenderMethod(obj
, lvmc
->lvmc_GInfo
, GREDRAW_REDRAW
);
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
);
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
);
3840 ld
->ld_Flags
|= LDF_LIST_BUSY
;
3845 switch ( lvm
->lvmm_Direction
) {
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
;
3866 * Already at the bottom?
3868 if ( lve
!= ld
->ld_Entries
.lvl_Last
) {
3870 * Pick up new predeccessor.
3872 tmp
= lve
->lve_Next
;
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
);
3891 * Already at the top?
3893 if ( lve
!= ld
->ld_Entries
.lvl_First
) {
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.
3909 ld
->ld_Flags
&= ~LDF_LIST_BUSY
;
3916 * Already at the bottom?
3918 if ( lve
!= ld
->ld_Entries
.lvl_Last
) {
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;
3934 ld
->ld_Flags
&= ~LDF_LIST_BUSY
;
3941 * Current position changed?
3943 if ( cpos
!= lvm
->lvmm_NewPos
) {
3947 if ( lvm
->lvmm_NewPos
== 0 ) {
3948 Remove(( struct Node
* )lve
);
3949 AddHead(( struct List
* )&ld
->ld_Entries
, ( struct Node
* )lve
);
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;
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
;
3981 ld
->ld_Flags
&= ~LDF_LIST_BUSY
;
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
++ ) {
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
;
4032 ld
->ld_Flags
&= ~LDF_LIST_BUSY
;
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
);
4049 struct lvResource lvr
;
4056 if (( ! lvmr
->lvmr_OldEntry
) || ( ! lvmr
->lvmr_NewEntry
))
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
) {
4076 lvr
.lvr_Command
= LVRC_MAKE
;
4077 lvr
.lvr_Entry
= lvmr
->lvmr_NewEntry
;
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
;
4095 * Allocate a string copy of the new data.
4097 if ( newdata
= ( APTR
)BGUI_AllocPoolMem( strlen(( UBYTE
* )lvmr
->lvmr_NewEntry
) + 1 )) {
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
);
4124 /// LVM_SETCOLUMNATTRS
4126 METHOD(ListClassSetColumnAttrs
, struct lvmColumnAttrs
*, lvca
)
4128 LD
*ld
= INST_DATA(cl
, obj
);
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
);
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
);
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
)
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.
4195 if (bi
= AllocBaseInfoDebug(__FILE__
,__LINE__
,BI_GadgetInfo
, bmdm
->bmdm_GInfo
, BI_RastPort
, NULL
, TAG_DONE
))
4197 if (bi
= AllocBaseInfo(BI_GadgetInfo
, bmdm
->bmdm_GInfo
, BI_RastPort
, NULL
, TAG_DONE
))
4200 ld
->ld_Flags
|= LDF_MOVE_DROPBOX
;
4204 DottedBox(bi
, &ld
->ld_InnerBox
);
4212 /// BASE_DRAGINACTIVE
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
);
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
);
4241 * Update drop position.
4243 METHOD(ListClassDragUpdate
, struct bmDragPoint
*, bmdp
)
4245 LD
*ld
= INST_DATA(cl
, obj
);
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
;
4256 if (ld
->ld_Flags
& LDF_MOVE_DROPBOX
)
4259 if (bi
= AllocBaseInfoDebug(__FILE__
,__LINE__
,BI_GadgetInfo
, gi
, BI_RastPort
, NULL
, TAG_DONE
))
4261 if (bi
= AllocBaseInfo(BI_GadgetInfo
, gi
, BI_RastPort
, NULL
, TAG_DONE
))
4264 DottedBox(bi
, &ld
->ld_InnerBox
);
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;
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
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
;
4320 if (dpos
!= ld
->ld_DropSpot
)
4323 * Yes. Get RastPort.
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
)
4338 if (bi
= AllocBaseInfoDebug(__FILE__
,__LINE__
,BI_GadgetInfo
, gi
, BI_RastPort
, NULL
, TAG_DONE
))
4340 if (bi
= AllocBaseInfo(BI_GadgetInfo
, gi
, BI_RastPort
, NULL
, TAG_DONE
))
4344 * Fix line at the old position.
4346 ClipBlit(ld
->ld_LineBuffer
, 0, 0, bi
->bi_RPort
, x1
, ld
->ld_DrawSpot
, w
, 1, 0xC0);
4352 * Scroll if necessary.
4356 DoSetMethod(obj
, gi
, LISTV_Top
, ntop
, TAG_DONE
);
4360 * Mark new position.
4362 ld
->ld_DropSpot
= dpos
;
4367 y
= range((dpos
- ld
->ld_Top
) * ld
->ld_EntryHeight
, 0, h
- 1) + y1
;
4369 ld
->ld_DrawSpot
= y
;
4372 if (bi
= AllocBaseInfoDebug(__FILE__
,__LINE__
,BI_GadgetInfo
, gi
, BI_RastPort
, NULL
, TAG_DONE
))
4374 if (bi
= AllocBaseInfo(BI_GadgetInfo
, gi
, BI_RastPort
, NULL
, TAG_DONE
))
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);
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.
4411 return BUR_CONTINUE
;
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;
4433 * Initialize buffer list.
4435 NewList(( struct List
* )&buffer
);
4440 ld
->ld_Flags
|= LDF_LIST_BUSY
;
4443 * Were we dropped in the list?
4447 * Find the drop-position node.
4451 pred
= ld
->ld_Entries
.lvl_First
;
4453 * We do make this the predecessor
4456 if ( pred
->lve_Flags
& LVEF_SELECTED
)
4458 } else if ( spot
>= ld
->ld_Total
) {
4459 pos
= ld
->ld_Total
- 1;
4460 pred
= ld
->ld_Entries
.lvl_Last
;
4462 pred
= FindNodeQuick( ld
, spot
- 1 );
4463 if ( spot
> ld
->ld_LastNum
) pos
= spot
- 1;
4468 * Now we have to scan back from the drop
4469 * position to find the first not selected
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
) {
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
;
4501 while ( lve
->lve_Next
) {
4503 * Is this a selected entry?
4505 if ( lve
->lve_Flags
& LVEF_SELECTED
) {
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.
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
);
4543 * We are not busy anymore.
4545 ld
->ld_Flags
&= ~LDF_LIST_BUSY
;
4546 ld
->ld_DropSpot
= ~0;
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
;
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?
4594 * Allocate the rastport.
4596 if (drag_rp
= BGUI_CreateRPortBitMap(rp
, lw
, num
* lh
, depth
))
4599 if (bi
= AllocBaseInfoDebug(__FILE__
,__LINE__
,BI_GadgetInfo
, gi
, BI_RastPort
, drag_rp
, TAG_DONE
))
4601 if (bi
= AllocBaseInfo(BI_GadgetInfo
, gi
, BI_RastPort
, drag_rp
, TAG_DONE
))
4604 BSetFont(bi
, ld
->ld_Font
);
4605 ColumnSeparators(ld
, bi
, 0, 0, num
* ld
->ld_EntryHeight
);
4610 entry
= (APTR
)FirstSelected(obj
);
4613 RenderEntry(obj
, ld
, bi
, ld
->ld_ScanNode
, REL_ZERO
| i
++);
4614 } while (entry
= (APTR
)NextSelected(obj
, entry
));
4618 * Setup the rastport so we can
4619 * deallocate it later.
4621 ld
->ld_DragRP
= drag_rp
;
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
);
4643 * More than 10 entries is a special case.
4645 if (drag_rp
= BGUI_CreateRPortBitMap(rp
, lw
, 3 * eh
, depth
))
4648 if (bi
= AllocBaseInfoDebug(__FILE__
,__LINE__
,BI_GadgetInfo
, gi
, BI_RastPort
, drag_rp
, TAG_DONE
))
4650 if (bi
= AllocBaseInfo(BI_GadgetInfo
, gi
, BI_RastPort
, drag_rp
, TAG_DONE
))
4653 BSetFont(bi
, ld
->ld_Font
);
4654 ColumnSeparators(ld
, bi
, 0, 0, 3 * ld
->ld_EntryHeight
);
4657 * Render the first entry...
4660 RenderEntry(obj
, ld
, bi
, ld
->ld_ScanNode
, REL_ZERO
| 0);
4663 * Setup rendering bounds.
4671 * Jam special text...
4673 RenderText(bi
, "\33d3\33D5--->", &box
);
4676 * Render the last entry...
4679 RenderEntry(obj
, ld
, bi
, ld
->ld_ScanNode
, REL_ZERO
| 2);
4681 FreeBaseInfo(bi
); bi
=NULL
;
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
;
4716 /// Class initialization.
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
,
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
,