6 * (C) Copyright 1998 Manuel Lemos.
7 * (C) Copyright 1995 Jaba Development.
8 * (C) Copyright 1995 Jan van den Baard.
12 * Revision 42.2 2004/06/17 07:38:47 chodorowski
13 * Added missing REGFUNC_END.
15 * Revision 42.1 2000/05/15 19:29:50 stegerg
16 * replacements for REG macro.
18 * Revision 42.0 2000/05/09 22:19:56 mlemos
19 * Bumped to revision 42.0 before handing BGUI to AROS team
21 * Revision 41.11 2000/05/09 20:33:46 mlemos
22 * Bumped to revision 41.11
24 * Revision 1.2 2000/05/09 19:59:02 mlemos
25 * Merged with the branch Manuel_Lemos_fixes.
27 * Revision 1.1.2.3 1999/02/19 05:03:56 mlemos
28 * Added support to build with Storm C.
30 * Revision 1.1.2.2 1998/04/27 00:13:01 mlemos
31 * Ensured that the stack is never less than 8000 bytes.
33 * Revision 1.1.2.1 1998/02/28 17:45:30 mlemos
40 dcc MultiColumn.c -proto -mi -ms -mRR -lbgui
48 #include <dos/exall.h>
49 #include <utility/hooks.h>
51 #include <clib/macros.h>
57 ** This is the data were going to add
58 ** to the listview object. It is a simple
59 ** structure in which the file-information
60 ** is stored. This data is created in the
61 ** LISTV_Resource hook from a pointer
62 ** to a ExAllData structure.
65 UBYTE fi_FileName
[ 108 ];
72 ** The LISTV_Resource hook is used to create
73 ** the FILEINFO structures from a struct ExAllData
74 ** at create time. At delete time the FILEINFO
75 ** structure is simply deallocated.
77 //SAVEDS ASM APTR ResourceHookFunc( REG(a0) struct Hook *hook, REG(a2) Object *obj, REG(a1) struct lvResource *lvr )
78 SAVEDS ASM
REGFUNC3(APTR
, ResourceHookFunc
,
79 REGPARAM(A0
, struct Hook
*, hook
),
80 REGPARAM(A2
, Object
*, obj
),
81 REGPARAM(A1
, struct lvResource
*, lvr
))
83 struct ExAllData
*ead
;
86 APTR return_code
= NULL
;
91 switch ( lvr
->lvr_Command
) {
95 ** Create a FILEINFO structure.
96 ** BGUI has passed us a pointer to a
97 ** ExAllData structure. Here we
98 ** convert it to a FILEINFO structure
99 ** which, eventually, get's added to
102 if (( fi
= ( FILEINFO
* )AllocVec( sizeof( FILEINFO
), MEMF_PUBLIC
))) {
104 ** Pick up the ExAllData.
106 ead
= ( struct ExAllData
* )lvr
->lvr_Entry
;
110 strcpy( &fi
->fi_FileName
[ 0 ], ead
->ed_Name
);
112 ** Format the size text. We can do all sorts of
113 ** fancy stuff here like using the locale.library
114 ** formatting stuff but hey, it's just a demo ;)
116 if ( ead
->ed_Type
< 0 ) {
117 fi
->fi_IsDir
= FALSE
;
118 sprintf( &fi
->fi_Size
[ 0 ], "%ld", (long int)ead
->ed_Size
);
121 strcpy( &fi
->fi_Size
[ 0 ], "(dir)" );
124 ** Convert the date to a string.
126 dt
.dat_Stamp
= *(( struct DateStamp
* )&ead
->ed_Days
);
127 dt
.dat_Format
= FORMAT_CDN
;
128 dt
.dat_Flags
= DTF_SUBST
| DTF_FUTURE
;
129 dt
.dat_StrDay
= NULL
;
130 dt
.dat_StrDate
= &fi
->fi_Date
[ 0 ];
131 dt
.dat_StrTime
= NULL
;
137 ** Return a pointer to the created
138 ** FILEINFO structure.
140 return_code
= ( APTR
)fi
;
146 ** Simply deallocate the FILEINFO
147 ** structure which has been created with
150 FreeVec( lvr
->lvr_Entry
);
154 ** Pointer to FILEINFO or NULL.
156 return( return_code
);
161 ** The listview will have three columns.
165 ** The following globals will contain the maximum
166 ** width of each of these columns.
168 UWORD MaxName
, MaxSize
, MaxDate
;
171 ** This global stores the total width of the
172 ** listview drawing area.
174 UWORD TotalWidth
= 0;
177 ** This boolean determines wether the hook must
178 ** re-compute the column sizes.
180 BOOL ReCompCols
= TRUE
;
183 ** We use 16 pixels as a minimum inner-column spacing.
185 #define INNER_SPACE 16
188 ** This routine re-computes the minimum column
189 ** sizes when necessary.
191 VOID
ReComputeColumns( struct RastPort
*rp
, Object
*obj
, UWORD list_width
)
197 ** A re-computation is necessary when:
199 ** 1) The ReCompCols flag is TRUE.
200 ** 2) The with of the listview has changed.
202 if ( ReCompCols
|| ( TotalWidth
!= list_width
)) {
204 ** Our listview also has a title entry.
205 ** Here we compute the default column
206 ** sizes accoording to this title.
208 MaxName
= TextLength( rp
, "Name:", 6 ) + INNER_SPACE
;
209 MaxSize
= TextLength( rp
, "Size:", 6 ) + INNER_SPACE
;
210 MaxDate
= TextLength( rp
, "Date:", 6 );
212 ** Now we loop through the entries to find
213 ** out the largest width of the three columns.
215 if (( fi
= ( FILEINFO
* )FirstEntry( obj
))) {
217 ** Loop until all are done.
221 ** Compute width of the Name: column
224 tmp
= TextLength( rp
, &fi
->fi_FileName
[ 0 ], strlen( &fi
->fi_FileName
[ 0 ] )) + INNER_SPACE
;
226 ** Is it bigger than the last one?
229 if ( tmp
> MaxName
) MaxName
= tmp
;
231 ** Compute width of the Size: column
234 tmp
= TextLength( rp
, &fi
->fi_Size
[ 0 ], strlen( &fi
->fi_Size
[ 0 ] )) + INNER_SPACE
;
236 ** Is it bigger than the last one?
239 if ( tmp
> MaxSize
) MaxSize
= tmp
;
241 ** Compute width of the Date: column
244 tmp
= TextLength( rp
, &fi
->fi_Date
[ 0 ], strlen( &fi
->fi_Date
[ 0 ] ));
246 ** Is it bigger than the last one?
249 if ( tmp
> MaxDate
) MaxDate
= tmp
;
251 ** Pick up the next entry.
253 fi
= ( FILEINFO
* )NextEntry( obj
, fi
);
257 ** Compute the total columns width.
259 total
= MaxName
+ MaxSize
+ MaxDate
;
261 ** If there's room left over we
262 ** distribute it between the columns so
263 ** we get a nice even spacing between
266 if ( list_width
> total
) {
267 MaxName
+= ( list_width
- total
) >> 1;
268 MaxSize
+= ( list_width
- total
) >> 1;
271 ** All done. Set the re-compute flag to
272 ** FALSE and store the list width.
275 TotalWidth
= list_width
;
280 ** The default DrawInfo pens. Just in case we don't
281 ** get them from the system.
283 UWORD DefDriPens
[] = {
284 0, 1, 1, 2, 1, 3, 1, 0, 2, 1, 2, 1 };
287 ** Listview rendering hook. Here's where the magic starts ;)
289 //SAVEDS ASM UBYTE *DisplayHookFunc( REG(a0) struct Hook *hook, REG(a2) Object *obj, REG(a1) struct lvRender *lvr )
290 SAVEDS ASM
REGFUNC3(UBYTE
*, DisplayHookFunc
,
291 REGPARAM(A0
, struct Hook
*, hook
),
292 REGPARAM(A2
, Object
*, obj
),
293 REGPARAM(A1
, struct lvRender
*, lvr
))
295 struct TextExtent te
;
297 FILEINFO
*fi
= ( FILEINFO
* )lvr
->lvr_Entry
;
298 UWORD
*pens
, numc
, w
, l
, cw
, h
;
301 ** Pick up the DrawInfo pen array.
303 pens
= lvr
->lvr_DrawInfo
? lvr
->lvr_DrawInfo
->dri_Pens
: DefDriPens
;
305 ** Pick up the width of the list.
307 w
= lvr
->lvr_Bounds
.MaxX
- lvr
->lvr_Bounds
.MinX
+ 1;
309 ** Pick up the list left-edge;
311 l
= lvr
->lvr_Bounds
.MinX
;
313 ** Pick up the height of the entry.
315 h
= lvr
->lvr_Bounds
.MaxY
- lvr
->lvr_Bounds
.MinY
+ 1;
318 ** First we render the background.
320 SetAPen( lvr
->lvr_RPort
, lvr
->lvr_State
== LVRS_SELECTED
? pens
[ FILLPEN
] : pens
[ BACKGROUNDPEN
] );
321 SetDrMd( lvr
->lvr_RPort
, JAM1
);
323 RectFill( lvr
->lvr_RPort
, lvr
->lvr_Bounds
.MinX
,
324 lvr
->lvr_Bounds
.MinY
,
325 lvr
->lvr_Bounds
.MaxX
,
326 lvr
->lvr_Bounds
.MaxY
);
329 ** When we are passed a NULL entry pointer
330 ** we are presumed to render the title. If your
331 ** listview does not have a title simply
332 ** recompute the columns and return NULL.
333 ** We have a title so here we go.
337 ** Recompute the column sizes. The routine
338 ** itself will decide if it's necessary.
340 ReComputeColumns( lvr
->lvr_RPort
, obj
, w
);
342 ** Set the pen for the title-entry.
344 SetAPen( lvr
->lvr_RPort
, pens
[ FILLPEN
] );
347 ** Set the pen for a non-title entry. Ofcourse
348 ** we can (should?) differenciate between normal and
349 ** selected here but I wont ;)
351 if ( fi
->fi_IsDir
) SetAPen( lvr
->lvr_RPort
, pens
[ HIGHLIGHTTEXTPEN
] );
352 else SetAPen( lvr
->lvr_RPort
, pens
[ TEXTPEN
] );
355 ** Obtain Name: column width. We check it against the
356 ** total list width so we do not go outside the
359 cw
= MIN( MaxName
, w
);
362 ** Pick up the name string or, when this
363 ** is a title call, the string "Name:".
365 str
= fi
? &fi
->fi_FileName
[ 0 ] : ( UBYTE
* )"Name:";
368 ** Compute the number of character we
371 numc
= TextFit( lvr
->lvr_RPort
, str
, strlen( str
), &te
, NULL
, 0, cw
, h
);
374 ** If the number of characters is
375 ** 0 we can stop right here and now.
377 if ( ! numc
) return( NULL
);
380 ** Move to the correct position
381 ** and render the text.
383 Move( lvr
->lvr_RPort
, l
, lvr
->lvr_Bounds
.MinY
+ lvr
->lvr_RPort
->TxBaseline
);
384 Text( lvr
->lvr_RPort
, str
, numc
);
387 ** Adjust the left-edge and width to
388 ** get past the Name: column.
391 w
= MAX(( WORD
)( w
- cw
), 0 );
394 ** Obtain Size: column width. We check it against the
395 ** total list width so we do not go outside the
398 cw
= MIN( MaxSize
, w
);
401 ** Pick up the size string or, when this
402 ** is a title call, the string "Size:".
404 str
= fi
? &fi
->fi_Size
[ 0 ] : ( UBYTE
* )"Size:";
407 ** Compute the number of character we
410 numc
= TextFit( lvr
->lvr_RPort
, str
, strlen( str
), &te
, NULL
, 0, cw
, h
);
413 ** If the number of characters is
414 ** 0 we can stop right here and now.
416 if ( ! numc
) return( NULL
);
419 ** Move to the correct position
420 ** and render the text.
422 Move( lvr
->lvr_RPort
, l
, lvr
->lvr_Bounds
.MinY
+ lvr
->lvr_RPort
->TxBaseline
);
423 Text( lvr
->lvr_RPort
, str
, numc
);
426 ** Adjust the left-edge and width to
427 ** get past the Size: column.
430 w
= MAX(( WORD
)( w
- cw
), 0 );
433 ** Obtain Date column width. We check it against the
434 ** total list width so we do not go outside the
437 cw
= MIN( MaxDate
, w
);
440 ** Pick up the date string or, when this
441 ** is a title call, the string "Date:".
443 str
= fi
? &fi
->fi_Date
[ 0 ] : ( UBYTE
* )"Date:";
446 ** Compute the number of character we
449 numc
= TextFit( lvr
->lvr_RPort
, str
, strlen( str
), &te
, NULL
, 0, cw
, h
);
452 ** If the number of characters is
453 ** 0 we can stop right here and now.
455 if ( ! numc
) return( NULL
);
458 ** Move to the correct position
459 ** and render the text.
461 Move( lvr
->lvr_RPort
, l
, lvr
->lvr_Bounds
.MinY
+ lvr
->lvr_RPort
->TxBaseline
);
462 Text( lvr
->lvr_RPort
, str
, numc
);
465 ** Return NULL. This is important. If we return a non-NULL
466 ** value the listview class will think it is a pointer to
467 ** the text to render and try to render it.
474 ** The comparrison hook. We do a simple name, dir/file
477 //SAVEDS ASM LONG CompareHookFunc( REG(a0) struct Hook *hook, REG(a2) Object *obj, REG(a1) struct lvCompare *lvc )
478 SAVEDS ASM
REGFUNC3(LONG
, CompareHookFunc
,
479 REGPARAM(A0
, struct Hook
*, hook
),
480 REGPARAM(A2
, Object
*, obj
),
481 REGPARAM(A1
, struct lvCompare
*,lvc
))
483 FILEINFO
*a
= ( FILEINFO
* )lvc
->lvc_EntryA
;
484 FILEINFO
*b
= ( FILEINFO
* )lvc
->lvc_EntryB
;
487 ** First we do a type comparrison to get the
488 ** directories at the top of the list.
490 if ( a
->fi_IsDir
&& ! b
->fi_IsDir
) return( -1L );
491 else if ( ! a
->fi_IsDir
&& b
->fi_IsDir
) return( 1L );
494 ** Otherwise we do a simple, case insensitive,
495 ** name string comparrison.
497 return( stricmp( &a
->fi_FileName
[ 0 ], &b
->fi_FileName
[ 0 ] ));
502 ** A IDCMP hook for the window which allows us
503 ** to control the listview from the keyboard.
505 //SAVEDS ASM VOID ScrollHookFunc( REG(a0) struct Hook *hook, REG(a2) Object *obj, REG(a1) struct IntuiMessage *msg )
506 SAVEDS ASM
REGFUNC3(VOID
, ScrollHookFunc
,
507 REGPARAM(A0
, struct Hook
*, hook
),
508 REGPARAM(A2
, Object
*, obj
),
509 REGPARAM(A1
, struct IntuiMessage
*, msg
))
511 struct Window
*window
;
512 Object
*lv_obj
= ( Object
* )hook
->h_Data
;
515 ** Obtain window pointer.
517 GetAttr( WINDOW_Window
, obj
, ( IPTR
* )&window
);
520 ** What key is pressed?
522 switch ( msg
->Code
) {
526 ** UP - Move entry up.
527 ** SHIFT + UP - Move page up.
528 ** CTRL + UP - Move to the top.
530 if ( msg
->Qualifier
& ( IEQUALIFIER_LSHIFT
| IEQUALIFIER_RSHIFT
))
531 SetGadgetAttrs(( struct Gadget
* )lv_obj
, window
, NULL
, LISTV_Select
, LISTV_Select_Page_Up
, TAG_END
);
532 else if ( msg
->Qualifier
& IEQUALIFIER_CONTROL
)
533 SetGadgetAttrs(( struct Gadget
* )lv_obj
, window
, NULL
, LISTV_Select
, LISTV_Select_First
, TAG_END
);
535 SetGadgetAttrs(( struct Gadget
* )lv_obj
, window
, NULL
, LISTV_Select
, LISTV_Select_Previous
, TAG_END
);
540 ** DOWN - Move entry down.
541 ** SHIFT + DOWN - Move page down.
542 ** CTRL + DOWN - Move to the end.
544 if ( msg
->Qualifier
& ( IEQUALIFIER_LSHIFT
| IEQUALIFIER_RSHIFT
))
545 SetGadgetAttrs(( struct Gadget
* )lv_obj
, window
, NULL
, LISTV_Select
, LISTV_Select_Page_Down
, TAG_END
);
546 else if ( msg
->Qualifier
& IEQUALIFIER_CONTROL
)
547 SetGadgetAttrs(( struct Gadget
* )lv_obj
, window
, NULL
, LISTV_Select
, LISTV_Select_Last
, TAG_END
);
549 SetGadgetAttrs(( struct Gadget
* )lv_obj
, window
, NULL
, LISTV_Select
, LISTV_Select_Next
, TAG_END
);
556 ** RETURN or ENTER - Report the listview ID to the event handler.
558 DoMethod( obj
, WM_REPORT_ID
, (( struct Gadget
* )lv_obj
)->GadgetID
, 0L );
565 ** The hook structures as added to the
566 ** listview object and window object.
568 ** If your compiler complaints about the
569 ** HOOKFUNC typedef uncomment the below line.
572 /* typedef ULONG (*HOOKFUNC)(); */
574 struct Hook ResourceHook
= { {NULL
, NULL
}, (HOOKFUNC
)ResourceHookFunc
, NULL
, NULL
};
575 struct Hook DisplayHook
= { {NULL
, NULL
}, (HOOKFUNC
)DisplayHookFunc
, NULL
, NULL
};
576 struct Hook CompareHook
= { {NULL
, NULL
}, (HOOKFUNC
)CompareHookFunc
, NULL
, NULL
};
577 struct Hook ScrollHook
= { {NULL
, NULL
}, (HOOKFUNC
)ScrollHookFunc
, NULL
, NULL
};
580 ** Scan the directory "name".
582 VOID
ScanDirectory( UBYTE
*name
, Object
*obj
)
584 struct ExAllControl
*eac
;
585 struct ExAllData
*ead
, *tmp
;
586 struct FileInfoBlock
*fib
;
591 ** We need to recompute the columns.
596 ** Get a lock to the directory.
598 if (( lock
= Lock( name
, ACCESS_READ
))) {
600 ** Allocate a FileInfoBlock structure.
602 if (( fib
= ( struct FileInfoBlock
* )AllocDosObject( DOS_FIB
, NULL
))) {
606 if ( Examine( lock
, fib
)) {
608 ** Is this a directory?
610 if ( fib
->fib_DirEntryType
> 0 ) {
612 ** Allocate ExAll() control structure.
614 if (( eac
= ( struct ExAllControl
* )AllocDosObject( DOS_EXALLCONTROL
, NULL
))) {
618 eac
->eac_LastKey
= (IPTR
)NULL
;
620 ** Allocate ExAll() buffer.
622 if (( ead
= ( struct ExAllData
* )AllocVec( 10 * sizeof( struct ExAllData
), MEMF_PUBLIC
))) {
630 ismore
= ExAll( lock
, ead
, 10 * sizeof( struct ExAllData
), ED_DATE
, eac
);
635 if (( ! ismore
) && ( IoErr() != ERROR_NO_MORE_ENTRIES
))
641 if ( ! eac
->eac_Entries
)
645 ** Pick up data pointer.
653 AddEntry( NULL
, obj
, tmp
, LVAP_SORTED
);
661 ** Deallocate ExAll() buffer.
666 ** Deallocate ExAll() control structure.
668 FreeDosObject( DOS_EXALLCONTROL
, eac
);
673 ** Deallocate FileInfoBlock structure.
675 FreeDosObject( DOS_FIB
, fib
);
691 VOID
StartDemo( void )
695 Object
*WO_DirWin
, *GO_DirList
, *GO_Quit
, *GO_NewDir
;
700 UBYTE name
[ 512 ], *ptr
;
704 ** Parse command line?
706 if (( ra
= ReadArgs( "NAME", &dname
, NULL
))) {
708 ** Copy the name into the buffer.
710 if ( dname
) strcpy( name
, ( UBYTE
* )dname
);
713 ** Create the listview object.
715 GO_DirList
= ListviewObject
,
716 LISTV_ResourceHook
, &ResourceHook
,
717 LISTV_DisplayHook
, &DisplayHook
,
718 LISTV_TitleHook
, &DisplayHook
,
719 LISTV_CompareHook
, &CompareHook
,
723 ** Put it in the IDCMP hook.
725 ScrollHook
.h_Data
= ( APTR
)GO_DirList
;
727 ** Create the window.
729 WO_DirWin
= WindowObject
,
730 WINDOW_Title
, "MultiCol",
731 WINDOW_RMBTrap
, TRUE
,
732 WINDOW_ScaleWidth
, 50,
733 WINDOW_ScaleHeight
, 30,
734 WINDOW_AutoAspect
, TRUE
,
735 WINDOW_SmartRefresh
, TRUE
,
736 WINDOW_IDCMPHookBits
, IDCMP_RAWKEY
,
737 WINDOW_IDCMPHook
, &ScrollHook
,
739 VGroupObject
, HOffset( 4 ), VOffset( 4 ), Spacing( 4 ), GROUP_BackFill
, SHINE_RASTER
,
742 StartMember
, GO_DirList
, EndMember
,
744 GO_NewDir
= KeyString( NULL
, name
, 512, ID_NEWDIR
), FixMinHeight
,
750 VarSpace( DEFAULT_WEIGHT
),
751 StartMember
, GO_Quit
= KeyButton( "_Quit", ID_QUIT
), EndMember
,
752 VarSpace( DEFAULT_WEIGHT
),
753 EndObject
, FixMinHeight
,
759 ** Window created OK?
765 if ( GadgetKey( WO_DirWin
, GO_Quit
, "q" )) {
769 if (( win
= WindowOpen( WO_DirWin
))) {
771 ** Obtain signal mask.
773 GetAttr( WINDOW_SigMask
, WO_DirWin
, &signal
);
775 ** Read in the directory.
777 WindowBusy( WO_DirWin
);
778 ScanDirectory( name
, GO_DirList
);
779 WindowReady( WO_DirWin
);
783 RefreshList( win
, GO_DirList
);
789 while (( rc
= HandleEvent( WO_DirWin
)) != WMHI_NOMORE
) {
793 case WMHI_CLOSEWINDOW
:
799 ** Get selected entry.
801 if (( fi
= ( FILEINFO
* )FirstSelected( GO_DirList
))) {
803 ** Is the entry a directory?
805 if ( fi
->fi_IsDir
) {
807 ** AddPart() the name to the buffer.
809 AddPart( name
, &fi
->fi_FileName
[ 0 ], 512 );
811 ** Refresh the string gadget.
813 SetGadgetAttrs(( struct Gadget
* )GO_NewDir
, win
, NULL
, STRINGA_TextVal
, name
, TAG_END
);
818 WindowBusy( WO_DirWin
);
819 ClearList( win
, GO_DirList
);
820 ScanDirectory( name
, GO_DirList
);
821 RefreshList( win
, GO_DirList
);
822 WindowReady( WO_DirWin
);
829 ** Copy the new name to the buffer.
831 GetAttr( STRINGA_TextVal
, GO_NewDir
, ( IPTR
* )&ptr
);
842 DisposeObject( WO_DirWin
);
845 ** Delete the ReadArgs structure.