fix remapping behavior. Remapping is only necessary if we are rendering on the workbe...
[AROS-Contrib.git] / bgui / listclass.c
blob6c8967f84f3b014f021bf3ac4c38e947c547f82a
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_MinEntriesShown, ld_MinShown, PKCTRL_UWORD),
242 LD_ENTRY(LISTV_Top, ld_Top, PKCTRL_ULONG),
243 LD_ENTRY(LISTV_Columns, ld_Columns, PKCTRL_UWORD),
245 LD_FLAG(LISTV_ReadOnly, LDF_READ_ONLY),
246 LD_FLAG(LISTV_MultiSelect, LDF_MULTI_SELECT),
247 LD_FLAG(LISTV_ThinFrames, LDF_THIN_FRAMES),
248 LD_FLAG(LISTV_MultiSelectNoShift, LDF_NOSHIFT),
249 LD_FLAG(LISTV_ShowDropSpot, LDF_SHOWDROPSPOT),
250 LD_FLAG(LISTV_CustomDisable, LDF_CUSTOMDISABLE),
251 LD_FLAG(LISTV_DragColumns, LDF_ALLOW_DRAG),
252 LD_FLAG(LISTV_PreClear, LDF_PRE_CLEAR),
254 PACK_ENDTABLE
257 #define LC_ENTRY(tag, offset, flags) PACK_ENTRY(LISTC_TAGSTART, tag, cd_, offset, flags)
258 #define LC_FLAG(tag, flag) PACK_LONGBIT(LISTC_TAGSTART, tag, cd_, cd_Flags, PKCTRL_BIT, flag)
260 static ULONG ColumnPackTable[] =
262 PACK_STARTTABLE(LISTV_TAGSTART),
264 LC_ENTRY(LISTC_MinWidth, cd_MinWidth, PKCTRL_UWORD),
265 LC_ENTRY(LISTC_MaxWidth, cd_MaxWidth, PKCTRL_UWORD),
266 LC_ENTRY(LISTC_Weight, cd_Weight, PKCTRL_UWORD),
268 LC_FLAG(LISTC_Draggable, LVCF_DRAGGABLE),
269 LC_FLAG(LISTC_Hidden, LVCF_HIDDEN),
270 LC_FLAG(LISTC_NoSeparator, LVCF_NOSEPARATOR),
271 LC_FLAG(LISTC_PreClear, LVCF_PRECLEAR),
273 PACK_ENDTABLE
277 * Prop map-list.
279 STATIC struct TagItem PGA2LISTV[] = {
280 { PGA_Top, LISTV_Top, },
281 { TAG_END },
284 STATIC VOID RenderEntry(Object *obj, LD *ld, struct BaseInfo *bi, LVE *lve, ULONG top);
285 #define REL_ZERO (0x80000000)
288 STATIC VOID ASM ColumnSeparators(REG(a0) LD *ld, REG(a1) struct BaseInfo *bi, REG(d0) ULONG x, REG(d1) ULONG y, REG(d2) ULONG h)
290 int col, pena, penb, x2, y2;
292 struct RastPort *rp = bi->bi_RPort;
294 x2 = x + ld->ld_InnerBox.Width - 4;
295 y2 = y + h - 1;
297 if (ld->ld_Flags & LDF_READ_ONLY)
299 pena = SHINEPEN;
300 penb = SHADOWPEN;
302 else
304 pena = SHADOWPEN;
305 penb = SHINEPEN;
308 for (col = 0; col < ld->ld_Columns; col++)
310 if (!(ld->ld_CD[col].cd_Flags & LVCF_HIDDEN))
312 x += ld->ld_CD[col].cd_Width - 2;
314 if (!(ld->ld_CD[col].cd_Flags & LVCF_NOSEPARATOR) && (x < x2))
316 BSetDPenA(bi, pena);
317 Move(rp, x, y);
318 Draw(rp, x++, y2);
320 BSetDPenA(bi, penb);
321 Move(rp, x, y);
322 Draw(rp, x++, y2);
328 * Find a node by it's number (slow!).
330 STATIC ASM LVE *FindNode( REG(a0) LD *ld, REG(d0) ULONG num )
332 LVE *lve;
333 ULONG lnum = 0L;
336 * List empty?
338 if ( ! ld->ld_Entries.lvl_First->lve_Next )
339 return( NULL );
342 * Scan the list top-down to find
343 * the correct entry.
345 for ( lve = ld->ld_Entries.lvl_First; lve->lve_Next; lve = lve->lve_Next, lnum++ ) {
346 if ( lnum == num )
347 break;
350 return( lve );
354 * Find a node by it's number (quickly).
356 STATIC ASM LVE *FindNodeQuick( REG(a0) LD *ld, REG(d0) ULONG num )
358 LVE *lve = ld->ld_TopEntry;
359 ULONG top = ld->ld_Top;
362 * List empty?
364 if ( ! ld->ld_Entries.lvl_First->lve_Next )
365 return( NULL );
368 * Scan the list from the current node
369 * to find the correct entry.
371 if ( num > top ) {
372 for ( ; lve->lve_Next; lve = lve->lve_Next, top++ ) {
373 if ( top == num )
374 break;
376 } else if ( num < top ) {
377 for ( ; lve->lve_Prev != ( LVE * )&ld->ld_Entries; lve = lve->lve_Prev, top-- ) {
378 if ( top == num )
379 break;
383 return( lve );
387 * Add an entry in the list. Ugly code with
388 * lotsa goto's :)
390 STATIC ASM VOID AddEntryInList( REG(a0) LD *ld, REG(a1) Object *obj, REG(a2) LVE *lve, REG(d0) ULONG how )
392 LVE *tmp;
393 struct lvCompare lvc;
396 * Save it.
398 ld->ld_LastAdded = lve;
401 * Set top entry if not
402 * defined yet.
404 if (!ld->ld_TopEntry)
405 ld->ld_TopEntry = lve;
407 switch (how)
409 case LVAP_SORTED:
411 * Entries available?
413 if (!ld->ld_Entries.lvl_First->lve_Next)
415 * No. Simply AddTail the entry.
417 goto tailIt;
420 * Scan through the entries.
422 for (tmp = ld->ld_Entries.lvl_Last; ; tmp = tmp->lve_Prev)
425 * Comparrison hook?
427 if (!ld->ld_Compare)
430 * No. Simple string comparrison.
432 if (Stricmp((STRPTR)lve->lve_Entry, (STRPTR)tmp->lve_Entry) >= 0)
433 break;
435 else
437 lvc.lvc_EntryA = lve->lve_Entry;
438 lvc.lvc_EntryB = tmp->lve_Entry;
439 if ((LONG)BGUI_CallHookPkt(ld->ld_Compare, (VOID *)obj, (VOID *)&lvc) >= 0)
440 break;
443 * Done?
445 if (tmp == ld->ld_Entries.lvl_First)
447 ld->ld_TopEntry = lve;
449 * First entry is AddHead'ed.
451 goto headIt;
455 ld->ld_Flags |= LDF_REFRESH_ALL;
456 Insert((struct List *)&ld->ld_Entries, (struct Node *)lve, (struct Node *)tmp);
457 break;
459 case LVAP_HEAD:
460 headIt:
461 ld->ld_Flags |= LDF_REFRESH_ALL;
462 AddHead((struct List *)&ld->ld_Entries, (struct Node *)lve);
463 ld->ld_TopEntry = lve;
464 break;
466 case LVAP_TAIL:
467 default:
468 tailIt:
469 AddTail((struct List *)&ld->ld_Entries, (struct Node *)lve);
470 break;
475 * Add entries to the list.
477 STATIC ASM BOOL AddEntries(REG(a0) LD *ld, REG(a1) APTR *entries, REG(a2) Object *obj, REG(d0) ULONG how, REG(a3) LVE *pred)
479 LVE *lve;
480 struct lvResource lvr;
481 BOOL success = TRUE;
484 * Entries valid?
486 if (!entries) return FALSE;
489 * Initialize lvResource structure to make entries.
491 lvr.lvr_Command = LVRC_MAKE;
493 ld->ld_Flags |= LDF_LIST_BUSY;
496 * Loop through the entries.
498 while ((lvr.lvr_Entry = *entries++))
501 * Create a node.
503 if ((lve = (LVE *)BGUI_AllocPoolMem(sizeof(LVE))))
506 * Do we have a resource hook?
508 if (ld->ld_Resource)
511 * Let the hook make the entry.
513 lve->lve_Entry = (APTR)BGUI_CallHookPkt(ld->ld_Resource, (void *)obj, (void *)&lvr);
515 else
518 * Simple string copy.
520 if ((lve->lve_Entry = (APTR)BGUI_AllocPoolMem(strlen((STRPTR)lvr.lvr_Entry) + 1)))
521 strcpy((STRPTR)lve->lve_Entry, (STRPTR)lvr.lvr_Entry);
524 * All ok?
526 if (lve->lve_Entry)
528 if (!pred)
530 AddEntryInList(ld, obj, lve, how);
531 if (how == LVAP_HEAD)
532 pred = lve;
534 else
536 Insert((struct List *)&ld->ld_Entries, (struct Node *)lve, (struct Node *)pred);
537 pred = lve;
539 ld->ld_LastAdded = lve;
540 ld->ld_Total++;
542 else
544 success = FALSE;
545 BGUI_FreePoolMem(lve);
549 ld->ld_Flags &= ~LDF_LIST_BUSY;
551 return success;
555 * Make an entry visible.
557 STATIC ASM ULONG MakeVisible(REG(a0) LD *ld, REG(d0) ULONG entry)
559 ULONG new_top;
562 * # of visible items known?
564 if (!ld->ld_Visible)
565 ld->ld_Visible = 1;
568 * Otherwise adjust the top to
569 * make it visible.
571 if (entry < ld->ld_Top) new_top = entry;
572 else if (entry >= (ld->ld_Top + ld->ld_Visible)) new_top = max((LONG)(entry - (ld->ld_Visible - 1)), 0);
573 else if (entry >= ld->ld_Total) new_top = max((LONG)ld->ld_Total - (LONG)(ld->ld_Visible - 1), 0);
574 else new_top = ld->ld_Top;
576 return new_top;
580 * De-select node list (slow!).
582 STATIC ASM VOID DeSelect(REG(a0) LD *ld)
584 LVE *lve;
586 for (lve = ld->ld_Entries.lvl_First; lve->lve_Next; lve = lve->lve_Next)
588 if (lve->lve_Flags & LVEF_SELECTED)
590 lve->lve_Flags &= ~LVEF_SELECTED;
591 lve->lve_Flags |= LVEF_REFRESH;
597 * Select node list (slow!).
599 STATIC ASM VOID Select(REG(a0) LD *ld)
601 LVE *lve;
603 for ( lve = ld->ld_Entries.lvl_First; lve->lve_Next; lve = lve->lve_Next ) {
604 if ( ! ( lve->lve_Flags & LVEF_SELECTED ))
605 lve->lve_Flags |= LVEF_SELECTED | LVEF_REFRESH;
610 * Setup a new top-value.
612 STATIC ASM VOID NewTop(REG(a0) LD *ld, REG(a1) struct GadgetInfo *gi, REG(a2) Object *obj, REG(d0) ULONG new_top)
614 struct BaseInfo *bi;
615 int i;
616 BC *bc = BASE_DATA(obj);
619 * How much do we need to go up?
621 int diff = new_top - ld->ld_Top;
624 * Gadget info present?
625 * New position different than old one?
627 if (gi && diff)
630 * Create rastport.
632 #ifdef DEBUG_BGUI
633 if ((bi = AllocBaseInfoDebug(__FILE__,__LINE__,BI_GadgetInfo, gi, BI_RastPort, NULL, TAG_DONE)))
634 #else
635 if ((bi = AllocBaseInfo(BI_GadgetInfo, gi, BI_RastPort, NULL, TAG_DONE)))
636 #endif
638 BOOL needs_refresh=FALSE;
641 * Setup font.
643 BSetFont(bi, ld->ld_Font);
646 * Set top position.
648 ld->ld_TopEntry = FindNodeQuick(ld, new_top);
649 ld->ld_Top = new_top;
651 if (diff <= -ld->ld_Visible)
654 * More than a single view?
656 diff = -ld->ld_Visible;
658 else if (diff >= +ld->ld_Visible)
661 * More than a single view?
663 diff = +ld->ld_Visible;
665 else
668 * Scroll view 'diff' lines.
670 ScrollRaster(bi->bi_RPort, 0, diff * ld->ld_EntryHeight,
671 ld->ld_ListArea.Left, ld->ld_ListArea.Top,
672 ld->ld_ListArea.Left + ld->ld_ListArea.Width - 1,
673 ld->ld_ListArea.Top + ld->ld_ListArea.Height - 1);
674 needs_refresh=(bi->bi_RPort->Layer->Flags & LAYERREFRESH);
677 ColumnSeparators(ld, bi, bc->bc_InnerBox.Left, bc->bc_InnerBox.Top, bc->bc_InnerBox.Height);
680 * Re-render first 'diff' lines.
682 if (diff < 0)
684 i = -diff;
686 else
688 i = diff;
689 new_top += ld->ld_Visible - diff;
692 while (i--)
694 RenderEntry(obj, ld, bi, FindNodeQuick(ld, new_top), new_top - ld->ld_Top);
695 new_top++;
699 * Dump BaseInfo.
701 FreeBaseInfo(bi);
703 if(needs_refresh)
705 BOOL update;
707 if(!(update=BeginUpdate(gi->gi_Layer)))
708 EndUpdate(gi->gi_Layer,FALSE);
709 DoRenderMethod( obj, gi, GREDRAW_REDRAW );
710 if(update)
711 EndUpdate(gi->gi_Layer,TRUE);
715 * Needed by RenderEntry().
717 ld->ld_Flags &= ~LDF_REFRESH_ALL;
720 else
722 ld->ld_TopEntry = FindNodeQuick(ld, new_top);
723 ld->ld_Top = new_top;
729 /************************************************************************
730 ********************** MCLV_GETCOLUMNPOSITIONS() **********************
731 *************************************************************************
732 * Work out the left offset (relative to listview view area) of each
733 * column separator. Put the results in ld_Offsets array, starting
734 * with the first separator (ie. the left offset of the *second* column
735 * from the left). Returns TRUE or FALSE to indicate success. Currently
736 * this can only fail if the object is not displayed when this function
737 * is called. If all goes well, LDF_OFFSETS_VALID is set to TRUE.
739 *************************************************************************/
741 STATIC BOOL GetColumnPositions(Object *obj, LD *ld)
743 int w, dw, x;
744 int totalwidth, columnwidth, totalweight, lastwidth;
745 int listwidth = ld->ld_ListArea.Width;
746 CD *cd, *fcd = ld->ld_CD, *lcd = fcd + ld->ld_Columns - 1;
749 * Start widths at minimum and compute width of all columns.
751 columnwidth = 0;
752 for (cd = fcd; cd <= lcd; ++cd)
754 cd->cd_Width = cd->cd_MinWidth;
755 columnwidth += cd->cd_Width;
759 * Cycle through until all extra space is used.
761 while (columnwidth < listwidth)
763 lastwidth = columnwidth;
764 totalwidth = listwidth;
765 totalweight = 0;
766 for (cd = fcd; cd <= lcd; ++cd)
768 if (!(cd->cd_Flags & LVCF_HIDDEN))
770 if (cd->cd_Width < cd->cd_MaxWidth)
772 totalweight += cd->cd_Weight;
774 else
776 cd->cd_Width = cd->cd_MaxWidth;
777 totalwidth -= cd->cd_Width;
782 if (totalweight == 0) break;
784 for (cd = fcd; cd <= lcd; ++cd)
786 if (!(cd->cd_Flags & LVCF_HIDDEN))
788 if (cd->cd_Width < cd->cd_MaxWidth)
790 w = (cd->cd_Weight * totalwidth) / totalweight;
791 if (w > cd->cd_MaxWidth) w = cd->cd_MaxWidth;
792 if (w < cd->cd_MinWidth) w = cd->cd_MinWidth;
794 dw = w - cd->cd_Width;
795 if (dw > listwidth - columnwidth)
797 dw = listwidth - columnwidth;
798 cd->cd_Width += dw;
799 columnwidth += dw;
800 break;
802 cd->cd_Width += dw;
803 columnwidth += dw;
807 if (columnwidth == lastwidth) break;
810 x = 0;
811 for (cd = fcd; cd <= lcd; ++cd)
813 if (!(cd->cd_Flags & LVCF_HIDDEN))
815 cd->cd_Offset = x;
816 x += cd->cd_Width;
819 cd->cd_Offset = x; // Last "dummy" column needs right offset of list.
821 ld->ld_Flags |= LDF_OFFSETS_VALID|LDF_REFRESH_ALL;
823 return TRUE;
826 /************************************************************************
827 ************************* MCLV_DRAWDRAGLINE() *************************
828 *************************************************************************
829 * Draw, using complement mode, the line that shows the user the position
830 * s/he has dragged the column separator to within the mclv.
832 *************************************************************************/
834 STATIC ASM VOID DrawDragLine(REG(a0) LD *ld, REG(a1) struct GadgetInfo *gi)
836 WORD x1 = ld->ld_InnerBox.Left + ld->ld_DragXLine - 2;
837 WORD x2 = x1 + 1;
838 WORD y1 = ld->ld_InnerBox.Top;
839 WORD y2 = ld->ld_InnerBox.Top + ld->ld_InnerBox.Height - 1;
841 struct BaseInfo *bi;
843 #ifdef DEBUG_BGUI
844 if ((bi = AllocBaseInfoDebug(__FILE__,__LINE__,BI_GadgetInfo, gi, BI_RastPort, NULL, TAG_DONE)))
845 #else
846 if ((bi = AllocBaseInfo(BI_GadgetInfo, gi, BI_RastPort, NULL, TAG_DONE)))
847 #endif
849 BSetDrMd(bi, COMPLEMENT);
850 BRectFill(bi, x1, y1, x2, y2);
851 FreeBaseInfo(bi);
855 /// OM_NEW
857 * Create a new object.
859 METHOD(ListClassNew, struct opSet *, ops)
861 LD *ld;
862 IPTR rc, data;
863 ULONG sort = LVAP_TAIL;
864 struct TagItem *tags, *tag;
865 struct TagItem *tstate;
866 ULONG *new_weights = NULL;
867 APTR *new_entries = NULL;
868 int i;
870 tags = DefTagList(BGUI_LISTVIEW_GADGET, ops->ops_AttrList);
873 * Let the superclass make the object.
875 if ((rc = NewSuperObject(cl, obj, tags)))
878 * Get the instance data.
880 ld = INST_DATA(cl, rc);
881 ld->ld_BC = BASE_DATA(rc);
883 ld->ld_Flags = LDF_REFRESH_ALL|LDF_PRE_CLEAR;
884 ld->ld_MinShown = 3;
885 ld->ld_Columns = 1;
886 ld->ld_Prop = (Object *)~0;
889 * Initialize the list of entries.
891 NewList((struct List *)&ld->ld_Entries);
892 // NewList((struct List *)&ld->ld_Hidden);
894 BGUI_PackStructureTags((APTR)ld, ListPackTable, tags);
896 tstate = tags;
898 while ((tag = NextTagItem(&tstate)))
900 data = tag->ti_Data;
901 switch (tag->ti_Tag)
903 case LISTV_EntryArray:
904 new_entries = (APTR *)data;
905 break;
907 case LISTV_SortEntryArray:
908 sort = LVAP_SORTED;
909 break;
911 case LISTV_ColumnWeights:
912 new_weights = (ULONG *)data;
913 break;
915 case LISTV_PropObject:
916 ld->ld_Prop = (Object *)data;
917 break;
919 case LISTV_ListFont:
920 ld->ld_ListFont = (struct TextAttr *)data;
921 break;
923 case LISTV_Title:
924 ld->ld_Title = (UBYTE *)data;
925 break;
927 case LISTV_TitleHook:
928 ld->ld_TitleHook = (struct Hook *)data;
929 break;
931 case LISTV_CompareHook:
932 ld->ld_Compare = (struct Hook *)data;
933 break;
935 case LISTV_DisplayHook:
936 ld->ld_Display = (struct Hook *)data;
937 break;
939 case LISTV_ResourceHook:
940 ld->ld_Resource = (struct Hook *)data;
941 break;
945 if (ld->ld_Columns < 1) ld->ld_Columns = 1;
947 ld->ld_CD = BGUI_AllocPoolMem((ld->ld_Columns + 1) * sizeof(CD));
950 * Verify array allocated.
952 if (ld->ld_CD)
954 for (i = 0; i <= ld->ld_Columns; i++)
956 ld->ld_CD[i].cd_MinWidth = 24;
957 ld->ld_CD[i].cd_MaxWidth = 0xFFFF;
958 ld->ld_CD[i].cd_Weight = new_weights ? (new_weights[i] ? new_weights[i] : 1) : DEFAULT_WEIGHT;
961 if (ld->ld_Prop == (Object *)~0)
964 * Filter out frame and label attributes.
966 tstate = tags;
968 while ((tag = NextTagItem(&tstate)))
970 switch (tag->ti_Tag)
973 * Don't disable prop!
975 case GA_Disabled:
977 * No drag'n'drop on the prop.
979 case BT_DragObject:
980 case BT_DropObject:
981 tag->ti_Tag = TAG_IGNORE;
982 break;
984 default:
985 if (FRM_TAG(tag->ti_Tag) || LAB_TAG(tag->ti_Tag))
986 tag->ti_Tag = TAG_IGNORE;
987 break;
992 * Create a scroller.
994 ld->ld_Prop = BGUI_NewObject(BGUI_PROP_GADGET, GA_ID, GADGET(rc)->GadgetID,
995 PGA_DontTarget, TRUE, BT_ParentView, ld->ld_BC->bc_View, BT_ParentWindow, ld->ld_BC->bc_Window,TAG_MORE, tags);
998 if (ld->ld_Prop)
1001 * Setup scroller notification.
1003 AsmDoMethod(ld->ld_Prop, BASE_ADDMAP, rc, PGA2LISTV);
1006 if (ld->ld_Frame)
1009 * Set frame attributes to match list attributes.
1011 DoSetMethodNG(ld->ld_Frame, FRM_Recessed, ld->ld_Flags & LDF_READ_ONLY,
1012 FRM_ThinFrame, ld->ld_Flags & LDF_THIN_FRAMES,
1013 TAG_DONE);
1017 * Add entries.
1019 AddEntries(ld, new_entries, (Object *)rc, sort, NULL);
1021 else
1023 AsmCoerceMethod(cl, (Object *)rc, OM_DISPOSE);
1024 rc = 0;
1027 FreeTagItems(tags);
1029 return rc;
1031 METHOD_END
1033 /// OM_SET, OM_UPDATE
1035 * Set or update some attributes.
1037 METHOD(ListClassSetUpdate, struct opUpdate *, opu)
1039 LD *ld = INST_DATA( cl, obj );
1040 struct TagItem *tstate = opu->opu_AttrList;
1041 struct TagItem *tag;
1042 LVE *lve;
1043 IPTR data;
1044 ULONG otop = ld->ld_Top, ntop = otop, num, oldcol = ld->ld_Columns;
1045 WORD dis = GADGET(obj)->Flags & GFLG_DISABLED;
1046 BOOL vc = FALSE;
1047 ULONG *new_weights;
1049 int i;
1052 * First the superclass...
1054 AsmDoSuperMethodA(cl, obj, (Msg)opu);
1056 BGUI_PackStructureTags((APTR)ld, ListPackTable, opu->opu_AttrList);
1059 * Scan for known attributes.
1061 while ((tag = NextTagItem(&tstate)))
1063 data = tag->ti_Data;
1064 switch (tag->ti_Tag)
1066 case LISTV_Title:
1067 ld->ld_Title = (UBYTE *)data;
1068 break;
1070 case LISTV_TitleHook:
1071 ld->ld_TitleHook = (struct Hook *)data;
1072 break;
1074 case LISTV_CompareHook:
1075 ld->ld_Compare = (struct Hook *)data;
1076 break;
1078 case LISTV_DisplayHook:
1079 ld->ld_Display = (struct Hook *)data;
1080 break;
1082 case LISTV_ResourceHook:
1083 ld->ld_Resource = (struct Hook *)data;
1084 break;
1086 case LISTV_PropObject:
1087 if (ld->ld_Prop) DisposeObject(ld->ld_Prop);
1088 ld->ld_Flags &= ~LDF_PROPACTIVE;
1090 if ((ld->ld_Prop = (Object *)data))
1093 * Setup scroller notification.
1095 AsmDoMethod(ld->ld_Prop, BASE_ADDMAP, obj, PGA2LISTV);
1097 vc = TRUE;
1098 break;
1100 case FRM_ThinFrame:
1102 * Set frame thickness.
1104 if (ld->ld_Frame) DoSetMethodNG(ld->ld_Frame, FRM_ThinFrame, data, TAG_END);
1105 if (ld->ld_Prop) DoSetMethodNG(ld->ld_Prop, FRM_ThinFrame, data, TAG_END);
1106 break;
1108 case BT_TextAttr:
1110 * Pickup superclass font.
1112 if (ld->ld_Font)
1114 BGUI_CloseFont(ld->ld_Font);
1115 ld->ld_Font = NULL;
1117 vc=TRUE;
1118 break;
1120 case LISTV_ListFont:
1121 ld->ld_ListFont = (struct TextAttr *)data;
1122 if (ld->ld_Font)
1124 BGUI_CloseFont(ld->ld_Font);
1125 ld->ld_Font = NULL;
1127 vc=TRUE;
1128 break;
1130 case LISTV_Top:
1131 ld->ld_Top = otop; // Needed to circumvent PackStructureTags.
1133 * Make sure we stay in range.
1135 ntop = range(data, 0, (ld->ld_Total > ld->ld_Visible) ? ld->ld_Total - ld->ld_Visible : 0);
1136 break;
1138 case LISTV_MakeVisible:
1140 * Stay in range.
1142 if ((num = data) >= ld->ld_Total)
1143 num = ld->ld_Total - 1;
1146 * Make it visible.
1148 ntop = MakeVisible( ld, num );
1149 vc = TRUE;
1150 break;
1152 case LISTV_DeSelect:
1154 * Get the node number.
1156 if (( num = data ) == ~0 ) {
1158 * A value of -1 means deselect
1159 * all entries.
1161 DeSelect( ld );
1162 vc = TRUE;
1163 } else {
1165 * Stay in range.
1167 num = num >= ld->ld_Total ? ld->ld_Total - 1 : num;
1170 * Find the node to deselect.
1172 if (( lve = FindNodeQuick( ld, num ))) {
1174 * Set it up.
1176 ld->ld_LastActive = lve;
1177 ld->ld_LastNum = num;
1180 * Already unselected?
1182 if (( lve->lve_Flags & LVEF_SELECTED ) && ( ! ( ld->ld_Flags & LDF_READ_ONLY ))) {
1184 * Mark it for a refresh.
1186 lve->lve_Flags &= ~LVEF_SELECTED;
1187 lve->lve_Flags |= LVEF_REFRESH;
1190 * Notify change.
1192 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 );
1193 vc = TRUE;
1197 break;
1199 case LISTV_SelectMulti:
1200 case LISTV_SelectMultiNotVisible:
1202 * Must be a multi-select object.
1204 if ( ! ( ld->ld_Flags & LDF_MULTI_SELECT ))
1205 break;
1208 * LISTV_Select_All?
1210 if (data == LISTV_Select_All)
1212 Select(ld);
1213 vc = TRUE;
1214 break;
1217 /* Fall through. */
1219 case LISTV_Select:
1220 case LISTV_SelectNotVisible:
1222 * Do some Magic?
1224 switch (data)
1226 case LISTV_Select_First:
1227 num = 0;
1228 goto doIt;
1230 case LISTV_Select_Last:
1231 num = ld->ld_Total - 1;
1232 goto doIt;
1234 case LISTV_Select_Next:
1235 if (!ld->ld_LastActive) num = ld->ld_Top;
1236 else num = ld->ld_LastNum + 1;
1237 goto doIt;
1239 case LISTV_Select_Previous:
1240 if (!ld->ld_LastActive) num = ld->ld_Top;
1241 else num = max((LONG)ld->ld_LastNum - 1, 0);
1242 goto doIt;
1244 case LISTV_Select_Top:
1245 num = ld->ld_Top;
1246 goto doIt;
1248 case LISTV_Select_Page_Up:
1249 if (!ld->ld_LastActive) num = ld->ld_Top;
1250 else
1252 num = ld->ld_LastNum;
1253 if (num > ld->ld_Top) num = ld->ld_Top;
1254 else num = max((LONG)(num - ld->ld_Visible + 1), 0);
1256 goto doIt;
1258 case LISTV_Select_Page_Down:
1259 if (!ld->ld_LastActive ) num = ld->ld_Top;
1260 else
1262 num = ld->ld_LastNum;
1263 if (num < (ld->ld_Top + ld->ld_Visible - 1)) num = ld->ld_Top + ld->ld_Visible - 1;
1264 else num = min((LONG)(num + (ld->ld_Visible - 1)), ld->ld_Total - 1);
1266 goto doIt;
1268 default:
1269 num = data;
1271 doIt:
1274 * Make sure we stay in range.
1276 num = (num >= ld->ld_Total) ? ld->ld_Total - 1 : num;
1279 * Find the node to select.
1281 if ((lve = ld->ld_LastActive = FindNodeQuick(ld, num)))
1284 * Setup the number as the last
1285 * selected one.
1287 ld->ld_LastNum = num;
1290 * Already selected?
1292 if (((( tag->ti_Tag != LISTV_SelectMulti ) && ( tag->ti_Tag != LISTV_SelectMultiNotVisible )) || ! ( lve->lve_Flags & LVEF_SELECTED )) && ( ! ( ld->ld_Flags & LDF_READ_ONLY ))) {
1294 * No? DeSelect all other labels if we are not
1295 * multi-selecting.
1297 if (( tag->ti_Tag != LISTV_SelectMulti ) && ( tag->ti_Tag != LISTV_SelectMultiNotVisible ))
1298 DeSelect( ld );
1301 * Mark this entry as selected.
1303 lve->lve_Flags |= LVEF_SELECTED | LVEF_REFRESH;
1306 * Notify change.
1308 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 );
1309 vc = TRUE;
1312 * Make the entry visible if requested.
1314 if (( tag->ti_Tag != LISTV_SelectNotVisible ) && ( tag->ti_Tag != LISTV_SelectMultiNotVisible ))
1315 ntop = MakeVisible( ld, num );
1317 break;
1319 break;
1321 case LISTV_ColumnWeights:
1322 if (!(ld->ld_Flags & LDF_DRAGGING_COLUMN))
1324 new_weights = (ULONG *)data;
1325 for (i = 0; i < ld->ld_Columns; i++)
1327 ld->ld_CD[i].cd_Weight = new_weights ? (new_weights[i] ? new_weights[i] : 1) : DEFAULT_WEIGHT;
1329 ld->ld_Flags &= ~LDF_OFFSETS_VALID;
1330 vc = TRUE;
1332 break;
1334 case LISTV_Columns:
1335 ld->ld_Columns = oldcol; // can't change yet
1336 break;
1338 case BT_ParentWindow:
1339 case BT_ParentView:
1340 DoSetMethodNG(ld->ld_Prop, tag->ti_Tag, data, TAG_DONE);
1341 break;
1346 * Disabled state changed?
1348 if ((GADGET(obj)->Flags & GFLG_DISABLED) != dis)
1350 if ( opu->opu_GInfo ) {
1351 ld->ld_Flags |= LDF_REFRESH_ALL;
1352 DoRenderMethod( obj, opu->opu_GInfo, GREDRAW_REDRAW );
1353 vc = FALSE;
1358 * Top value changed?
1360 if (vc)
1362 DoRenderMethod(obj, opu->opu_GInfo, GREDRAW_UPDATE);
1363 if (ntop != otop)
1365 NewTop(ld, opu->opu_GInfo, obj, ntop);
1366 if (ld->ld_Prop) DoSetMethod(ld->ld_Prop, opu->opu_GInfo, PGA_Top, ntop, TAG_DONE);
1369 else if (ntop != otop)
1371 if (!FindTagItem(GA_ID, opu->opu_AttrList))
1372 if (ld->ld_Prop) DoSetMethod(ld->ld_Prop, opu->opu_GInfo, PGA_Top, ntop, TAG_END);
1373 NewTop(ld, opu->opu_GInfo, obj, ntop);
1376 return 1;
1378 METHOD_END
1380 /// OM_GET
1382 * They want to know something.
1384 METHOD(ListClassGet, struct opGet *, opg)
1386 LD *ld = INST_DATA( cl, obj );
1387 ULONG rc = TRUE, num = ~0;
1388 Tag tag = opg->opg_AttrID;
1389 IPTR *store = opg->opg_Storage;
1391 switch (tag)
1393 case LISTV_LastClicked:
1394 if (ld->ld_LastActive) STORE ld->ld_LastActive->lve_Entry;
1395 else STORE 0;
1396 break;
1398 case LISTV_LastClickedNum:
1399 if (ld->ld_LastActive)
1400 num = ld->ld_LastNum;
1401 STORE num;
1402 break;
1404 case LISTV_NumEntries:
1405 STORE ld->ld_Total;
1406 break;
1408 case LISTV_DropSpot:
1409 STORE ld->ld_DropSpot;
1410 break;
1412 case LISTV_NewPosition:
1413 STORE ld->ld_NewPos;
1414 break;
1416 case LISTV_ViewBounds:
1417 STORE &ld->ld_InnerBox;
1418 break;
1420 case LISTV_LastColumn:
1421 STORE ld->ld_LastCol;
1422 break;
1424 case LISTV_PropObject:
1425 STORE ld->ld_Prop;
1426 break;
1428 case LISTV_ListFont:
1429 STORE ld->ld_ListFont;
1430 break;
1432 case LISTV_Title:
1433 STORE ld->ld_Title;
1434 break;
1436 case LISTV_TitleHook:
1437 STORE ld->ld_TitleHook;
1438 break;
1440 case LISTV_CompareHook:
1441 STORE ld->ld_Compare;
1442 break;
1444 case LISTV_DisplayHook:
1445 STORE ld->ld_Display;
1446 break;
1448 case LISTV_ResourceHook:
1449 STORE ld->ld_Resource;
1450 break;
1452 default:
1453 rc = BGUI_UnpackStructureTag((UBYTE *)ld, ListPackTable, tag, store);
1454 if (!rc) rc = AsmDoSuperMethodA(cl, obj, (Msg)opg);
1455 break;
1457 return rc;
1459 METHOD_END
1461 /// OM_DISPOSE
1463 * Dispose of the object.
1465 METHOD(ListClassDispose, Msg, msg)
1467 LD *ld = INST_DATA(cl, obj);
1468 LVE *lve;
1469 struct lvResource lvr;
1472 * Free all entries.
1474 while ((lve = (LVE *)RemHead((struct List *)&ld->ld_Entries)))
1477 * Do we have a resource hook?
1479 if (ld->ld_Resource)
1482 * Initialize lvResource structure to kill the entry.
1484 lvr.lvr_Command = LVRC_KILL;
1485 lvr.lvr_Entry = lve->lve_Entry;
1488 * Call the hook.
1490 BGUI_CallHookPkt(ld->ld_Resource, (VOID *)obj, (VOID *)&lvr);
1492 else
1495 * Simple deallocation.
1497 BGUI_FreePoolMem(lve->lve_Entry);
1501 * Free the entry node.
1503 BGUI_FreePoolMem(lve);
1507 * Kill the scroller.
1509 if (ld->ld_Prop) AsmDoMethod(ld->ld_Prop, OM_DISPOSE);
1511 if (ld->ld_CD) BGUI_FreePoolMem(ld->ld_CD);
1513 if (ld->ld_Font) BGUI_CloseFont(ld->ld_Font);
1516 * The superclass takes care of the rest.
1518 return AsmDoSuperMethodA(cl, obj, msg);
1520 METHOD_END
1523 /// ListAreaBounds
1525 * Setup the list area bounds.
1527 STATIC ASM VOID ListAreaBounds(REG(a0) Object *obj, REG(a1) LD *ld)
1529 int fh = ld->ld_EntryHeight;
1530 int overhead;
1532 if (ld->ld_ListArea.Width != ld->ld_InnerBox.Width)
1533 ld->ld_Flags &= ~LDF_OFFSETS_VALID;
1536 * Setup list display area and view bounds.
1538 ld->ld_ListArea = ld->ld_InnerBox;
1541 * Title?
1543 if (ld->ld_Title || ld->ld_TitleHook)
1545 ld->ld_ListArea.Top += (fh + 2);
1546 ld->ld_ListArea.Height -= (fh + 2);
1550 * Setup the amount of visible entries
1551 * and the amount of pixels to adjust.
1553 ld->ld_Visible = ld->ld_ListArea.Height / fh;
1554 overhead = ld->ld_ListArea.Height % fh;
1557 * If the list area is larger than the
1558 * total amount of entries we set overhead
1559 * to 0.
1561 if (ld->ld_Total <= ld->ld_Visible)
1563 overhead = 0;
1565 else
1567 ld->ld_ListArea.Top += overhead / 2;
1568 ld->ld_ListArea.Height -= overhead;
1572 * Any change in the overhead?
1574 if (overhead != ld->ld_Overhead)
1576 ld->ld_Overhead = overhead;
1577 ld->ld_Flags |= LDF_REFRESH_ALL;
1581 * Recompute the columns.
1583 if (!(ld->ld_Flags & LDF_OFFSETS_VALID))
1584 GetColumnPositions(obj, ld);
1587 * Check top setting.
1589 ld->ld_Top = max(0, min(ld->ld_Top, ld->ld_Total - ld->ld_Visible));
1592 * Get top entry.
1594 ld->ld_TopEntry = FindNode(ld, ld->ld_Top);
1598 /// RenderColumn
1600 STATIC ASM SAVEDS VOID RenderColumn(REG(a0) char *text, REG(a2) Object *obj, REG(a1) struct lvRender *lvr)
1602 int col = lvr->lvr_Column;
1603 struct BaseInfo *bi;
1604 struct IBox area;
1605 char *term = NULL;
1608 * Setup rendering area.
1610 area.Left = lvr->lvr_Bounds.MinX + 2;
1611 area.Width = lvr->lvr_Bounds.MaxX - lvr->lvr_Bounds.MinX - 3;
1612 area.Top = lvr->lvr_Bounds.MinY;
1613 area.Height = lvr->lvr_Bounds.MaxY - lvr->lvr_Bounds.MinY + 1;
1615 if (strchr(text, '\t'))
1617 while (col--)
1619 text = strchr(text, '\t');
1620 if (text) text++;
1621 else break;
1623 if (text)
1625 term = strchr(text, '\t');
1626 if (term) *term = 0;
1630 #ifdef DEBUG_BGUI
1631 if ((bi = AllocBaseInfoDebug(__FILE__,__LINE__,BI_DrawInfo, lvr->lvr_DrawInfo, BI_RastPort, lvr->lvr_RPort, TAG_DONE)))
1632 #else
1633 if ((bi = AllocBaseInfo(BI_DrawInfo, lvr->lvr_DrawInfo, BI_RastPort, lvr->lvr_RPort, TAG_DONE)))
1634 #endif
1637 * Render entry.
1639 if (text)
1641 RenderText(bi, text, &area);
1642 if (term) *term = '\t';
1645 if (GADGET(obj)->Flags & GFLG_DISABLED)
1647 area.Left -= 2;
1648 area.Width += 4;
1650 * Disable it.
1652 BDisableBox(bi, &area);
1654 FreeBaseInfo(bi);
1658 /// RenderEntry
1660 * Render a single entry.
1662 STATIC VOID RenderEntry(Object *obj, LD *ld, struct BaseInfo *bi, LVE *lve, ULONG top)
1664 BC *bc = BASE_DATA(obj);
1665 struct lvRender lvr;
1666 UBYTE *txt;
1667 UWORD col;
1668 int cx, cw, xoff, yoff;
1669 BOOL sel, clear;
1670 struct Hook *hook;
1671 struct RastPort *rp = bi->bi_RPort;
1673 if (top & REL_ZERO)
1675 top &= ~REL_ZERO;
1676 xoff = yoff = 0;
1678 else
1680 xoff = ld->ld_ListArea.Left;
1681 yoff = ld->ld_ListArea.Top;
1685 * Initialize the lvRender structure.
1687 lvr.lvr_RPort = rp;
1688 lvr.lvr_DrawInfo = bi->bi_DrInfo;
1690 if (lve)
1692 sel = lve->lve_Flags & LVEF_SELECTED;
1693 hook = ld->ld_Display;
1695 lvr.lvr_Entry = lve->lve_Entry;
1696 lvr.lvr_Bounds.MinY = yoff + ld->ld_EntryHeight * top;
1697 lvr.lvr_Bounds.MaxY = lvr.lvr_Bounds.MinY + ld->ld_EntryHeight - 1;
1699 else
1702 * A NULL entry means render the title.
1704 sel = FALSE;
1705 hook = ld->ld_TitleHook;
1707 lvr.lvr_Entry = ld->ld_Title;
1708 lvr.lvr_Bounds.MinY = bc->bc_InnerBox.Top;
1709 lvr.lvr_Bounds.MaxY = bc->bc_InnerBox.Top + ld->ld_EntryHeight - 1;
1713 * Setup state information.
1715 if (GADGET(obj)->Flags & GFLG_DISABLED)
1716 lvr.lvr_State = sel ? LVRS_SELECTED_DISABLED : LVRS_NORMAL_DISABLED;
1717 else
1718 lvr.lvr_State = sel ? LVRS_SELECTED : LVRS_NORMAL;
1720 for (col = 0, cx = xoff; col < ld->ld_Columns; col++)
1722 if (!(ld->ld_CD[col].cd_Flags & LVCF_HIDDEN))
1724 cw = ld->ld_CD[col].cd_Width;
1726 lvr.lvr_Bounds.MinX = cx;
1727 lvr.lvr_Bounds.MaxX = cx + cw - ((col < (ld->ld_Columns - 1)) ? 3 : 1);
1728 lvr.lvr_Column = col;
1730 cx += cw;
1732 if (!(ld->ld_Flags & LDF_ONE_COLUMN) || (col == ld->ld_OneColumn))
1735 * Do we have a display hook?
1737 if (hook)
1739 clear = (ld->ld_Flags & LDF_PRE_CLEAR) || (ld->ld_CD[col].cd_Flags & LVCF_PRECLEAR);
1741 if (clear)
1743 AsmDoMethod(ld->ld_Frame, FRAMEM_BACKFILL, bi, &lvr.lvr_Bounds, sel ? IDS_SELECTED : IDS_NORMAL);
1744 clear = FALSE;
1747 * Call the hook.
1749 txt = (UBYTE *)BGUI_CallHookPkt(hook, (void *)obj, (void *)&lvr);
1751 else
1754 * Pick up the entry text.
1756 clear = TRUE;
1757 txt = lvr.lvr_Entry;
1759 if (txt)
1761 if (clear)
1763 AsmDoMethod(ld->ld_Frame, FRAMEM_BACKFILL, bi, &lvr.lvr_Bounds, sel ? IDS_SELECTED : IDS_NORMAL);
1765 BSetDPenA(bi, sel ? FILLTEXTPEN : TEXTPEN);
1766 RenderColumn(txt, obj, &lvr);
1772 * Done this one.
1774 if (lve) lve->lve_Flags &= ~LVEF_REFRESH;
1777 /// BASE_LAYOUT
1779 * Render the list.
1781 METHOD(ListClassLayout, struct bmLayout *, bml)
1783 LD *ld = INST_DATA(cl, obj);
1784 BC *bc = BASE_DATA(obj);
1785 struct TextAttr *lf;
1786 int sw = 0;
1787 ULONG rc;
1789 if (ld->ld_Prop)
1791 lf = ld->ld_ListFont ? ld->ld_ListFont : bc->bc_TextAttr;
1792 if (lf)
1794 sw = max((lf->ta_YSize * 2) / 3, 16);
1796 else
1798 sw = 16;
1801 * Setup offset.
1803 bml->bml_Bounds->Width -= sw;
1807 * First the superclass.
1809 rc = AsmDoSuperMethodA(cl, obj, (Msg)bml);
1811 if (sw) bml->bml_Bounds->Width += sw;
1813 return rc;
1815 METHOD_END
1817 /// BASE_RENDER
1819 * Render the list.
1821 METHOD(ListClassRender, struct bmRender *, bmr)
1823 LD *ld = INST_DATA(cl, obj);
1824 BC *bc = BASE_DATA(obj);
1825 struct BaseInfo *bi = bmr->bmr_BInfo;
1826 struct RastPort *rp = bi->bi_RPort;
1827 struct TextFont *tf = ld->ld_Font, *of;
1828 struct TextAttr *lf = ld->ld_ListFont;
1829 struct Rectangle rect;
1830 ULONG num, a;
1831 LVE *lve;
1832 int x, y, w, h, sw;
1833 UWORD overhead;
1836 * Render the baseclass.
1838 AsmDoSuperMethodA(cl, obj, (Msg)bmr);
1840 if (!lf) lf = bc->bc_TextAttr;
1842 sw = lf ? ((lf->ta_YSize * 2) / 3) : 0;
1843 if (sw < 16) sw = 16;
1846 * Setup the font.
1848 of = rp->Font;
1849 if (!tf)
1851 if (lf)
1853 if ((tf = BGUI_OpenFont(lf)))
1855 ld->ld_Font = tf;
1856 BSetFont(bi, tf);
1858 else
1860 tf = of;
1863 else
1865 tf = of;
1870 * Setup entry height always even.
1872 ld->ld_EntryHeight = (tf->tf_YSize + 1) & (ULONG)~1;
1874 overhead=ld->ld_Overhead;
1877 * Setup the list area bounds.
1879 ListAreaBounds(obj, ld);
1881 x = bc->bc_InnerBox.Left;
1882 w = bc->bc_InnerBox.Width;
1885 * Complete redraw?
1887 if (overhead<ld->ld_Overhead
1888 || bmr->bmr_Flags == GREDRAW_REDRAW)
1890 if(overhead<ld->ld_Overhead)
1891 overhead=ld->ld_Overhead;
1894 * Clear the overhead.
1896 if ((h = overhead >> 1))
1898 y = bc->bc_InnerBox.Top;
1899 if (ld->ld_Title || ld->ld_TitleHook) y += ld->ld_EntryHeight+2;
1901 rect.MinX = x; rect.MaxX = x + w - 1;
1902 rect.MinY = y; rect.MaxY = y + h - 1;
1904 AsmDoMethod(bc->bc_Frame, FRAMEM_BACKFILL, bi, &rect, IDS_NORMAL);
1907 if ((h = overhead - h))
1909 y = bc->bc_InnerBox.Top + bc->bc_InnerBox.Height - h;
1911 rect.MinX = x; rect.MaxX = x + w - 1;
1912 rect.MinY = y; rect.MaxY = y + h - 1;
1914 AsmDoMethod(bc->bc_Frame, FRAMEM_BACKFILL, bi, &rect, IDS_NORMAL);
1918 * Draw the separators.
1920 ColumnSeparators(ld, bi, bc->bc_InnerBox.Left, bc->bc_InnerBox.Top, bc->bc_InnerBox.Height);
1923 * If we have a title render it.
1925 if (ld->ld_Title || ld->ld_TitleHook)
1928 * Just in case the font changed.
1930 if (tf) BSetFont(bi, tf);
1932 RenderEntry(obj, ld, bi, NULL, 0);
1934 if (ld->ld_Columns > 1)
1936 y = bc->bc_InnerBox.Top + ld->ld_EntryHeight;
1937 BSetDPenA(bi, ld->ld_Flags & LDF_READ_ONLY ? SHINEPEN : SHADOWPEN);
1938 HLine(rp, x, y++, x + w - 1);
1939 BSetDPenA(bi, ld->ld_Flags & LDF_READ_ONLY ? SHADOWPEN : SHINEPEN);
1940 HLine(rp, x, y, x + w - 1);
1945 * Just do it.
1947 ld->ld_Flags |= LDF_REFRESH_ALL;
1951 * Just in case the font changed.
1953 if (tf) BSetFont(bi, tf);
1956 * Loop through the entries.
1958 for (num = ld->ld_Top, a = 0; (a < ld->ld_Visible) && (num < ld->ld_Total); num++, a++)
1961 * Render the entry.
1963 if ((lve = FindNodeQuick(ld, num)))
1966 * Only render when necessary.
1968 if ((ld->ld_Flags & LDF_REFRESH_ALL) || (lve->lve_Flags & LVEF_REFRESH))
1970 RenderEntry(obj, ld, bi, lve, a);
1976 * Disabled?
1978 if ((!(ld->ld_Flags & LDF_CUSTOMDISABLE)) && ld->ld_Display)
1980 if (GADGET(obj)->Flags & GFLG_DISABLED)
1982 BDisableBox(bi, &bc->bc_HitBox);
1987 * Done.
1989 ld->ld_Flags &= ~LDF_REFRESH_ALL;
1992 * Replace the font.
1994 BSetFont(bi, of);
1996 if (ld->ld_Prop)
1999 * Setup scroller bounds.
2001 GADGETBOX(ld->ld_Prop)->Left = ld->ld_HitBox.Left + ld->ld_HitBox.Width;
2002 GADGETBOX(ld->ld_Prop)->Top = ld->ld_HitBox.Top;
2003 GADGETBOX(ld->ld_Prop)->Width = sw;
2004 GADGETBOX(ld->ld_Prop)->Height = ld->ld_HitBox.Height;
2007 * Set top, total etc.
2009 DoSetMethodNG(ld->ld_Prop, PGA_Top, ld->ld_Top, PGA_Total, ld->ld_Total,
2010 PGA_Visible, ld->ld_Visible, TAG_DONE);
2013 * Re-render the scroller.
2015 AsmDoMethod(ld->ld_Prop, GM_RENDER, bi, rp, bmr->bmr_Flags);
2017 return 1;
2019 METHOD_END
2023 * Find out over which entry the mouse is located.
2025 STATIC ASM LONG MouseOverEntry(REG(a0) LD *ld, REG(d0) LONG t)
2027 t -= ld->ld_ListArea.Top;
2028 if (t < 0) return -1;
2030 t = (t / ld->ld_EntryHeight) + ld->ld_Top;
2032 if (t > ld->ld_Total) t = ld->ld_Total;
2034 return t;
2038 * Perform multi-(de)selection.
2040 STATIC ASM BOOL MultiSelect( REG(a0) LD *ld, REG(d0) ULONG active )
2042 LVE *node = FindNodeQuick( ld, ld->ld_MultiStart ), *anode = FindNodeQuick( ld, active );
2043 BOOL rc = FALSE;
2046 * Backward?
2048 if ( ld->ld_MultiStart > active ) {
2050 * Loop through the entries.
2052 for ( ; ; node = node->lve_Prev ) {
2054 * Select entries?
2056 if ( ! ld->ld_MultiMode ) {
2058 * Yes.
2060 if ( ! ( node->lve_Flags & LVEF_SELECTED )) {
2061 node->lve_Flags |= LVEF_SELECTED | LVEF_REFRESH;
2062 rc = TRUE;
2065 else
2067 if (node->lve_Flags & LVEF_SELECTED)
2069 node->lve_Flags &= ~LVEF_SELECTED;
2070 node->lve_Flags |= LVEF_REFRESH;
2071 rc = TRUE;
2075 * Done?
2077 if ( node == anode )
2078 return( rc );
2080 } else {
2082 * Loop through the entries.
2084 for ( ; ; node = node->lve_Next ) {
2086 * Select entries?
2088 if ( ! ld->ld_MultiMode ) {
2090 * Yes.
2092 if ( ! ( node->lve_Flags & LVEF_SELECTED )) {
2093 node->lve_Flags |= LVEF_SELECTED | LVEF_REFRESH;
2094 rc = TRUE;
2096 } else {
2097 if ( node->lve_Flags & LVEF_SELECTED ) {
2098 node->lve_Flags &= ~LVEF_SELECTED;
2099 node->lve_Flags |= LVEF_REFRESH;
2100 rc = TRUE;
2104 * Done?
2106 if ( node == anode )
2107 return( rc );
2110 return rc;
2112 /// GM_HITTEST
2114 * Test if the gadget was hit.
2116 METHOD(ListClassHitTest, struct gpHitTest *, gph)
2118 LD *ld = INST_DATA(cl, obj);
2119 BC *bc = BASE_DATA(obj);
2120 ULONG rc = 0;
2123 * Get absolute click position.
2125 WORD l = GADGET(obj)->LeftEdge + gph->gpht_Mouse.X;
2126 WORD t = GADGET(obj)->TopEdge + gph->gpht_Mouse.Y;
2128 if (PointInBox(&bc->bc_InnerBox, l, t))
2130 * Hit inside the list area?
2132 return GMR_GADGETHIT;
2134 if (ld->ld_Prop)
2137 * Route the message.
2139 rc = ForwardMsg(obj, ld->ld_Prop, (Msg)gph);
2141 if (rc == GMR_GADGETHIT)
2143 * Mark the scroller active.
2145 ld->ld_Flags |= LDF_PROPACTIVE;
2147 return rc;
2149 METHOD_END
2151 /// GM_GOACTIVE
2153 * They want us to go active.
2155 METHOD(ListClassGoActive, struct gpInput *, gpi)
2157 LD *ld = INST_DATA(cl, obj);
2158 LVE *lve;
2159 ULONG rc = GMR_NOREUSE, last = ld->ld_LastNum;
2160 int col, dx;
2161 struct GadgetInfo *gi = gpi->gpi_GInfo;
2164 * Get hit position.
2166 int x = gpi->gpi_Mouse.X;
2167 int y = gpi->gpi_Mouse.Y;
2168 //int l = x + GADGET(obj)->LeftEdge;
2169 int t = y + GADGET(obj)->TopEdge;
2172 * We do not go active if we were activated by ActivateGadget().
2174 if (!gpi->gpi_IEvent)
2175 return GMR_NOREUSE;
2178 * Check for left mouse button down event
2180 if ((gpi->gpi_IEvent->ie_Class == IECLASS_RAWMOUSE) && (gpi->gpi_IEvent->ie_Code == SELECTDOWN))
2183 * Update column positions if required
2185 if (!(ld->ld_Flags & LDF_OFFSETS_VALID))
2186 GetColumnPositions(obj, ld);
2189 * Step through column positions array, marking a hit if
2190 * mouse is within 4 pixels either side of column separator.
2192 col = ld->ld_Columns - 1;
2193 while (col--)
2195 if (!(ld->ld_CD[col].cd_Flags & LVCF_HIDDEN))
2197 dx = x - ld->ld_CD[col + 1].cd_Offset;
2199 * If hit column separator, set Dragging to TRUE, record
2200 * drag column and initial drag position, draw first
2201 * drag line and return GMR_MEACTIVE.
2203 if ((dx >= -4) && (dx <= 4))
2206 * Check for column dragging enabled.
2208 if ((ld->ld_Flags & LDF_ALLOW_DRAG) || (ld->ld_CD[col].cd_Flags & LVCF_DRAGGABLE))
2210 ld->ld_Flags |= LDF_DRAGGING_COLUMN;
2211 ld->ld_DragColumn = col;
2212 ld->ld_DragXLine = ld->ld_CD[col+1].cd_Offset;
2213 DrawDragLine(ld, gi);
2215 return GMR_MEACTIVE;
2222 if (ld->ld_Prop)
2225 * Prop hit?
2227 if (ld->ld_Flags & LDF_PROPACTIVE)
2230 * Adjust coordinates and re-direct message.
2232 return ForwardMsg(obj, ld->ld_Prop, (Msg)gpi) & ~GMR_VERIFY;
2237 * Can we go active?
2239 if (( GADGET( obj )->Flags & GFLG_DISABLED ) || ( ld->ld_Flags & LDF_READ_ONLY ) || ( ld->ld_Flags & LDF_LIST_BUSY ))
2240 return( GMR_NOREUSE );
2243 * Get the entry which lays under the mouse.
2245 ld->ld_ActiveEntry = (ULONG)MouseOverEntry(ld, t);
2247 col = ld->ld_Columns;
2248 while (--col)
2250 if (x >= ld->ld_CD[col].cd_Offset)
2251 break;
2253 ld->ld_LastCol = col;
2256 * Is it in range?
2258 if (ld->ld_ActiveEntry < ld->ld_Total)
2260 if ((lve = ld->ld_LastActive = FindNodeQuick(ld, ld->ld_ActiveEntry)))
2262 ld->ld_LastNum = ld->ld_ActiveEntry;
2264 * If we are not a multi-select object we
2265 * de-select all entries. Otherwise we mark the
2266 * entry which initiated the multi-(de)select.
2268 if (!(ld->ld_Flags & LDF_MULTI_SELECT)) DeSelect(ld);
2269 else {
2271 * De-select entries if shift isn't down.
2273 if (!(ld->ld_Flags & LDF_NOSHIFT))
2275 if (!(gpi->gpi_IEvent->ie_Qualifier & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT)))
2276 DeSelect(ld);
2280 * Multi-selection. When MultiMode is 1 we need
2281 * to multi-deselect. Otherwise we multi-select.
2283 if (lve->lve_Flags & LVEF_SELECTED) ld->ld_MultiMode = 1;
2284 else ld->ld_MultiMode = 0;
2287 * Setup starting position.
2289 ld->ld_MultiStart = ld->ld_ActiveEntry;
2292 * Select entry if necessary.
2294 if ( ! ld->ld_MultiMode ) {
2296 * Note the time we got clicked.
2298 CurrentTime( &ld->ld_Secs[ 0 ], &ld->ld_Mics[ 0 ] );
2299 lve->lve_Flags |= LVEF_SELECTED | LVEF_REFRESH;
2300 } else {
2302 * De-selection only in multi-mode.
2304 if ( ld->ld_Flags & LDF_MULTI_SELECT ) {
2306 * The same selection as the previous?
2308 if ( ld->ld_ActiveEntry == last ) {
2310 * Yes, time it.
2312 CurrentTime( &ld->ld_Secs[ 1 ], &ld->ld_Mics[ 1 ] );
2315 * Double clicked the selection?
2317 if ( ! DoubleClick( ld->ld_Secs[ 0 ], ld->ld_Mics[ 0 ], ld->ld_Secs[ 1 ], ld->ld_Mics[ 1 ] )) {
2319 * No, deselect it.
2321 lve->lve_Flags &= ~LVEF_SELECTED;
2322 lve->lve_Flags |= LVEF_REFRESH;
2324 } else {
2326 * Deselect the entry.
2328 lve->lve_Flags &= ~LVEF_SELECTED;
2329 lve->lve_Flags |= LVEF_REFRESH;
2331 } else
2332 lve->lve_Flags |= LVEF_SELECTED | LVEF_REFRESH;
2335 * Notify & Re-render.
2337 DoNotifyMethod(obj, gi, OPUF_INTERIM, GA_ID, GADGET(obj)->GadgetID, LISTV_Entry, lve->lve_Entry,
2338 LISTV_EntryNumber, ld->ld_LastNum, LISTV_LastColumn, ld->ld_LastCol, TAG_DONE);
2339 DoRenderMethod(obj, gi, GREDRAW_UPDATE );
2342 * Setup any drag and drop buffers we may need.
2344 if ( AsmDoSuperMethodA( cl, obj, ( Msg )gpi ) == GMR_MEACTIVE )
2345 ld->ld_Flags |= LDF_DRAGGABLE;
2347 rc = GMR_MEACTIVE;
2349 else if (ld->ld_ActiveEntry == (ULONG)-1)
2352 * Notify.
2354 DoNotifyMethod(obj, gi, OPUF_INTERIM, GA_ID, GADGET(obj)->GadgetID, LISTV_Entry, -1,
2355 LISTV_EntryNumber, -1, LISTV_LastColumn, ld->ld_LastCol, TAG_DONE);
2357 return rc;
2359 METHOD_END
2361 /// GM_HANDLEINPUT
2363 * Handle user input.
2365 METHOD(ListClassHandleInput, struct gpInput *, gpi)
2367 LD *ld = INST_DATA(cl, obj);
2368 LVE *lve;
2369 struct gpInput hit;
2370 int vc, xmin, xmax, dx;
2371 ULONG rc = GMR_MEACTIVE, otop = ld->ld_Top, ntop = ld->ld_Top;
2372 LONG nc;
2373 int dcol = ld->ld_DragColumn;
2374 int totalwidth, totalweight;
2375 struct GadgetInfo *gi = gpi->gpi_GInfo;
2376 CD *cd, *cd2, *cd3;
2380 * Get mouse position.
2382 int x = gpi->gpi_Mouse.X;
2383 int y = gpi->gpi_Mouse.Y;
2384 //int l = x + GADGET(obj)->LeftEdge;
2385 int t = y + GADGET(obj)->TopEdge;
2388 * List in use?
2390 if (ld->ld_Flags & LDF_LIST_BUSY)
2391 return rc;
2394 * Dragging column?
2396 if (ld->ld_Flags & LDF_DRAGGING_COLUMN)
2398 cd = ld->ld_CD + dcol;
2399 cd2 = cd + 1;
2400 cd3 = cd + 2;
2403 * Return this by default, implying we want to
2404 * remain the active gadget.
2406 rc = GMR_MEACTIVE;
2409 * If this is a mouse event (movement or button)
2412 if (gpi->gpi_IEvent->ie_Class == IECLASS_RAWMOUSE)
2415 * Update column offsets if required
2417 if (!(ld->ld_Flags & LDF_OFFSETS_VALID))
2418 GetColumnPositions(obj, ld);
2421 * gpi_Mouse.X has mouse-x relative to left side of gadget
2422 * hitbox. Set minimum and maximum values for x at positions
2423 * of columns either side of the one being dragged. Add a
2424 * minimum column width to those limits as well.
2426 xmin = cd->cd_Offset + cd->cd_MinWidth;
2427 xmax = min(cd->cd_Offset + cd->cd_MaxWidth, cd3->cd_Offset - cd2->cd_MinWidth);
2429 if (xmax < xmin) /* just in case column is */
2430 { /* already very narrow, */
2431 xmin = xmax = cd2->cd_Offset; /* stop user from adjusting it */
2435 * Prevent dragline from wandering outside of limits.
2437 if (x < xmin) x = xmin;
2438 if (x > xmax) x = xmax;
2441 * Don't update if dragline position hasn't changed.
2444 if (x != ld->ld_DragXLine)
2447 * Reposition dragline by drawing again at old position
2448 * using complement mode, then drawing at new position.
2451 DrawDragLine(ld, gi);
2452 ld->ld_DragXLine = x;
2453 DrawDragLine(ld, gi);
2457 * Check for left mouse button release: implies user
2458 * has stopped dragging column and it's time to recalculate
2459 * the column weights.
2461 if (gpi->gpi_IEvent->ie_Code == SELECTUP)
2463 rc = GMR_NOREUSE; /* we will use LMB-up event */
2466 * No need to do anything if column position not changed.
2468 if ((dx = x - cd2->cd_Offset))
2471 * Set new column position at x.
2473 cd2->cd_Offset = x;
2474 cd2->cd_Width -= dx;
2475 cd->cd_Width += dx;
2478 * Set new weights for dragged column and the one to the
2479 * right of it, by redistributing the total of their
2480 * original weights in the ratio of their new positions
2481 * over the original total distance between them. Both
2482 * total weight and total distance remain unchanged.
2484 totalweight = cd->cd_Weight + cd2->cd_Weight;
2485 totalwidth = cd3->cd_Offset - cd->cd_Offset;
2487 cd2->cd_Weight = ((cd3->cd_Offset - x) * totalweight) / totalwidth;
2488 cd->cd_Weight = totalweight - cd2->cd_Weight;
2491 * If we have a GadgetInfo, invoke GM_RENDER
2492 * to update gadget visuals.
2495 if (gi)
2497 DoRenderMethod(obj, gi, GREDRAW_REDRAW);
2500 * No need for GoInactive() to erase dragline:
2502 ld->ld_Flags |= LDF_NEW_COLUMN_POS;
2505 } /* endif posn changed */
2507 } /* endif LMB down */
2509 * If event was menu button down, abandon dragging
2510 * and let Intuition activate menus.
2513 if (gpi->gpi_IEvent->ie_Code == MENUDOWN)
2514 rc = GMR_REUSE;
2516 } /* endif mouse event */
2519 * If event was either shift key going down, abandon
2520 * dragging as per BGUI standard.
2523 if (gpi->gpi_IEvent->ie_Class == IECLASS_RAWKEY)
2525 if ((gpi->gpi_IEvent->ie_Code == 0x60) || (gpi->gpi_IEvent->ie_Code == 0x61))
2526 rc = GMR_REUSE;
2528 return rc;
2532 * Prop gadget active?
2534 if (ld->ld_Prop && (ld->ld_Flags & LDF_PROPACTIVE))
2537 * Adjust coordinates and route message.
2539 return ForwardMsg(obj, ld->ld_Prop, (Msg)gpi) & ~GMR_VERIFY;
2542 hit = *gpi;
2543 hit.MethodID = BASE_DRAGGING;
2545 nc = AsmDoMethodA(obj, (Msg)&hit);
2547 switch (nc)
2549 case BDR_DROP:
2550 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);
2551 rc = GMR_VERIFY;
2552 break;
2554 case BDR_CANCEL:
2555 rc = GMR_NOREUSE;
2556 break;
2558 case BDR_NONE:
2560 * Get the entry which lies under the mouse.
2562 nc = MouseOverEntry(ld, t);
2565 * There are only so much entries...
2567 if (nc < 0) nc = ld->ld_Top - 1;
2568 if (nc < 0) nc = 0;
2569 if (nc >= ld->ld_Total) nc = ld->ld_Total - 1;
2572 * Let's see what we got...
2574 if (gpi->gpi_IEvent->ie_Class == IECLASS_RAWMOUSE)
2577 * We do not support drag-selection when we
2578 * are in drag and drop mode.
2580 if (!(ld->ld_Flags & LDF_DRAGGABLE))
2583 * We only respond when:
2584 * A) The entry under the mouse changed.
2585 * B) The entry under the mouse is in the visible area.
2587 if ((nc != ld->ld_ActiveEntry) && (nc >= ld->ld_Top) && (nc < (ld->ld_Top + ld->ld_Visible)))
2590 * Mark the new entry.
2592 ld->ld_ActiveEntry = nc;
2595 * Get the node.
2597 if ((lve = ld->ld_LastActive = FindNodeQuick(ld, nc)))
2599 ld->ld_LastNum = nc;
2601 * Are we a multi-select object?
2603 if (!(ld->ld_Flags & LDF_MULTI_SELECT))
2606 * No. Deselect other entries.
2608 DeSelect(ld);
2611 * We need a visual change.
2613 vc = TRUE;
2616 * Select entry.
2618 lve->lve_Flags |= LVEF_SELECTED | LVEF_REFRESH;
2619 } else
2621 * Do a multi-(de)select.
2623 vc = MultiSelect( ld, nc );
2626 * Update visuals if necessary.
2628 if (vc) DoRenderMethod(obj, gi, GREDRAW_UPDATE);
2629 DoNotifyMethod(obj, gi, OPUF_INTERIM, GA_ID, GADGET(obj)->GadgetID, LISTV_Entry, lve->lve_Entry, LISTV_EntryNumber, ld->ld_LastNum, TAG_END );
2635 * What code do we have...
2637 switch (gpi->gpi_IEvent->ie_Code)
2639 case SELECTUP:
2641 * Releasing the left button
2642 * de-activates the object.
2644 rc = GMR_NOREUSE | GMR_VERIFY;
2645 CurrentTime(&ld->ld_Secs[0], &ld->ld_Mics[0]);
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;
2649 case MENUDOWN:
2651 * Reuse menu events.
2653 rc = GMR_REUSE | GMR_VERIFY;
2654 DoNotifyMethod(obj, gi, 0, GA_ID, GADGET(obj)->GadgetID, LISTV_Entry, ld->ld_LastActive->lve_Entry, LISTV_EntryNumber, ld->ld_LastNum, TAG_END );
2655 break;
2658 else if (gpi->gpi_IEvent->ie_Class == IECLASS_TIMER)
2661 * When the mouse is moved above or below
2662 * the visible area the entries scroll up
2663 * or down using timer events for a delay.
2665 if ((nc != ld->ld_ActiveEntry) && (!(ld->ld_Flags & LDF_DRAGGABLE)))
2668 * When the active entry is located before
2669 * the top entry we scroll up one entry. When the
2670 * entry is located after the last visible entry
2671 * we scroll down one entry.
2673 vc = FALSE;
2674 if (nc >= ntop + ld->ld_Visible)
2676 nc = ntop++ + ld->ld_Visible;
2677 vc = TRUE;
2679 else if (nc < ntop)
2681 nc = --ntop;
2682 vc = TRUE;
2685 if (vc)
2688 * Set the new entry.
2690 ld->ld_LastNum = ld->ld_ActiveEntry = nc;
2693 * Find the entry.
2695 if ((lve = ld->ld_LastActive = FindNodeQuick(ld, nc)))
2698 * Are we a multi-select object?
2700 if (ld->ld_Flags & LDF_MULTI_SELECT)
2703 * Do a multi-(de)select.
2705 vc = MultiSelect(ld, nc);
2707 else
2710 * No. Deselect all entries.
2712 DeSelect(ld);
2714 * Select the entry.
2716 lve->lve_Flags |= LVEF_SELECTED | LVEF_REFRESH;
2718 * We need a visual change.
2720 vc = TRUE;
2724 * Update visuals when necessary.
2726 if (vc || (otop != ntop))
2729 * Top changed?
2731 DoRenderMethod(obj, gi, GREDRAW_UPDATE);
2732 if (otop != ntop)
2734 NewTop(ld, gi, obj, ntop);
2735 if (ld->ld_Prop)
2736 DoSetMethod(ld->ld_Prop, gi, PGA_Top, ntop, TAG_END);
2738 DoNotifyMethod(obj, gi, OPUF_INTERIM, GA_ID, GADGET(obj)->GadgetID, LISTV_Entry, lve->lve_Entry, LISTV_EntryNumber, ld->ld_LastNum, TAG_END);
2744 break;
2746 return rc;
2748 METHOD_END
2750 /// GM_GOINACTIVE
2752 * Go inactive.
2754 METHOD(ListClassGoInActive, struct gpGoInactive *, ggi)
2756 LD *ld = INST_DATA(cl, obj);
2758 if (ld->ld_Flags & LDF_DRAGGING_COLUMN)
2760 if (!(ld->ld_Flags & LDF_NEW_COLUMN_POS))
2761 DrawDragLine(ld, ggi->gpgi_GInfo);
2763 ld->ld_Flags &= ~(LDF_DRAGGING_COLUMN|LDF_NEW_COLUMN_POS);
2764 return 0;
2766 else
2769 * Clear draggable bit.
2771 ld->ld_Flags &= ~LDF_DRAGGABLE;
2773 if (ld->ld_Prop)
2776 * If the scroller was active pass this message on for compatibility reasons.
2778 if (ld->ld_Flags & LDF_PROPACTIVE)
2781 * Mark the scroller as not active.
2783 ld->ld_Flags &= ~LDF_PROPACTIVE;
2785 return AsmDoMethodA(ld->ld_Prop, (Msg)ggi);
2788 return AsmDoSuperMethodA(cl, obj, (Msg)ggi);
2791 METHOD_END
2793 /// BASE_DIMENSIONS
2795 * They want our minimum dimensions.
2797 METHOD(ListClassDimensions, struct bmDimensions *, bmd)
2799 LD *ld = INST_DATA(cl, obj);
2800 struct BaseInfo *bi = bmd->bmd_BInfo;
2801 UWORD my, mx = 0, mpx = 0, mpy = 0;
2802 int th = ld->ld_EntryHeight, col;
2803 struct TextAttr *lf = ld->ld_ListFont;
2806 * First the prop...
2808 if (ld->ld_Prop) AsmDoMethod(ld->ld_Prop, GRM_DIMENSIONS, bi, bi->bi_RPort, &mpx, &mpy, 0);
2810 if (!lf)
2813 * Pickup superclass font.
2815 Get_SuperAttr(cl, obj, BT_TextAttr, (IPTR *)&lf);
2818 * Setup entry height always even.
2820 ld->ld_EntryHeight = (lf->ta_YSize + 1) & (ULONG)~1;
2823 * Calculate minimum x&y size.
2825 my = ld->ld_EntryHeight * ld->ld_MinShown;
2827 for (col = 0; col < ld->ld_Columns; col++)
2829 mx += ld->ld_CD[col].cd_MinWidth;
2833 * The listview grows when a title
2834 * hook is specified.
2836 if (ld->ld_Title)
2837 my += th + 4;
2839 mpx += mx;
2840 if (my > mpy) mpy = my;
2843 * Set it.
2845 return CalcDimensions(cl, obj, bmd, mpx, mpy);
2847 METHOD_END
2849 /// WM_KEYACTIVE
2851 * Key activation.
2853 METHOD(ListClassKeyActive, struct wmKeyInput *, wmki )
2855 LD *ld = ( LD * )INST_DATA( cl, obj );
2856 UWORD qual = wmki->wmki_IEvent->ie_Qualifier;
2857 LONG nnum = 0, otop = ld->ld_Top, newtop = otop;
2858 LVE *node;
2859 BOOL sel = FALSE;
2862 * Read-only?
2864 if ( ld->ld_Flags & LDF_READ_ONLY ) {
2866 * Shifted scrolls up.
2868 if ( qual & ( IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT )) {
2869 if ( newtop ) newtop--;
2870 } else {
2871 if ( newtop < ( ld->ld_Total - ld->ld_Visible )) newtop++;
2875 * Top changed?
2877 if ( newtop != otop )
2878 DoSetMethod( obj, wmki->wmki_GInfo, LISTV_Top, newtop, TAG_END );
2881 * Ignore this.
2883 *( wmki->wmki_ID ) = WMHI_IGNORE;
2884 } else {
2886 * Find the selected node.
2888 for ( node = ld->ld_Entries.lvl_First; node->lve_Next; node = node->lve_Next, nnum++ ) {
2889 if ( node->lve_Flags & LVEF_SELECTED ) {
2890 sel = TRUE;
2891 break;
2896 * Found?
2898 if ( ! sel )
2900 * Select first entry.
2902 DoSetMethod( obj, wmki->wmki_GInfo, LISTV_Select, 0L, TAG_END );
2903 else {
2904 if ( qual & ( IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT )) {
2906 * Shifted selectes the previous entry.
2908 nnum=LISTV_Select_Previous;
2909 } else {
2911 * Normal the next entry.
2913 nnum=LISTV_Select_Next;
2917 * Select the new entry.
2919 DoSetMethod( obj, wmki->wmki_GInfo, LISTV_Select, nnum, TAG_END );
2922 * Ignore this..
2924 *( wmki->wmki_ID ) = WMHI_IGNORE;
2927 return( WMKF_VERIFY );
2929 METHOD_END
2932 * Get entry predecessor, position and add method.
2934 STATIC ASM VOID EntryPosHow( REG(a0) LD *ld, REG(a1) LVE **lve, REG(d0) ULONG pos, REG(a2) ULONG *how )
2936 if ( ! pos ) {
2938 * Position of 0 means add to the head.
2940 *how = LVAP_HEAD;
2941 *lve = NULL;
2942 } else if ( pos >= ld->ld_Total ) {
2944 * A position larger or equal to the
2945 * number of available entries means
2946 * add to the tail.
2948 *how = LVAP_TAIL;
2949 *lve = NULL;
2950 } else {
2952 * Look up the entry to insert after.
2954 *how = 0L;
2955 *lve = FindNodeQuick( ld, pos - 1 );
2961 * (Multi)Select entry and/or make it visible.
2963 STATIC 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)
2965 ULONG tag;
2968 * Make visible? Select?
2970 if (flags & LVASF_SELECT)
2973 * Visible or not?
2975 tag = (flags & LVASF_NOT_VISIBLE) ? LISTV_SelectNotVisible : LISTV_Select;
2977 DoSetMethod(obj, gi, tag, number, TAG_DONE);
2979 else if (flags & LVASF_MULTISELECT)
2982 * Visible or not?
2984 tag = (flags & LVASF_NOT_VISIBLE) ? LISTV_SelectMultiNotVisible : LISTV_SelectMulti;
2986 DoSetMethod(obj, gi, tag, number, TAG_DONE);
2988 else if (flags & LVASF_MAKEVISIBLE)
2991 * Just make it visible.
2993 DoSetMethod(obj, gi, LISTV_MakeVisible, number, TAG_DONE);
2995 else
2998 * Re-render list.
3000 ld->ld_Flags |= LDF_REFRESH_ALL;
3001 DoRenderMethod(obj, gi, GREDRAW_UPDATE);
3004 /// LVM_INSERTENTRIES
3006 * Insert entries.
3008 METHOD(ListClassInsertEntries, struct lvmInsertEntries *, lvmi)
3010 LD *ld = INST_DATA( cl, obj );
3011 LVE *lve;
3012 ULONG rc, pos = lvmi->lvmi_Pos, how;
3015 * Where do we insert them.
3017 EntryPosHow(ld, &lve, pos, &how);
3020 * Insert the entries.
3022 rc = AddEntries(ld, lvmi->lvmi_Entries, obj, how, lve);
3025 * We want the list completely refreshed.
3027 ld->ld_Flags |= LDF_REFRESH_ALL;
3030 * Render the object.
3032 DoRenderMethod(obj, lvmi->lvmi_GInfo, GREDRAW_UPDATE);
3034 return rc;
3036 METHOD_END
3038 /// LVM_INSERTSINGLE
3040 * Insert a single entry.
3042 METHOD(ListClassInsertSingle, struct lvmInsertSingle *, lvis)
3044 LD *ld = INST_DATA(cl, obj);
3045 struct lvmInsertEntries lvmi;
3046 APTR entries[2];
3047 ULONG rc;
3050 * Set it up.
3052 entries[0] = lvis->lvis_Entry;
3053 entries[1] = NULL;
3055 lvmi.MethodID = LVM_INSERTENTRIES;
3056 lvmi.lvmi_GInfo = lvis->lvis_GInfo;
3057 lvmi.lvmi_Entries = entries;
3058 lvmi.lvmi_Pos = lvis->lvis_Pos;
3061 * Insert them.
3063 rc = METHOD_CALL(ListClassInsertEntries, cl, obj, (Msg)&lvmi, (APTR)getreg(REG_A4));
3066 * Select the entry or make it visible
3067 * or just redraw the list.
3069 ld->ld_LastAdded->lve_Flags |= LVEF_REFRESH;
3072 * Make visible? Select?
3074 DoEntry(lvis->lvis_GInfo, obj, ld, lvis->lvis_Flags, lvis->lvis_Pos >= ld->ld_Total ? ld->ld_Total - 1 : lvis->lvis_Pos );
3076 return rc;
3078 METHOD_END
3080 /// LVM_ADDENTRIES
3082 * Add entries.
3084 METHOD(ListClassAddEntries, struct lvmAddEntries *, lva)
3086 LD *ld = INST_DATA( cl, obj );
3087 ULONG rc;
3090 * Add the entries.
3092 rc = AddEntries(ld, lva->lvma_Entries, obj, lva->lvma_How, NULL);
3095 * We want the list completely refreshed.
3097 ld->ld_Flags |= LDF_REFRESH_ALL;
3100 * Render the object.
3102 DoRenderMethod(obj, lva->lvma_GInfo, GREDRAW_UPDATE);
3104 return rc;
3106 METHOD_END
3108 /// LVM_ADDSINGLE
3110 * Add a single entry.
3112 METHOD(ListClassAddSingle, struct lvmAddSingle *, lva)
3114 LD *ld = INST_DATA(cl, obj);
3115 APTR entries[2];
3116 ULONG number, rc;
3117 LVE *tmp;
3120 * Set it up.
3122 entries[0] = lva->lvma_Entry;
3123 entries[1] = NULL;
3126 * Add it.
3128 ld->ld_LastAdded = NULL;
3129 rc = AddEntries(ld, entries, obj, lva->lvma_How, NULL);
3132 * No need to compute the number of the
3133 * entry when it is added to the start or
3134 * the end of the list.
3136 if (lva->lvma_How == LVAP_HEAD) number = 0;
3137 else if (lva->lvma_How == LVAP_TAIL) number = ld->ld_Total - 1;
3138 else
3141 * Get it's number.
3143 for (tmp = ld->ld_Entries.lvl_First, number = 0; tmp->lve_Next; tmp = tmp->lve_Next, number++)
3145 if (tmp == ld->ld_LastAdded)
3146 break;
3149 * Select the entry or make it visible
3150 * or just redraw the list.
3152 tmp->lve_Flags |= LVEF_REFRESH;
3156 * New top visible entry?
3158 if (number == ld->ld_Top)
3159 ld->ld_TopEntry = ld->ld_LastAdded;
3162 * Make visible? Select?
3164 DoEntry(lva->lvma_GInfo, obj, ld, lva->lvma_Flags, number);
3166 return rc;
3168 METHOD_END
3170 /// LVM_CLEAR
3172 * Clear the entire list.
3174 METHOD(ListClassClear, struct lvmCommand *, lvc)
3176 LD *ld = INST_DATA(cl, obj);
3177 LVE *lve;
3178 struct lvResource lvr;
3181 * Initialize lvResource structure to kill the entry.
3183 lvr.lvr_Command = LVRC_KILL;
3186 * Say were busy.
3188 ld->ld_Flags |= LDF_LIST_BUSY;
3191 * Free all entries.
3193 while ((lve = (LVE *)RemHead((struct List *)&ld->ld_Entries)))
3196 * Do we have a resource hook?
3198 if (ld->ld_Resource)
3201 * Setup the entry.
3203 lvr.lvr_Entry = lve->lve_Entry;
3206 * Call the hook.
3208 BGUI_CallHookPkt(ld->ld_Resource, (VOID *)obj, (VOID *)&lvr);
3210 else
3213 * Simple deallocation.
3215 BGUI_FreePoolMem(lve->lve_Entry);
3218 * Free the entry node.
3220 BGUI_FreePoolMem(lve);
3224 * Zero entries.
3226 ld->ld_Total = ld->ld_Top = 0;
3227 ld->ld_TopEntry = NULL;
3228 ld->ld_LastActive = NULL;
3229 ld->ld_LastNum = 0;
3230 ld->ld_LastAdded = NULL;
3233 * We ain't busy no more.
3235 ld->ld_Flags &= ~LDF_LIST_BUSY;
3238 * Notify a NULL entry.
3240 DoNotifyMethod(obj, lvc->lvmc_GInfo, 0, GA_ID, GADGET(obj)->GadgetID, LISTV_Entry, NULL, TAG_END);
3243 * We need a complete refresh.
3245 DoRenderMethod(obj, lvc->lvmc_GInfo, GREDRAW_REDRAW);
3247 return 1;
3249 METHOD_END
3252 * Find a node by it's entry data (slow!).
3254 STATIC ASM LVE *FindEntryData(REG(a0) LD *ld, REG(a1) APTR data, REG(a2) ULONG *number)
3256 LVE *lve;
3257 ULONG num = 0;
3259 for (lve = ld->ld_Entries.lvl_First; lve->lve_Next; lve = lve->lve_Next, num++)
3261 if (lve->lve_Entry == data)
3263 if (number) *number = num;
3264 return lve;
3267 return NULL;
3271 * Find a node by it's entry data (can be fast!).
3273 STATIC ASM LVE *FindEntryDataF(REG(a0) LD *ld, REG(a1) APTR data)
3275 LVE *lve;
3277 if (data == ld->ld_ScanEntry)
3278 return ld->ld_ScanNode;
3280 for (lve = ld->ld_Entries.lvl_First; lve->lve_Next; lve = lve->lve_Next)
3282 if (lve->lve_Entry == data)
3283 return lve;
3285 return NULL;
3288 /// LVM_GETENTRY
3290 * Get an entry.
3292 STATIC METHOD(ListClassGetEntry, struct lvmGetEntry *, lvg )
3294 LD *ld = ( LD * )INST_DATA( cl, obj );
3295 LVE *lve;
3296 ULONG rc = 0L;
3299 * Were busy.
3301 ld->ld_Flags |= LDF_LIST_BUSY;
3304 * What do they want?
3306 switch ( lvg->MethodID ) {
3308 case LVM_FIRSTENTRY:
3310 * The first entry.
3312 if ( ld->ld_Total ) {
3314 * Selected entry?
3316 if ( lvg->lvmg_Flags & LVGEF_SELECTED ) {
3318 * Scan the list.
3320 for ( lve = ld->ld_Entries.lvl_First; lve->lve_Next; lve = lve->lve_Next ) {
3322 * Found?
3324 if ( lve->lve_Flags & LVEF_SELECTED ) {
3325 ld->ld_ScanNode = lve;
3326 ld->ld_ScanEntry = lve->lve_Entry;
3327 rc = ( IPTR )lve->lve_Entry;
3328 break;
3331 } else {
3333 * Normal first entry.
3335 if ( ld->ld_Entries.lvl_First->lve_Next ) {
3336 ld->ld_ScanNode = ld->ld_Entries.lvl_First;
3337 ld->ld_ScanEntry = ld->ld_ScanNode->lve_Entry;
3338 rc = ( IPTR )ld->ld_ScanEntry;
3342 break;
3344 case LVM_LASTENTRY:
3346 * The last entry.
3348 if ( ld->ld_Total ) {
3350 * Selected entry?
3352 if ( lvg->lvmg_Flags & LVGEF_SELECTED ) {
3354 * Scan the list.
3356 for ( lve = ld->ld_Entries.lvl_Last; ; lve = lve->lve_Prev ) {
3358 * Found?
3360 if ( lve->lve_Flags & LVEF_SELECTED ) {
3361 ld->ld_ScanNode = lve;
3362 ld->ld_ScanEntry = lve->lve_Entry;
3363 rc = ( IPTR )lve->lve_Entry;
3364 break;
3367 * Done?
3369 if ( lve == ld->ld_Entries.lvl_First )
3370 break;
3372 } else {
3374 * Normal last entry.
3376 if ( ld->ld_Entries.lvl_First->lve_Next ) {
3377 ld->ld_ScanNode = ld->ld_Entries.lvl_Last;
3378 ld->ld_ScanEntry = ld->ld_ScanNode->lve_Entry;
3379 rc = ( IPTR )ld->ld_ScanEntry;
3383 break;
3385 case LVM_NEXTENTRY:
3387 * Valid entry?
3389 if (( lve = FindEntryDataF( ld, lvg->lvmg_Previous ))) {
3391 * Is there a next one?
3393 if ( lve != ld->ld_Entries.lvl_Last ) {
3395 * Selected entry?
3397 if ( lvg->lvmg_Flags & LVGEF_SELECTED ) {
3399 * Scan the list.
3401 for ( lve = lve->lve_Next; lve->lve_Next; lve = lve->lve_Next ) {
3403 * Found?
3405 if ( lve->lve_Flags & LVEF_SELECTED ) {
3406 ld->ld_ScanNode = lve;
3407 ld->ld_ScanEntry = lve->lve_Entry;
3408 rc = ( IPTR )lve->lve_Entry;
3409 break;
3412 } else {
3414 * Normal next entry.
3416 ld->ld_ScanNode = lve->lve_Next;
3417 ld->ld_ScanEntry = lve->lve_Next->lve_Entry;
3418 rc = ( IPTR )ld->ld_ScanEntry;
3422 break;
3424 case LVM_PREVENTRY:
3426 * Valid entry?
3428 if (( lve = FindEntryDataF( ld, lvg->lvmg_Previous ))) {
3430 * Is there a previous one?
3432 if ( lve != ld->ld_Entries.lvl_First ) {
3434 * Selected entry?
3436 if ( lvg->lvmg_Flags & LVGEF_SELECTED ) {
3438 * Scan the list.
3440 for ( lve = lve->lve_Prev; ; lve = lve->lve_Prev ) {
3442 * Found?
3444 if ( lve->lve_Flags & LVEF_SELECTED ) {
3445 ld->ld_ScanNode = lve;
3446 ld->ld_ScanEntry = lve->lve_Entry;
3447 rc = ( IPTR )lve->lve_Entry;
3448 break;
3451 * Done?
3453 if ( lve == ld->ld_Entries.lvl_First )
3454 break;
3456 } else {
3458 * Normal previous entry.
3460 ld->ld_ScanNode = lve->lve_Prev;
3461 ld->ld_ScanEntry = lve->lve_Prev->lve_Entry;
3462 rc = ( IPTR )ld->ld_ScanEntry;
3466 break;
3470 * Were not busy anymore.
3472 ld->ld_Flags &= ~LDF_LIST_BUSY;
3473 return rc;
3475 METHOD_END
3478 /// LVM_REMENTRY
3480 * Remove an entry from the list.
3482 STATIC METHOD(ListClassRemEntry, struct lvmRemEntry *, lvmr )
3484 LD *ld = INST_DATA(cl, obj);
3485 LVE *lve;
3486 struct lvResource lvr;
3487 ULONG rc = 0L;
3490 * Mark us as busy.
3492 ld->ld_Flags |= LDF_LIST_BUSY;
3495 * Find the entry node.
3497 if ((lve = FindEntryData(ld, lvmr->lvmr_Entry, NULL)))
3500 * Remove the node.
3502 Remove(( struct Node * )lve );
3505 * The last clicked one?
3507 if ( lve == ld->ld_LastActive ) {
3508 ld->ld_LastActive = NULL;
3509 ld->ld_LastNum = 0L;
3513 * Send NULL notification when this is
3514 * a single-select listview and the entry
3515 * is selected.
3517 if (( ! ( ld->ld_Flags & LDF_MULTI_SELECT )) && ( lve->lve_Flags & LVEF_SELECTED ))
3518 DoNotifyMethod( obj, lvmr->lvmr_GInfo, 0L, GA_ID, GADGET( obj )->GadgetID, LISTV_Entry, NULL, TAG_END );
3521 * Resource hook?
3523 if ( ld->ld_Resource ) {
3525 * Init structure.
3527 lvr.lvr_Command = LVRC_KILL;
3528 lvr.lvr_Entry = lve->lve_Entry;
3531 * Call the hook.
3533 rc = ( ULONG )BGUI_CallHookPkt(( void * )ld->ld_Resource, ( void * )obj, ( void * )&lvr );
3534 } else {
3536 * Simple de-allocation
3538 BGUI_FreePoolMem( lve->lve_Entry );
3539 rc = 1L;
3542 * Free node.
3544 BGUI_FreePoolMem( lve );
3547 * One less entry.
3549 ld->ld_Total--;
3552 * Not busy anymore.
3554 ld->ld_Flags &= ~LDF_LIST_BUSY;
3557 * Refresh list.
3559 DoRenderMethod( obj, lvmr->lvmr_GInfo, GREDRAW_REDRAW );
3561 ld->ld_Flags &= ~LDF_LIST_BUSY;
3562 return rc;
3564 METHOD_END
3567 /// LVM_REMSELECTED
3569 * Remove the selected entry from the list and
3570 * select the next/previous one.
3572 METHOD(ListClassRemSelected, struct lvmCommand *, lvmc)
3574 LD *ld = INST_DATA(cl, obj);
3575 LVE *lve, *sel = NULL;
3576 ULONG rc = 0;
3579 * Scan the selected entry.
3581 while (FirstSelected(obj))
3584 * Pick up it's node.
3586 lve = ld->ld_ScanNode;
3589 * Was it the last one?
3591 if (lve == ld->ld_Entries.lvl_Last)
3594 * Also the first one?
3596 if (lve != ld->ld_Entries.lvl_First)
3599 * No. Deselect entry and select
3600 * it's predecessor.
3602 lve->lve_Flags &= ~LVEF_SELECTED;
3603 sel = lve->lve_Prev;
3606 * Setup selection data.
3608 if (lve == ld->ld_LastActive)
3610 ld->ld_LastActive = sel;
3611 ld->ld_LastNum--;
3614 else
3616 sel = NULL;
3619 else
3622 * Deselect entry and select it's successor.
3624 lve->lve_Flags &= ~LVEF_SELECTED;
3625 sel = lve->lve_Next;
3628 * Setup selection data.
3630 if (lve == ld->ld_LastActive)
3631 ld->ld_LastActive = sel;
3635 * Remove entry.
3637 AsmDoMethod(obj, LVM_REMENTRY, NULL, lve->lve_Entry);
3639 rc++;
3641 if (sel)
3644 * Notify new entry.
3646 sel->lve_Flags |= (LVEF_SELECTED | LVEF_REFRESH);
3647 DoNotifyMethod(obj, lvmc->lvmc_GInfo, 0L, GA_ID, GADGET(obj)->GadgetID, LISTV_Entry, sel->lve_Entry, TAG_END);
3649 if (rc)
3651 AsmDoMethod(obj, LVM_REFRESH, lvmc->lvmc_GInfo);
3653 return rc;
3655 METHOD_END
3657 /// LVM_REFRESH
3659 * Refresh the listview.
3661 METHOD(ListClassRefresh, struct lvmCommand *, lvmc)
3663 return DoRenderMethod(obj, lvmc->lvmc_GInfo, GREDRAW_REDRAW);
3665 METHOD_END
3667 /// LVM_REDRAW
3669 * Redraw the listview entries.
3671 METHOD(ListClassRedraw, struct lvmCommand *, lvmc)
3673 LD *ld = INST_DATA(cl, obj);
3675 ld->ld_Flags |= LDF_REFRESH_ALL;
3676 return DoRenderMethod(obj, lvmc->lvmc_GInfo, GREDRAW_UPDATE);
3678 METHOD_END
3680 /// LVM_REDRAWSINGLE
3681 METHOD(ListClassRedrawSingle, struct lvmRedrawSingle *, lvrs)
3683 LD *ld = INST_DATA(cl, obj);
3684 LVE *lve;
3685 ULONG rc;
3687 if (!(lvrs->lvrs_Flags & LVRF_ALL_COLUMNS))
3689 ld->ld_OneColumn = lvrs->lvrs_Column;
3690 ld->ld_Flags |= LDF_ONE_COLUMN;
3693 if (!(lvrs->lvrs_Flags & LVRF_ALL_ENTRIES))
3695 if ((lve = FindEntryDataF(ld, lvrs->lvrs_Entry)))
3696 lve->lve_Flags |= LVEF_REFRESH;
3698 else
3700 ld->ld_Flags |= LDF_REFRESH_ALL;
3702 rc = DoRenderMethod(obj, lvrs->lvrs_GInfo, GREDRAW_UPDATE);
3704 ld->ld_Flags &= ~LDF_ONE_COLUMN;
3706 return rc;
3708 METHOD_END
3710 /// LVM_SORT
3712 * Sort the list.
3714 STATIC METHOD(ListClassSort, struct lvmCommand *, lvmc )
3716 LD *ld = ( LD * )INST_DATA( cl, obj );
3717 LVE *lve;
3718 LVL buffer;
3721 * Do we have entries?
3723 if ( ld->ld_Total ) {
3725 * Were busy.
3727 ld->ld_Flags |= LDF_LIST_BUSY;
3730 * Initialize buffer.
3732 NewList(( struct List * )&buffer );
3735 * Attach all entries to the buffer.
3737 while (( lve = ( LVE * )RemHead(( struct List * )&ld->ld_Entries )))
3738 AddTail(( struct List * )&buffer, ( struct Node * )lve );
3741 * And put them back again sorted.
3743 while (( lve = ( LVE * )RemHead(( struct List * )&buffer )))
3744 AddEntryInList( ld, obj, lve, LVAP_SORTED );
3747 * Were not busy anymore.
3749 ld->ld_Flags &= ~LDF_LIST_BUSY;
3752 * Refresh the list.
3754 ld->ld_Flags |= LDF_REFRESH_ALL;
3755 DoRenderMethod( obj, lvmc->lvmc_GInfo, GREDRAW_UPDATE );
3757 return 1;
3759 METHOD_END
3761 /// LVM_LOCK, LVM_UNLOCK
3763 * (Un)lock the list.
3765 STATIC METHOD(ListClassLock, struct lvmCommand *, lvmc )
3767 LD *ld = INST_DATA(cl, obj);
3769 switch (lvmc->MethodID)
3771 case LVM_LOCKLIST:
3772 ld->ld_Flags |= LDF_LIST_BUSY;
3773 break;
3775 case LVM_UNLOCKLIST:
3776 ld->ld_Flags &= ~LDF_LIST_BUSY;
3777 DoRenderMethod(obj, lvmc->lvmc_GInfo, GREDRAW_REDRAW);
3778 break;
3780 return 1;
3782 METHOD_END
3785 /// LVM_MOVE
3787 * Move an entry.
3789 STATIC METHOD(ListClassMove, struct lvmMove *, lvm )
3791 LD *ld = ( LD * )INST_DATA( cl, obj );
3792 LVE *lve, *tmp;
3793 ULONG rc = 0L, num = 0L, cpos = 0;
3796 * Look up the entry.
3798 if ( ! lvm->lvmm_Entry ) lve = ld->ld_LastActive;
3799 else lve = FindEntryData( ld, lvm->lvmm_Entry, &cpos );
3801 if ( lve ) {
3803 * Lock the list.
3805 ld->ld_Flags |= LDF_LIST_BUSY;
3808 * Move to?
3810 switch ( lvm->lvmm_Direction ) {
3812 case LVMOVE_UP:
3814 * Already at the top?
3816 if ( lve != ld->ld_Entries.lvl_First ) {
3818 * Pick up new predeccessor.
3820 tmp = lve->lve_Prev->lve_Prev;
3823 * Do it.
3825 goto insertIt;
3827 break;
3829 case LVMOVE_DOWN:
3831 * Already at the bottom?
3833 if ( lve != ld->ld_Entries.lvl_Last ) {
3835 * Pick up new predeccessor.
3837 tmp = lve->lve_Next;
3839 insertIt:
3842 * Remove the node.
3844 Remove(( struct Node * )lve );
3847 * Insert it into it's new spot.
3849 Insert(( struct List * )&ld->ld_Entries, ( struct Node * )lve, ( struct Node * )tmp );
3850 rc = 1L;
3852 break;
3854 case LVMOVE_TOP:
3856 * Already at the top?
3858 if ( lve != ld->ld_Entries.lvl_First ) {
3860 * Remove the node.
3862 Remove(( struct Node * )lve );
3865 * Insert it into it's new spot.
3867 AddHead(( struct List * )&ld->ld_Entries, ( struct Node * )lve );
3870 * The number is known.
3872 num = 0;
3873 rc = 1L;
3874 ld->ld_Flags &= ~LDF_LIST_BUSY;
3875 goto gotNum;
3877 break;
3879 case LVMOVE_BOTTOM:
3881 * Already at the bottom?
3883 if ( lve != ld->ld_Entries.lvl_Last ) {
3885 * Remove the node.
3887 Remove(( struct Node * )lve );
3890 * Insert it into it's new spot.
3892 AddTail(( struct List * )&ld->ld_Entries, ( struct Node * )lve );
3895 * The number is known.
3897 num = ld->ld_Total - 1;
3898 rc = 1L;
3899 ld->ld_Flags &= ~LDF_LIST_BUSY;
3900 goto gotNum;
3902 break;
3904 case LVMOVE_NEWPOS:
3906 * Current position changed?
3908 if ( cpos != lvm->lvmm_NewPos ) {
3910 * New position 0?
3912 if ( lvm->lvmm_NewPos == 0 ) {
3913 Remove(( struct Node * )lve );
3914 AddHead(( struct List * )&ld->ld_Entries, ( struct Node * )lve );
3915 num = 0;
3916 } else if ( lvm->lvmm_NewPos >= ld->ld_Total ) {
3917 Remove(( struct Node * )lve );
3918 AddTail(( struct List * )&ld->ld_Entries, ( struct Node * )lve );
3919 num = ld->ld_Total - 1;
3920 } else {
3922 * Not at the start and not at the end. Find the predecessor
3923 * of the place we drop the this node.
3925 tmp = FindNodeQuick( ld, lvm->lvmm_NewPos - 1 );
3928 * If we are our precedecessor ourselves
3929 * we take the one before us.
3931 if ( tmp == lve ) tmp = tmp->lve_Prev;
3934 * Remove the node from it's current location
3935 * and insert back in it's new place.
3937 Remove(( struct Node * )lve );
3938 Insert(( struct List * )&ld->ld_Entries, ( struct Node * )lve, ( struct Node * )tmp );
3941 * The number is known.
3943 num = lvm->lvmm_NewPos;
3945 rc = 1L;
3946 ld->ld_Flags &= ~LDF_LIST_BUSY;
3947 goto gotNum;
3949 break;
3952 * List changed?
3954 if ( rc ) {
3956 * Not busy anymore.
3958 ld->ld_Flags &= ~LDF_LIST_BUSY;
3961 * Find out it's number.
3963 for ( tmp = ld->ld_Entries.lvl_First; tmp->lve_Next; tmp = tmp->lve_Next, num++ ) {
3965 * Is this the one?
3967 if ( tmp == lve ) {
3968 gotNum:
3970 * Was it selected?
3972 if (lve->lve_Flags & LVEF_SELECTED)
3974 * Setup it's number.
3976 ld->ld_LastNum = num;
3979 * Make the moved entry visible.
3981 ld->ld_Flags |= LDF_REFRESH_ALL;
3982 DoSetMethod( obj, lvm->lvmm_GInfo, LISTV_MakeVisible, num, TAG_END );
3985 * Notify and setup the new position.
3987 DoNotifyMethod( obj, lvm->lvmm_GInfo, 0L, GA_ID, GADGET( obj )->GadgetID, LISTV_NewPosition, num, TAG_END );
3988 ld->ld_NewPos = num;
3989 return rc;
3995 * Not busy anymore.
3997 ld->ld_Flags &= ~LDF_LIST_BUSY;
3998 return rc;
4000 METHOD_END
4003 /// LVM_REPLACE
4005 * Replace an entry.
4007 STATIC METHOD(ListClassReplace, struct lvmReplace *, lvmr )
4009 LD *ld = ( LD * )INST_DATA( cl, obj );
4010 LVE *lvo;
4011 struct lvResource lvr;
4012 APTR newdata;
4013 ULONG rc = 0L;
4016 * Data OK?
4018 if (( ! lvmr->lvmr_OldEntry ) || ( ! lvmr->lvmr_NewEntry ))
4019 return( 0L );
4022 * Were busy.
4024 ld->ld_Flags |= LDF_LIST_BUSY;
4027 * Find the old entry.
4029 if ((lvo = FindEntryData(ld, lvmr->lvmr_OldEntry, NULL)))
4032 * Create the new entry.
4034 if ( ld->ld_Resource ) {
4036 * Init structure.
4038 lvr.lvr_Command = LVRC_MAKE;
4039 lvr.lvr_Entry = lvmr->lvmr_NewEntry;
4042 * Call the hook.
4044 if (( newdata = ( APTR )BGUI_CallHookPkt(( void * )ld->ld_Resource, ( void * )obj, ( void * )&lvr ))) {
4046 * Free the old entry and setup the new one.
4048 lvr.lvr_Command = LVRC_KILL;
4049 lvr.lvr_Entry = lvmr->lvmr_OldEntry;
4050 BGUI_CallHookPkt(( void * )ld->ld_Resource, ( void * )obj, ( void * )&lvr );
4051 lvo->lve_Entry = newdata;
4052 lvo->lve_Flags |= LVEF_REFRESH;
4053 rc = ( IPTR )newdata;
4055 } else {
4057 * Allocate a string copy of the new data.
4059 if (( newdata = ( APTR )BGUI_AllocPoolMem( strlen(( UBYTE * )lvmr->lvmr_NewEntry ) + 1 ))) {
4061 * Copy it.
4063 strcpy(( UBYTE * )newdata, ( UBYTE * )lvmr->lvmr_NewEntry );
4066 * Free the old entry, and setup the new one.
4068 BGUI_FreePoolMem( lvmr->lvmr_OldEntry );
4069 lvo->lve_Entry = newdata;
4070 lvo->lve_Flags |= LVEF_REFRESH;
4071 rc = ( IPTR )newdata;
4077 * Were not busy anymore.
4079 ld->ld_Flags &= ~LDF_LIST_BUSY;
4081 if ( rc ) DoRenderMethod( obj, lvmr->lvmr_GInfo, GREDRAW_UPDATE );
4082 return( rc );
4084 METHOD_END
4087 /// LVM_SETCOLUMNATTRS
4089 METHOD(ListClassSetColumnAttrs, struct lvmColumnAttrs *, lvca)
4091 LD *ld = INST_DATA(cl, obj);
4092 ULONG rc;
4094 rc = BGUI_PackStructureTags(&ld->ld_CD[lvca->lvca_Column], ColumnPackTable, (struct TagItem *)&lvca->lvca_AttrList);
4096 if (rc) AsmDoMethod(obj, LVM_REDRAW, lvca->lvca_GInfo);
4098 return rc;
4100 METHOD_END
4102 /// LVM_GETCOLUMNATTRS
4104 METHOD(ListClassGetColumnAttrs, struct lvmColumnAttrs *, lvca)
4106 LD *ld = INST_DATA(cl, obj);
4108 return BGUI_UnpackStructureTags(&ld->ld_CD[lvca->lvca_Column], ColumnPackTable, (struct TagItem *)&lvca->lvca_AttrList);
4110 METHOD_END
4112 /// BASE_DRAGQUERY
4114 * Query if we accept data from the dragged object. We only accept when:
4116 * A) The querying object is us.
4117 * B) We are in LISTV_ShowDropSpot mode.
4118 * C) The mouse is located inside the list view area.
4120 * All other instances are refused.
4122 METHOD(ListClassDragQuery, struct bmDragPoint *, bmdp)
4124 LD *ld = INST_DATA( cl, obj );
4126 if (bmdp->bmdp_Source == obj && ld->ld_Flags & LDF_SHOWDROPSPOT)
4128 if (bmdp->bmdp_Mouse.X >= 0 &&
4129 bmdp->bmdp_Mouse.Y >= 0 &&
4130 bmdp->bmdp_Mouse.X < ld->ld_InnerBox.Width &&
4131 bmdp->bmdp_Mouse.Y < ld->ld_InnerBox.Height)
4132 return BQR_ACCEPT;
4134 return BQR_REJECT;
4136 METHOD_END
4138 /// BASE_DRAGACTIVE
4140 * Show us being the active drop object.
4142 METHOD(ListClassDragActive, struct bmDragMsg *, bmdm)
4144 LD *ld = INST_DATA(cl, obj);
4145 struct BaseInfo *bi;
4147 ld->ld_DropSpot=ld->ld_DrawSpot = ~0;
4150 * Drop anywhere or do we have to mark the spot?
4152 if ((!(ld->ld_Flags & LDF_SHOWDROPSPOT)) || (!ld->ld_Entries.lvl_First->lve_Next))
4155 * Anywhere or the list is empty. Simply place a dotted line around the view area.
4157 #ifdef DEBUG_BGUI
4158 if ((bi = AllocBaseInfoDebug(__FILE__,__LINE__,BI_GadgetInfo, bmdm->bmdm_GInfo, BI_RastPort, NULL, TAG_DONE)))
4159 #else
4160 if ((bi = AllocBaseInfo(BI_GadgetInfo, bmdm->bmdm_GInfo, BI_RastPort, NULL, TAG_DONE)))
4161 #endif
4163 ld->ld_Flags |= LDF_MOVE_DROPBOX;
4165 * Draw the box.
4167 DottedBox(bi, &ld->ld_InnerBox);
4168 FreeBaseInfo(bi);
4171 return 1;
4173 METHOD_END
4175 /// BASE_DRAGINACTIVE
4177 * Deactivate.
4179 STATIC METHOD(ListClassDragInactive, struct bmDragMsg *, bmdm )
4181 LD *ld = INST_DATA(cl, obj);
4184 * Clear drop spot.
4186 if (ld->ld_LineBuffer)
4188 BGUI_FreeRPortBitMap(ld->ld_LineBuffer);
4189 ld->ld_LineBuffer = NULL;
4191 ld->ld_DropSpot = ~0;
4192 ld->ld_Flags &= ~LDF_MOVE_DROPBOX;
4194 return AsmDoSuperMethodA(cl, obj, (Msg)bmdm);
4196 METHOD_END
4199 /// BASE_DRAGUPDATE
4201 * Update drop position.
4203 METHOD(ListClassDragUpdate, struct bmDragPoint *, bmdp)
4205 LD *ld = INST_DATA(cl, obj);
4206 struct IBox *ib;
4207 struct GadgetInfo *gi = bmdp->bmdp_GInfo;
4208 struct BaseInfo *bi;
4210 int dpos, otop = ld->ld_Top, ntop = otop;
4212 int x = bmdp->bmdp_Mouse.X;
4213 int y = bmdp->bmdp_Mouse.Y;
4214 int x1, y1, w, h;
4216 if (ld->ld_Flags & LDF_MOVE_DROPBOX)
4218 #ifdef DEBUG_BGUI
4219 if ((bi = AllocBaseInfoDebug(__FILE__,__LINE__,BI_GadgetInfo, gi, BI_RastPort, NULL, TAG_DONE)))
4220 #else
4221 if ((bi = AllocBaseInfo(BI_GadgetInfo, gi, BI_RastPort, NULL, TAG_DONE)))
4222 #endif
4224 DottedBox(bi, &ld->ld_InnerBox);
4225 FreeBaseInfo(bi);
4230 * Keep track of the drop position?
4232 if (ld->ld_Flags & LDF_SHOWDROPSPOT && ld->ld_Entries.lvl_First->lve_Next)
4235 * Reject when we are out of the list bounds.
4237 if ((x < 0) || (x >= ld->ld_InnerBox.Width))
4240 * We deactivate when the mouse has left us out of the hitbox.
4242 ld->ld_DropSpot = ld->ld_DrawSpot = ~0;
4243 return BUR_ABORT;
4247 * Make the y position relative to the window.
4249 Get_SuperAttr(cl, obj, BT_HitBox, (IPTR *)&ib);
4250 y += ib->Top + (ld->ld_EntryHeight >> 1);
4253 * Get the entry under the mouse.
4255 dpos = MouseOverEntry(ld, y);
4258 * New position above or below the
4259 * visible entries?
4261 if (dpos < ntop)
4263 dpos = --ntop;
4265 else if (dpos > (ntop + ld->ld_Visible))
4267 dpos = ++ntop + ld->ld_Visible;
4270 if (ntop > ld->ld_Total - ld->ld_Visible)
4271 ntop = ld->ld_Total - ld->ld_Visible;
4272 if (ntop < 0) ntop = 0;
4274 if (dpos < 0) dpos = 0;
4275 if (dpos > ld->ld_Total) dpos = ld->ld_Total;
4278 * Position changed?
4280 if (dpos != ld->ld_DropSpot)
4283 * Yes. Get RastPort.
4285 if (gi)
4287 x1 = ld->ld_ListArea.Left;
4288 y1 = ld->ld_ListArea.Top;
4289 w = ld->ld_ListArea.Width;
4290 h = ld->ld_ListArea.Height;
4293 * Re-render the current entry.
4295 if ((ld->ld_DrawSpot != (UWORD)~0) && ld->ld_LineBuffer)
4297 #ifdef DEBUG_BGUI
4298 if ((bi = AllocBaseInfoDebug(__FILE__,__LINE__,BI_GadgetInfo, gi, BI_RastPort, NULL, TAG_DONE)))
4299 #else
4300 if ((bi = AllocBaseInfo(BI_GadgetInfo, gi, BI_RastPort, NULL, TAG_DONE)))
4301 #endif
4304 * Fix line at the old position.
4306 ClipBlit(ld->ld_LineBuffer, 0, 0, bi->bi_RPort, x1, ld->ld_DrawSpot, w, 1, 0xC0);
4307 FreeBaseInfo(bi);
4312 * Scroll if necessary.
4314 if (ntop != otop)
4316 DoSetMethod(obj, gi, LISTV_Top, ntop, TAG_DONE);
4320 * Mark new position.
4322 ld->ld_DropSpot = dpos;
4325 * Setup y position.
4327 y = range((dpos - ld->ld_Top) * ld->ld_EntryHeight, 0, h - 1) + y1;
4329 ld->ld_DrawSpot = y;
4331 #ifdef DEBUG_BGUI
4332 if ((bi = AllocBaseInfoDebug(__FILE__,__LINE__,BI_GadgetInfo, gi, BI_RastPort, NULL, TAG_DONE)))
4333 #else
4334 if ((bi = AllocBaseInfo(BI_GadgetInfo, gi, BI_RastPort, NULL, TAG_DONE)))
4335 #endif
4337 if (!ld->ld_LineBuffer) ld->ld_LineBuffer = BGUI_CreateRPortBitMap(bi->bi_RPort, w, 1, 0);
4339 if (ld->ld_LineBuffer)
4342 * Copy the old line to a buffer.
4344 ClipBlit(bi->bi_RPort, x1, y, ld->ld_LineBuffer, 0, 0, w, 1, 0xC0);
4347 * Render line at the new position.
4349 SetDashedLine(bi, 0);
4350 HLine(bi->bi_RPort, x1, y, x1 + w - 1);
4352 FreeBaseInfo(bi);
4357 else
4360 * Reject when we are out of the list bounds.
4362 if ((x < 0) || (x >= ld->ld_InnerBox.Width) ||
4363 (y < 0) || (y >= ld->ld_InnerBox.Height))
4366 * We deactivate when the mouse has left us out of the hitbox.
4368 return BUR_ABORT;
4371 return BUR_CONTINUE;
4373 METHOD_END
4375 /// BASE_DROPPED
4377 * We have been dropped upon.
4379 STATIC METHOD(ListClassDropped, struct bmDropped *, bmd)
4381 LD *ld = ( LD * )INST_DATA( cl, obj );
4382 struct MinList buffer;
4383 LVE *lve, *tmp, *pred = NULL;
4384 ULONG spot = ld->ld_DropSpot, pos = 0;
4386 if(spot==~0)
4387 return(1);
4389 * Initialize buffer list.
4391 NewList(( struct List * )&buffer );
4394 * Were busy buddy.
4396 ld->ld_Flags |= LDF_LIST_BUSY;
4399 * Were we dropped in the list?
4401 if ( spot ) {
4403 * Find the drop-position node.
4405 if ( spot == 1 ) {
4406 pos = 0;
4407 pred = ld->ld_Entries.lvl_First;
4409 * We do make this the predecessor
4410 * if it's selected.
4412 if ( pred->lve_Flags & LVEF_SELECTED )
4413 pred = NULL;
4414 } else if ( spot >= ld->ld_Total ) {
4415 pos = ld->ld_Total - 1;
4416 pred = ld->ld_Entries.lvl_Last;
4417 } else {
4418 pred = FindNodeQuick( ld, spot - 1 );
4419 if ( spot > ld->ld_LastNum ) pos = spot - 1;
4420 else pos = spot;
4424 * Now we have to scan back from the drop
4425 * position to find the first not selected
4426 * entry node.
4428 if ( pred ) {
4429 while ( pred->lve_Flags & LVEF_SELECTED ) {
4431 * Get previous entry.
4433 pred = pred->lve_Prev;
4436 * Is it the first one and still selected?
4438 if ( pred == ld->ld_Entries.lvl_First && pred->lve_Flags & LVEF_SELECTED ) {
4439 pred = NULL;
4440 break;
4447 * Remove all selected entries and append them to the buffer.
4449 lve = ld->ld_Entries.lvl_First;
4452 * Clear the last-active data.
4454 ld->ld_LastActive = NULL;
4455 ld->ld_LastNum = 0;
4457 while ( lve->lve_Next ) {
4459 * Is this a selected entry?
4461 if ( lve->lve_Flags & LVEF_SELECTED ) {
4463 * Mark successor.
4465 tmp = lve->lve_Next;
4468 * Remove from the list and
4469 * append it to the buffer.
4471 Remove(( struct Node * )lve );
4472 AddTail(( struct List * )&buffer, ( struct Node * )lve );
4475 * Make the successor current.
4477 lve = tmp;
4478 } else
4480 * Next please.
4482 lve = lve->lve_Next;
4486 * Move 'm back into their new position.
4488 while (( lve = ( LVE * )RemHead(( struct List * )&buffer ))) {
4489 if ( ! ld->ld_LastActive ) {
4490 ld->ld_LastActive = lve;
4491 ld->ld_LastNum = pos;
4493 if ( ! pred ) AddHead(( struct List * )&ld->ld_Entries, ( struct Node * )lve );
4494 else Insert(( struct List * )&ld->ld_Entries, ( struct Node * )lve, ( struct Node * )pred );
4495 pred = lve;
4499 * We are not busy anymore.
4501 ld->ld_Flags &= ~LDF_LIST_BUSY;
4502 ld->ld_DropSpot = ~0;
4504 return 1;
4506 METHOD_END
4508 /// BASE_GETOBJECT
4510 * Create a rastport in which the selected entries(s) are rendered.
4512 METHOD(ListClassGetObject, struct bmGetDragObject *, bmgo)
4514 LD *ld = INST_DATA(cl, obj);
4515 struct GadgetInfo *gi = bmgo->bmgo_GInfo;
4516 struct RastPort *drag_rp, *rp = &gi->gi_Screen->RastPort;
4517 APTR entry;
4518 struct IBox box;
4519 ULONG rc = 0, num, i = 0;
4520 int depth = gi->gi_DrInfo->dri_Depth;
4521 struct BaseInfo *bi;
4523 int lx = ld->ld_ListArea.Left;
4524 //int ly = ld->ld_ListArea.Top;
4525 int lw = ld->ld_ListArea.Width;
4526 int lh = ld->ld_ListArea.Height;
4527 int eh = ld->ld_EntryHeight;
4528 //int mx = gi->gi_Window->MouseX;
4529 int my = gi->gi_Window->MouseY;
4532 * Do we have any selected entries?
4534 if ((entry = (APTR)FirstSelected(obj)))
4537 * Count the number of selected entries.
4539 for (num = 0; entry && (num <= 10); num++)
4541 entry = (APTR)NextSelected(obj, entry);
4545 * Less than the maximum?
4547 if (num <= 10)
4550 * Allocate the rastport.
4552 if ((drag_rp = BGUI_CreateRPortBitMap(rp, lw, num * lh, depth)))
4554 #ifdef DEBUG_BGUI
4555 if ((bi = AllocBaseInfoDebug(__FILE__,__LINE__,BI_GadgetInfo, gi, BI_RastPort, drag_rp, TAG_DONE)))
4556 #else
4557 if ((bi = AllocBaseInfo(BI_GadgetInfo, gi, BI_RastPort, drag_rp, TAG_DONE)))
4558 #endif
4560 BSetFont(bi, ld->ld_Font);
4561 ColumnSeparators(ld, bi, 0, 0, num * ld->ld_EntryHeight);
4564 * Render them...
4566 entry = (APTR)FirstSelected(obj);
4569 RenderEntry(obj, ld, bi, ld->ld_ScanNode, REL_ZERO | i++);
4570 } while ((entry = (APTR)NextSelected(obj, entry)));
4571 setIt:
4574 * Setup the rastport so we can
4575 * deallocate it later.
4577 ld->ld_DragRP = drag_rp;
4580 * Setup bounds.
4582 bmgo->bmgo_Bounds->Left = lx;
4583 bmgo->bmgo_Bounds->Top = my - ((num * eh) >> 1);
4584 bmgo->bmgo_Bounds->Width = lw;
4585 bmgo->bmgo_Bounds->Height = num * eh;
4588 * Return a pointer to the bitmap.
4590 rc = (IPTR)drag_rp->BitMap;
4592 if(bi) FreeBaseInfo(bi);
4596 else
4599 * More than 10 entries is a special case.
4601 if ((drag_rp = BGUI_CreateRPortBitMap(rp, lw, 3 * eh, depth)))
4603 #ifdef DEBUG_BGUI
4604 if ((bi = AllocBaseInfoDebug(__FILE__,__LINE__,BI_GadgetInfo, gi, BI_RastPort, drag_rp, TAG_DONE)))
4605 #else
4606 if ((bi = AllocBaseInfo(BI_GadgetInfo, gi, BI_RastPort, drag_rp, TAG_DONE)))
4607 #endif
4609 BSetFont(bi, ld->ld_Font);
4610 ColumnSeparators(ld, bi, 0, 0, 3 * ld->ld_EntryHeight);
4613 * Render the first entry...
4615 FirstSelected(obj);
4616 RenderEntry(obj, ld, bi, ld->ld_ScanNode, REL_ZERO | 0);
4619 * Setup rendering bounds.
4621 box.Left = 0;
4622 box.Top = eh;
4623 box.Width = lw;
4624 box.Height = eh;
4627 * Jam special text...
4629 RenderText(bi, "\33d3\33D5--->", &box);
4632 * Render the last entry...
4634 LastSelected(obj);
4635 RenderEntry(obj, ld, bi, ld->ld_ScanNode, REL_ZERO | 2);
4637 FreeBaseInfo(bi); bi=NULL;
4640 num = 3;
4641 goto setIt;
4645 return rc;
4647 METHOD_END
4649 /// BASE_FREEOBJECT
4651 * Free the dragged object.
4653 STATIC METHOD(ListClassFreeObject, struct bmFreeDragObject *, bmfo )
4655 LD *ld = INST_DATA( cl, obj );
4658 * Simply deallocate the bitmap and rastport.
4660 BGUI_FreeRPortBitMap( ld->ld_DragRP );
4661 ld->ld_DragRP = NULL;
4663 return 1;
4665 METHOD_END
4668 /// Class initialization.
4670 * Function table.
4672 STATIC DPFUNC ClassFunc[] = {
4673 { BASE_RENDER, ListClassRender, },
4674 { BASE_LAYOUT, ListClassLayout, },
4675 { BASE_DIMENSIONS, ListClassDimensions, },
4677 { OM_NEW, ListClassNew, },
4678 { OM_SET, ListClassSetUpdate, },
4679 { OM_UPDATE, ListClassSetUpdate, },
4680 { OM_GET, ListClassGet, },
4681 { OM_DISPOSE, ListClassDispose, },
4682 { GM_HITTEST, ListClassHitTest, },
4683 { GM_GOACTIVE, ListClassGoActive, },
4684 { GM_HANDLEINPUT, ListClassHandleInput, },
4685 { GM_GOINACTIVE, ListClassGoInActive, },
4686 { WM_KEYACTIVE, ListClassKeyActive, },
4687 { LVM_ADDENTRIES, ListClassAddEntries, },
4688 { LVM_INSERTENTRIES, ListClassInsertEntries, },
4689 { LVM_ADDSINGLE, ListClassAddSingle, },
4690 { LVM_INSERTSINGLE, ListClassInsertSingle, },
4691 { LVM_CLEAR, ListClassClear, },
4692 { LVM_FIRSTENTRY, ListClassGetEntry, },
4693 { LVM_LASTENTRY, ListClassGetEntry, },
4694 { LVM_NEXTENTRY, ListClassGetEntry, },
4695 { LVM_PREVENTRY, ListClassGetEntry, },
4696 { LVM_REMENTRY, ListClassRemEntry, },
4697 { LVM_REFRESH, ListClassRefresh, },
4698 { LVM_REDRAW, ListClassRedraw, },
4699 { LVM_REDRAWSINGLE, ListClassRedrawSingle, },
4700 { LVM_SORT, ListClassSort, },
4701 { LVM_LOCKLIST, ListClassLock, },
4702 { LVM_UNLOCKLIST, ListClassLock, },
4703 { LVM_REMSELECTED, ListClassRemSelected, },
4704 { LVM_MOVE, ListClassMove, },
4705 { LVM_REPLACE, ListClassReplace, },
4706 { LVM_SETCOLUMNATTRS, ListClassSetColumnAttrs, },
4707 { LVM_GETCOLUMNATTRS, ListClassGetColumnAttrs, },
4708 { BASE_DRAGQUERY, ListClassDragQuery, },
4709 { BASE_DRAGACTIVE, ListClassDragActive, },
4710 { BASE_DRAGINACTIVE, ListClassDragInactive, },
4711 { BASE_DRAGUPDATE, ListClassDragUpdate, },
4712 { BASE_DROPPED, ListClassDropped, },
4713 { BASE_GETDRAGOBJECT, ListClassGetObject, },
4714 { BASE_FREEDRAGOBJECT, ListClassFreeObject, },
4715 { DF_END, NULL },
4719 * Simple class initialization.
4721 makeproto Class *InitListClass(void)
4723 return BGUI_MakeClass(CLASS_SuperClassBGUI, BGUI_BASE_GADGET,
4724 CLASS_ObjectSize, sizeof(LD),
4725 CLASS_DFTable, ClassFunc,
4726 TAG_DONE);