2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
9 #include <exec/memory.h>
11 #include <utility/utility.h>
12 #include <intuition/intuition.h>
13 #include <graphics/rastport.h>
14 #include <libraries/gadtools.h>
15 #include <libraries/asl.h>
16 #include <devices/rawkeycodes.h>
18 #include <proto/exec.h>
19 #include <proto/dos.h>
20 #include <proto/intuition.h>
21 #include <proto/graphics.h>
22 #include <proto/utility.h>
23 #include <proto/alib.h>
24 #include <proto/gadtools.h>
25 #include <proto/asl.h>
26 #include "con_handler_intern.h"
28 #include "completion.h"
34 #include <aros/debug.h>
36 /****************************************************************************************/
44 /****************************************************************************************/
48 struct filehandle
*fh
;
49 struct List matchlist
;
56 UBYTE pattern
[256 * 2 + 3];
61 /****************************************************************************************/
63 /* Delay opening of the gadtools.library to the first time InitCompletion is called */
65 static struct completioninfo
*InitCompletion(struct filehandle
*fh
, BOOL withinfo
)
67 struct completioninfo
*ci
= NULL
;
70 if (fh
->gtbase
== NULL
)
71 fh
->gtbase
= OpenLibrary("gadtools.library", 39);
73 if (fh
->gfxbase
== NULL
)
74 fh
->gfxbase
= (APTR
) OpenLibrary("graphics.library", 39);
76 if (fh
->lastwritetask
&& GadToolsBase
)
77 if (fh
->lastwritetask
->tc_Node
.ln_Type
== NT_PROCESS
)
79 if ((pool
= CreatePool(MEMF_CLEAR
, 1024, 1024)))
83 if ((ci
= (struct completioninfo
*) AllocPooled(pool
, sizeof(*ci
))))
87 ci
->withinfo
= withinfo
;
88 NewList(&ci
->matchlist
);
104 /****************************************************************************************/
106 static void CleanupCompletion(struct completioninfo
*ci
)
108 DeletePool(ci
->pool
);
111 /****************************************************************************************/
113 static void PrepareCompletion(struct filehandle
*fh
, struct completioninfo
*ci
)
116 BOOL in_quotes
= FALSE
;
118 /* Find word start */
120 ci
->wordstart
= i
= ci
->fh
->inputstart
;
122 while (i
!= ci
->fh
->inputpos
)
124 switch (ci
->fh
->inputbuffer
[i
++])
127 in_quotes
= !in_quotes
;
139 strncpy(ci
->dirpart
, &ci
->fh
->inputbuffer
[ci
->wordstart
], ci
->fh
->inputpos
- ci
->wordstart
);
140 strcpy(ci
->filepart
, FilePart(ci
->dirpart
));
142 *(PathPart(ci
->dirpart
)) = '\0';
144 ci
->wordquoted
= in_quotes
;
146 D(bug("PrepareCompletion: dirpart = \"%s\" filepart = \"%s\"\n", ci
->dirpart
, ci
->filepart
));
149 /****************************************************************************************/
151 static void AddQuotes(struct completioninfo
*ci
, STRPTR s
, LONG s_size
)
153 LONG len
= strlen(s
);
157 if (len
< s_size
- 1)
158 memmove(s
+ 1, s
, len
+ 1);
166 if (len
< s_size
- 3)
168 if ((s
[len
] != '/') && (s
[len
] != ':'))
176 /****************************************************************************************/
178 static void InsertIntoConBuffer(struct completioninfo
*ci
, STRPTR s
)
180 WORD i
= ci
->fh
->conbufferpos
;
182 /* Lame code, but speed is no issue here */
183 while ((ci
->fh
->conbuffersize
< CONSOLEBUFFER_SIZE
) && *s
)
185 memmove(&ci
->fh
->consolebuffer
[i
+ 1], &ci
->fh
->consolebuffer
[i
], ci
->fh
->conbuffersize
- i
);
187 ci
->fh
->consolebuffer
[i
++] = *s
++;
188 ci
->fh
->conbuffersize
++;
192 /****************************************************************************************/
194 static void DoFileReq(struct filehandle
*fh
, struct completioninfo
*ci
)
196 struct Library
*AslBase
;
199 if ((lock
= DupLock(((struct Process
*) ci
->fh
->lastwritetask
)->pr_CurrentDir
)))
201 olddir
= CurrentDir(lock
);
203 if ((AslBase
= OpenLibrary("asl.library", 36)))
205 struct FileRequester
*fr
;
206 struct TagItem tags
[] =
208 { ASLFR_Window
, (IPTR
) ci
->fh
->window
},
209 { ASLFR_DoPatterns
, (IPTR
) TRUE
},
210 { ASLFR_InitialPattern
, 0 },
213 tags
[2].ti_Data
= (IPTR
) (ci
->withinfo
? "#?" : "~(#?.info)");
215 if ((fr
= AllocAslRequest(ASL_FileRequest
, tags
)))
217 if (AslRequest(fr
, NULL
))
221 strcpy(ci
->match
, fr
->fr_Drawer
);
222 AddPart(ci
->match
, fr
->fr_File
, sizeof(ci
->match
));
226 if (strchr(ci
->match
, ' '))
227 AddQuotes(ci
, ci
->match
, sizeof(ci
->match
));
229 c
= ci
->match
[strlen(ci
->match
) - 1];
230 if ((c
!= '/') && (c
!= ':'))
232 strncat(ci
->match
, " ", sizeof(ci
->match
));
235 InsertIntoConBuffer(ci
, ci
->match
);
242 CloseLibrary(AslBase
);
244 } /* if ((AslBase = OpenLibrary("asl.library", 36))) */
251 /****************************************************************************************/
253 static BOOL
PreparePattern(struct filehandle
*fh
, struct completioninfo
*ci
)
257 parsecode
= ParsePatternNoCase(ci
->filepart
, ci
->pattern
, sizeof(ci
->pattern
));
264 strncat(ci
->filepart
, "#?", sizeof(ci
->filepart
));
268 strncat(ci
->filepart
, "~(#?.info)", sizeof(ci
->filepart
));
270 parsecode
= ParsePatternNoCase(ci
->filepart
, ci
->pattern
, sizeof(ci
->pattern
));
274 return (parsecode
== 1);
278 /****************************************************************************************/
280 static void AddMatchNode(struct filehandle
*fh
, struct completioninfo
*ci
, STRPTR name
, WORD type
)
282 struct matchnode
*mn
;
283 struct Node
*prev
, *check
;
287 size
= strlen(name
) + 1 + sizeof(struct matchnode
) + ((type
> 0) ? 1 : 0);
289 if ((mn
= AllocPooled(ci
->pool
, size
)))
291 strcpy(mn
->name
, name
);
292 mn
->node
.ln_Name
= mn
->name
;
295 strcat(mn
->name
, "/");
297 strcat(mn
->name
, ":");
299 /* Sort into matchlist */
302 ForeachNode(&ci
->matchlist
, check
)
306 match
= Stricmp(mn
->name
, ((struct matchnode
*) check
)->name
);
320 Insert(&ci
->matchlist
, (struct Node
*) mn
, prev
);
325 FreePooled(ci
->pool
, mn
, size
);
330 /****************************************************************************************/
332 static void ScanDir(struct filehandle
*fh
, struct completioninfo
*ci
)
334 struct FileInfoBlock
*fib
;
337 if ((fib
= AllocDosObject(DOS_FIB
, 0)))
339 if ((lock
= Lock(ci
->dirpart
, SHARED_LOCK
)))
341 if (Examine(lock
, fib
))
343 while (ExNext(lock
, fib
))
345 if (MatchPatternNoCase(ci
->pattern
, fib
->fib_FileName
))
347 BOOL isdir
= (fib
->fib_DirEntryType
> 0);
349 AddMatchNode(fh
, ci
, fib
->fib_FileName
, (isdir
? 1 : 0));
357 FreeDosObject(DOS_FIB
, fib
);
361 /****************************************************************************************/
363 static void ScanVol(struct filehandle
*fh
, struct completioninfo
*ci
)
365 struct DosList
*dlist
;
367 dlist
= LockDosList(LDF_READ
| LDF_VOLUMES
| LDF_DEVICES
| LDF_ASSIGNS
);
369 while ((dlist
= NextDosEntry(dlist
, LDF_VOLUMES
| LDF_ASSIGNS
| LDF_DEVICES
)) != NULL
)
371 STRPTR devname
= AROS_BSTR_ADDR(dlist
->dol_Name
);
373 if (MatchPatternNoCase(ci
->pattern
, devname
))
375 AddMatchNode(fh
, ci
, devname
, 2);
379 UnLockDosList(LDF_READ
| LDF_VOLUMES
| LDF_DEVICES
| LDF_ASSIGNS
);
382 static void DoScan(struct filehandle
*fh
, struct completioninfo
*ci
)
386 if ((lock
= DupLock(((struct Process
*) ci
->fh
->lastwritetask
)->pr_CurrentDir
)))
388 olddir
= CurrentDir(lock
);
390 if (ci
->dirpart
[0] == 0)
399 /****************************************************************************************/
401 #define BUTTON_EXTRA_WIDTH 16
402 #define BUTTON_EXTRA_HEIGHT 6
405 #define BUTTON_SPACING_X 8
406 #define LV_BUTTON_SPACING_Y 4
407 #define LV_EXTRA_HEIGHT 4
409 #define ID_LISTVIEW 1
413 /****************************************************************************************/
415 static BOOL
DoChooseReq(struct filehandle
*fh
, struct completioninfo
*ci
)
417 static const char oktext
[] = "Ok";
418 static const char canceltext
[] = "Cancel";
419 static const char titletext
[] = "Select filename";
421 struct RastPort temprp
;
422 struct DrawInfo
*dri
;
424 struct Gadget
*gadlist
, *gad
, *lvgad
;
427 WORD i
, buttonwidth
, buttonheight
, visible_lv_lines
;
428 WORD winwidth
, winheight
, winleft
, wintop
;
429 LONG sec
, micro
, secold
, microold
;
432 if ((dri
= GetScreenDrawInfo(ci
->fh
->window
->WScreen
)))
434 if ((vi
= GetVisualInfoA(ci
->fh
->window
->WScreen
, NULL
)))
436 InitRastPort(&temprp
);
437 SetFont(&temprp
, dri
->dri_Font
);
439 buttonwidth
= TextLength(&temprp
, oktext
, strlen(oktext
));
440 i
= TextLength(&temprp
, canceltext
, strlen(canceltext
));
441 buttonwidth
= (buttonwidth
> i
) ? buttonwidth
: i
;
442 buttonwidth
+= BUTTON_EXTRA_WIDTH
;
444 buttonheight
= dri
->dri_Font
->tf_YSize
+ BUTTON_EXTRA_HEIGHT
;
446 i
= ci
->nummatchnodes
> 15 ? 15 : ci
->nummatchnodes
;
450 winheight
= i
* (dri
->dri_Font
->tf_YSize
+ 1) + LV_EXTRA_HEIGHT
+ LV_BUTTON_SPACING_Y
+ buttonheight
453 winwidth
= buttonwidth
* 2 + BUTTON_SPACING_X
+ BORDER_X
* 2;
454 i
= ci
->fh
->window
->WScreen
->Width
* 1 / 3;
458 winleft
= ci
->fh
->window
->WScreen
->MouseX
459 - (winwidth
+ ci
->fh
->window
->WScreen
->WBorLeft
+ ci
->fh
->window
->WScreen
->WBorRight
) / 2;
460 wintop
= ci
->fh
->window
->WScreen
->MouseY
461 - (winheight
+ ci
->fh
->window
->WScreen
->WBorTop
+ dri
->dri_Font
->tf_YSize
+ 1
462 + ci
->fh
->window
->WScreen
->WBorBottom
) / 2;
464 gad
= CreateContext(&gadlist
);
466 ng
.ng_LeftEdge
= ci
->fh
->window
->WScreen
->WBorLeft
+ BORDER_X
;
467 ng
.ng_TopEdge
= ci
->fh
->window
->WScreen
->WBorTop
+ dri
->dri_Font
->tf_YSize
+ 1 + BORDER_Y
;
468 ng
.ng_Width
= winwidth
- BORDER_X
* 2;
469 ng
.ng_Height
= winheight
- BORDER_Y
* 2 - buttonheight
- LV_BUTTON_SPACING_Y
;
470 ng
.ng_GadgetText
= NULL
;
471 ng
.ng_TextAttr
= NULL
;
472 ng
.ng_GadgetID
= ID_LISTVIEW
;
474 ng
.ng_VisualInfo
= vi
;
478 struct TagItem lvtags
[] =
480 { GTLV_Labels
, (IPTR
) &ci
->matchlist
},
481 { GTLV_ShowSelected
, 0 },
482 { GTLV_Selected
, 0 },
485 gad
= lvgad
= CreateGadgetA(LISTVIEW_KIND
, gad
, &ng
, lvtags
);
487 visible_lv_lines
= (ng
.ng_Height
- LV_EXTRA_HEIGHT
) / (dri
->dri_Font
->tf_YSize
+ 1);
490 ng
.ng_TopEdge
+= ng
.ng_Height
+ LV_BUTTON_SPACING_Y
;
491 ng
.ng_Width
= buttonwidth
;
492 ng
.ng_Height
= buttonheight
;
493 ng
.ng_GadgetText
= oktext
;
494 ng
.ng_GadgetID
= ID_OK
;
496 gad
= CreateGadgetA(BUTTON_KIND
, gad
, &ng
, NULL
);
498 ng
.ng_LeftEdge
+= winwidth
- buttonwidth
- BORDER_X
* 2;
499 ng
.ng_GadgetText
= canceltext
;
500 ng
.ng_GadgetID
= ID_CANCEL
;
502 gad
= CreateGadgetA(BUTTON_KIND
, gad
, &ng
, NULL
);
506 struct TagItem wintags
[] =
508 { WA_CustomScreen
, (IPTR
) ci
->fh
->window
->WScreen
},
509 { WA_Title
, (IPTR
) titletext
},
510 { WA_CloseGadget
, TRUE
},
511 { WA_DragBar
, TRUE
},
512 { WA_DepthGadget
, TRUE
},
513 { WA_Activate
, TRUE
},
514 { WA_AutoAdjust
, TRUE
},
515 { WA_Left
, winleft
},
517 { WA_InnerWidth
, winwidth
},
518 { WA_InnerHeight
, winheight
},
519 { WA_IDCMP
, IDCMP_CLOSEWINDOW
| IDCMP_RAWKEY
| IDCMP_VANILLAKEY
| LISTVIEWIDCMP
| BUTTONIDCMP
},
520 { WA_Gadgets
, (IPTR
) gadlist
},
524 if ((win
= OpenWindowTagList(NULL
, wintags
)))
529 CurrentTime(&secold
, µold
);
531 while (!done
&& !doit
)
533 struct IntuiMessage
*msg
;
535 WaitPort(win
->UserPort
);
537 while ((msg
= GT_GetIMsg(win
->UserPort
)))
541 case IDCMP_CLOSEWINDOW
:
545 case IDCMP_VANILLAKEY
:
562 BOOL extreme
= FALSE
;
579 case RAWKEY_PAGEDOWN
:
594 case RAWKEY_NM_WHEEL_UP
:
598 case RAWKEY_NM_WHEEL_DOWN
:
603 if (msg
->Qualifier
& (IEQUALIFIER_LSHIFT
| IEQUALIFIER_RSHIFT
))
608 if (msg
->Qualifier
& (IEQUALIFIER_LALT
| IEQUALIFIER_RALT
| IEQUALIFIER_CONTROL
))
616 struct TagItem tags
[] =
618 { GTLV_Selected
, (IPTR
) &getsel
},
619 { TAG_IGNORE
, TRUE
},
623 GT_GetGadgetAttrsA(lvgad
, win
, NULL
, tags
);
628 scroll
*= ci
->nummatchnodes
;
632 scroll
*= visible_lv_lines
;
637 sel
= ci
->nummatchnodes
- 1;
638 if (sel
>= ci
->nummatchnodes
)
641 tags
[0].ti_Data
= (IPTR
) sel
;
642 tags
[1].ti_Tag
= GTLV_MakeVisible
;
643 tags
[1].ti_Data
= (IPTR
) sel
;
645 GT_SetGadgetAttrsA(lvgad
, win
, NULL
, tags
);
651 gad
= (struct Gadget
*) msg
->IAddress
;
653 switch (gad
->GadgetID
)
664 CurrentTime(&sec
, µ
);
665 if (DoubleClick(secold
, microold
, sec
, micro
))
673 } /* switch(msg->Class) */
677 } /* while((msg = GT_GetIMsg(win->UserPort))) */
679 } /* while (!done && !doit) */
686 struct TagItem gettags
[] =
688 { GTLV_Selected
, (IPTR
) &sel
},
691 GT_GetGadgetAttrsA(lvgad
, win
, NULL
, gettags
);
694 ForeachNode(&ci
->matchlist
, node
)
698 AddPart(ci
->match
, node
->ln_Name
, sizeof(ci
->match
));
710 } /* if ((win = OpenWindowTagList(NULL, wintags))) */
714 FreeGadgets(gadlist
);
717 } /* if ((vi = GetVisualInfoA(ci->fh->window->WScreen, NULL))) */
719 FreeScreenDrawInfo(ci
->fh
->window
->WScreen
, dri
);
721 } /* if ((dri = GetScreenDrawInfo(fh->window->WScreen))) */
726 /****************************************************************************************/
728 void Completion(struct filehandle
*fh
, BOOL withinfo
)
730 struct completioninfo
*ci
;
732 if ((ci
= InitCompletion(fh
, withinfo
)))
734 PrepareCompletion(fh
, ci
);
736 if (!ci
->dirpart
[0] && !ci
->filepart
[0])
742 if (PreparePattern(fh
, ci
))
744 BOOL doprint
= FALSE
;
748 strncpy(ci
->match
, ci
->dirpart
, sizeof(ci
->match
));
750 if (ci
->nummatchnodes
== 1)
752 AddPart(ci
->match
, ((struct matchnode
*) GetHead(&ci
->matchlist
))->name
, sizeof(ci
->match
));
756 else if (ci
->nummatchnodes
> 1)
758 doprint
= DoChooseReq(fh
, ci
);
766 if (strchr(ci
->match
, ' '))
767 AddQuotes(ci
, ci
->match
, sizeof(ci
->match
));
769 /* Insert as many backspaces in front of the string,
770 to erase whole "word" first (starting at ci->wordstart)
771 before reprinting expanded filename */
773 backspaces
= ci
->fh
->inputpos
- ci
->wordstart
;
775 memmove(ci
->match
+ backspaces
, ci
->match
, sizeof(ci
->match
) - backspaces
);
776 memset(ci
->match
, 8, backspaces
);
778 c
= ci
->match
[strlen(ci
->match
) - 1];
779 if ((c
!= '/') && (c
!= ':'))
781 strncat(ci
->match
, " ", sizeof(ci
->match
));
784 InsertIntoConBuffer(ci
, ci
->match
);
790 CleanupCompletion(ci
);
792 } /* if ((ci = InitCompletion())) */
796 /****************************************************************************************/