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];
60 /****************************************************************************************/
62 /* Delay opening of the gadtools.library to the first time InitCompletion is called */
64 static struct completioninfo
*InitCompletion(struct filehandle
*fh
)
66 struct completioninfo
*ci
= NULL
;
69 if (fh
->gtbase
== NULL
)
70 fh
->gtbase
= OpenLibrary("gadtools.library", 39);
72 if (fh
->gfxbase
== NULL
)
73 fh
->gfxbase
= (APTR
)OpenLibrary("graphics.library", 39);
75 if (fh
->lastwritetask
&& GadToolsBase
)
76 if (fh
->lastwritetask
->tc_Node
.ln_Type
== NT_PROCESS
)
78 if ((pool
= CreatePool(MEMF_CLEAR
, 1024, 1024)))
82 if ((ci
= (struct completioninfo
*)AllocPooled(pool
, sizeof(*ci
))))
86 NewList(&ci
->matchlist
);
102 /****************************************************************************************/
104 static void CleanupCompletion(struct completioninfo
*ci
)
106 DeletePool(ci
->pool
);
109 /****************************************************************************************/
111 static void PrepareCompletion(struct filehandle
*fh
, struct completioninfo
*ci
)
114 BOOL in_quotes
= FALSE
;
116 /* Find word start */
118 ci
->wordstart
= i
= ci
->fh
->inputstart
;
120 while(i
!= ci
->fh
->inputpos
)
122 switch(ci
->fh
->inputbuffer
[i
++])
125 in_quotes
= !in_quotes
;
126 if (in_quotes
) ci
->wordstart
= i
;
130 if (!in_quotes
) ci
->wordstart
= i
;
136 &ci
->fh
->inputbuffer
[ci
->wordstart
],
137 ci
->fh
->inputpos
- ci
->wordstart
);
138 strcpy(ci
->filepart
, FilePart(ci
->dirpart
));
140 *(PathPart(ci
->dirpart
)) = '\0';
142 ci
->wordquoted
= in_quotes
;
144 D(bug("PrepareCompletion: dirpart = \"%s\" filepart = \"%s\"\n", ci
->dirpart
, ci
->filepart
));
147 /****************************************************************************************/
149 static void AddQuotes(struct completioninfo
*ci
, STRPTR s
, LONG s_size
)
151 LONG len
= strlen(s
);
155 if (len
< s_size
- 1)
156 memmove(s
+ 1, s
, len
+ 1);
164 if (len
< s_size
- 3)
166 if ((s
[len
] != '/') && (s
[len
] != ':'))
174 /****************************************************************************************/
176 static void InsertIntoConBuffer(struct completioninfo
*ci
, STRPTR s
)
178 WORD i
= ci
->fh
->conbufferpos
;
180 /* Lame code, but speed is no issue here */
181 while((ci
->fh
->conbuffersize
< CONSOLEBUFFER_SIZE
) && *s
)
183 memmove(&ci
->fh
->consolebuffer
[i
+ 1],
184 &ci
->fh
->consolebuffer
[i
],
185 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
, (IPTR
)"~(#?.info)" },
214 if ((fr
= AllocAslRequest(ASL_FileRequest
, tags
)))
216 if (AslRequest(fr
, NULL
))
220 strcpy(ci
->match
, fr
->fr_Drawer
);
221 AddPart(ci
->match
, fr
->fr_File
, sizeof(ci
->match
));
225 if (strchr(ci
->match
, ' ')) AddQuotes(ci
, ci
->match
, sizeof(ci
->match
));
227 c
= ci
->match
[strlen(ci
->match
) - 1];
228 if ((c
!= '/') && (c
!= ':'))
230 strncat(ci
->match
, " ", sizeof(ci
->match
));
233 InsertIntoConBuffer(ci
, ci
->match
);
240 CloseLibrary(AslBase
);
242 } /* if ((AslBase = OpenLibrary("asl.library", 36))) */
249 /****************************************************************************************/
251 static BOOL
PreparePattern(struct filehandle
*fh
, struct completioninfo
*ci
)
255 parsecode
= ParsePatternNoCase(ci
->filepart
, ci
->pattern
, sizeof(ci
->pattern
));
260 strncat(ci
->filepart
, "~(#?.info)", sizeof(ci
->filepart
));
261 parsecode
= ParsePatternNoCase(ci
->filepart
, ci
->pattern
, sizeof(ci
->pattern
));
265 return (parsecode
== 1);
269 /****************************************************************************************/
271 static void AddMatchNode(struct filehandle
*fh
, struct completioninfo
*ci
,
272 STRPTR name
, WORD type
)
274 struct matchnode
*mn
;
275 struct Node
*prev
, *check
;
279 size
= strlen(name
) + 1 + sizeof(struct matchnode
) + ((type
> 0) ? 1 : 0);
281 if ((mn
= AllocPooled(ci
->pool
, size
)))
283 strcpy(mn
->name
, name
);
284 mn
->node
.ln_Name
= mn
->name
;
286 if (type
== 1) strcat(mn
->name
, "/");
287 if (type
== 2) strcat(mn
->name
, ":");
289 /* Sort into matchlist */
292 ForeachNode(&ci
->matchlist
, check
)
296 match
= Stricmp(mn
->name
, ((struct matchnode
*)check
)->name
);
297 if (match
< 0) break;
309 Insert(&ci
->matchlist
, (struct Node
*)mn
, prev
);
314 FreePooled(ci
->pool
, mn
, size
);
319 /****************************************************************************************/
321 static void ScanDir(struct filehandle
*fh
, struct completioninfo
*ci
)
323 struct FileInfoBlock
*fib
;
326 if ((fib
= AllocDosObject(DOS_FIB
, 0)))
328 if ((lock
= Lock(ci
->dirpart
, SHARED_LOCK
)))
330 if (Examine(lock
, fib
))
332 while(ExNext(lock
, fib
))
334 if (MatchPatternNoCase(ci
->pattern
, fib
->fib_FileName
))
336 BOOL isdir
= (fib
->fib_DirEntryType
> 0);
338 AddMatchNode(fh
, ci
, fib
->fib_FileName
, (isdir
? 1 : 0));
346 FreeDosObject(DOS_FIB
, fib
);
350 /****************************************************************************************/
353 static void ScanVol(struct filehandle
*fh
, struct completioninfo
*ci
)
355 struct DosList
*dlist
;
357 dlist
= LockDosList(LDF_READ
| LDF_VOLUMES
| LDF_DEVICES
| LDF_ASSIGNS
);
359 while ((dlist
= NextDosEntry(dlist
, LDF_VOLUMES
| LDF_ASSIGNS
| LDF_DEVICES
)) != NULL
)
361 STRPTR devname
= AROS_BSTR_ADDR(dlist
->dol_Name
);
363 if (MatchPatternNoCase(ci
->pattern
, devname
))
365 AddMatchNode(fh
, ci
, devname
, 2);
369 UnLockDosList(LDF_READ
| LDF_VOLUMES
| LDF_DEVICES
| LDF_ASSIGNS
);
372 static void DoScan(struct filehandle
*fh
, struct completioninfo
*ci
)
376 if ((lock
= DupLock(((struct Process
*)ci
->fh
->lastwritetask
)->pr_CurrentDir
)))
378 olddir
= CurrentDir(lock
);
380 if (ci
->dirpart
[0] == 0) ScanVol(fh
, ci
);
388 /****************************************************************************************/
390 #define BUTTON_EXTRA_WIDTH 16
391 #define BUTTON_EXTRA_HEIGHT 6
394 #define BUTTON_SPACING_X 8
395 #define LV_BUTTON_SPACING_Y 4
396 #define LV_EXTRA_HEIGHT 4
398 #define ID_LISTVIEW 1
402 /****************************************************************************************/
404 static BOOL
DoChooseReq(struct filehandle
*fh
, struct completioninfo
*ci
)
406 static const char oktext
[] = "Ok";
407 static const char canceltext
[] = "Cancel";
408 static const char titletext
[] = "Select filename";
410 struct RastPort temprp
;
411 struct DrawInfo
*dri
;
413 struct Gadget
*gadlist
, *gad
, *lvgad
;
416 WORD i
, buttonwidth
, buttonheight
, visible_lv_lines
;
417 WORD winwidth
, winheight
, winleft
, wintop
;
418 LONG sec
, micro
, secold
, microold
;
421 if ((dri
= GetScreenDrawInfo(ci
->fh
->window
->WScreen
)))
423 if ((vi
= GetVisualInfoA(ci
->fh
->window
->WScreen
, NULL
)))
425 InitRastPort(&temprp
);
426 SetFont(&temprp
, dri
->dri_Font
);
428 buttonwidth
= TextLength(&temprp
, oktext
, strlen(oktext
));
429 i
= TextLength(&temprp
, canceltext
, strlen(canceltext
));
430 buttonwidth
= (buttonwidth
> i
) ? buttonwidth
: i
;
431 buttonwidth
+= BUTTON_EXTRA_WIDTH
;
433 buttonheight
= dri
->dri_Font
->tf_YSize
+ BUTTON_EXTRA_HEIGHT
;
435 i
= ci
->nummatchnodes
> 15 ? 15 : ci
->nummatchnodes
;
438 winheight
= i
* (dri
->dri_Font
->tf_YSize
+ 1) +
440 LV_BUTTON_SPACING_Y
+
444 winwidth
= buttonwidth
* 2 + BUTTON_SPACING_X
+ BORDER_X
* 2;
445 i
= ci
->fh
->window
->WScreen
->Width
* 1 / 3;
446 if (i
> winwidth
) winwidth
= i
;
448 winleft
= ci
->fh
->window
->WScreen
->MouseX
-
449 (winwidth
+ ci
->fh
->window
->WScreen
->WBorLeft
+
450 ci
->fh
->window
->WScreen
->WBorRight
) / 2;
451 wintop
= ci
->fh
->window
->WScreen
->MouseY
-
452 (winheight
+ ci
->fh
->window
->WScreen
->WBorTop
+
453 dri
->dri_Font
->tf_YSize
+ 1 +
454 ci
->fh
->window
->WScreen
->WBorBottom
) / 2;
456 gad
= CreateContext(&gadlist
);
458 ng
.ng_LeftEdge
= ci
->fh
->window
->WScreen
->WBorLeft
+ BORDER_X
;
459 ng
.ng_TopEdge
= ci
->fh
->window
->WScreen
->WBorTop
+ dri
->dri_Font
->tf_YSize
+ 1 + BORDER_Y
;
460 ng
.ng_Width
= winwidth
- BORDER_X
* 2;
461 ng
.ng_Height
= winheight
- BORDER_Y
* 2 - buttonheight
- LV_BUTTON_SPACING_Y
;
462 ng
.ng_GadgetText
= NULL
;
463 ng
.ng_TextAttr
= NULL
;
464 ng
.ng_GadgetID
= ID_LISTVIEW
;
466 ng
.ng_VisualInfo
= vi
;
470 struct TagItem lvtags
[] =
472 {GTLV_Labels
, (IPTR
)&ci
->matchlist
},
473 {GTLV_ShowSelected
, 0 },
474 {GTLV_Selected
, 0 },
478 gad
= lvgad
= CreateGadgetA(LISTVIEW_KIND
, gad
, &ng
, lvtags
);
480 visible_lv_lines
= (ng
.ng_Height
- LV_EXTRA_HEIGHT
) / (dri
->dri_Font
->tf_YSize
+ 1);
483 ng
.ng_TopEdge
+= ng
.ng_Height
+ LV_BUTTON_SPACING_Y
;
484 ng
.ng_Width
= buttonwidth
;
485 ng
.ng_Height
= buttonheight
;
486 ng
.ng_GadgetText
= oktext
;
487 ng
.ng_GadgetID
= ID_OK
;
489 gad
= CreateGadgetA(BUTTON_KIND
, gad
, &ng
, NULL
);
491 ng
.ng_LeftEdge
+= winwidth
- buttonwidth
- BORDER_X
* 2;
492 ng
.ng_GadgetText
= canceltext
;
493 ng
.ng_GadgetID
= ID_CANCEL
;
495 gad
= CreateGadgetA(BUTTON_KIND
, gad
, &ng
, NULL
);
499 struct TagItem wintags
[] =
501 {WA_CustomScreen
, (IPTR
)ci
->fh
->window
->WScreen
},
502 {WA_Title
, (IPTR
)titletext
},
503 {WA_CloseGadget
, TRUE
},
504 {WA_DragBar
, TRUE
},
505 {WA_DepthGadget
, TRUE
},
506 {WA_Activate
, TRUE
},
507 {WA_AutoAdjust
, TRUE
},
508 {WA_Left
, winleft
},
510 {WA_InnerWidth
, winwidth
},
511 {WA_InnerHeight
, winheight
},
512 {WA_IDCMP
, IDCMP_CLOSEWINDOW
|
517 {WA_Gadgets
, (IPTR
)gadlist
},
522 if ((win
= OpenWindowTagList(NULL
, wintags
)))
527 CurrentTime(&secold
, µold
);
529 while (!done
&& !doit
)
531 struct IntuiMessage
*msg
;
533 WaitPort(win
->UserPort
);
535 while((msg
= GT_GetIMsg(win
->UserPort
)))
539 case IDCMP_CLOSEWINDOW
:
543 case IDCMP_VANILLAKEY
:
560 BOOL extreme
= FALSE
;
577 case RAWKEY_PAGEDOWN
:
592 case RAWKEY_NM_WHEEL_UP
:
596 case RAWKEY_NM_WHEEL_DOWN
:
601 if (msg
->Qualifier
& (IEQUALIFIER_LSHIFT
| IEQUALIFIER_RSHIFT
))
606 if (msg
->Qualifier
& (IEQUALIFIER_LALT
| IEQUALIFIER_RALT
| IEQUALIFIER_CONTROL
))
614 struct TagItem tags
[] =
616 {GTLV_Selected
, (IPTR
)&getsel
},
617 {TAG_IGNORE
, TRUE
},
622 GT_GetGadgetAttrsA(lvgad
, win
, NULL
, tags
);
627 scroll
*= ci
->nummatchnodes
;
631 scroll
*= visible_lv_lines
;
635 if (sel
< 0) sel
= ci
->nummatchnodes
- 1;
636 if (sel
>= ci
->nummatchnodes
) sel
= 0;
638 tags
[0].ti_Data
= (IPTR
)sel
;
639 tags
[1].ti_Tag
= GTLV_MakeVisible
;
640 tags
[1].ti_Data
= (IPTR
)sel
;
642 GT_SetGadgetAttrsA(lvgad
, win
, NULL
, tags
);
648 gad
= (struct Gadget
*)msg
->IAddress
;
650 switch(gad
->GadgetID
)
661 CurrentTime(&sec
, µ
);
662 if (DoubleClick(secold
, microold
, sec
, micro
)) doit
= TRUE
;
663 secold
= sec
; microold
= micro
;
668 } /* switch(msg->Class) */
672 } /* while((msg = GT_GetIMsg(win->UserPort))) */
674 } /* while (!done && !doit) */
681 struct TagItem gettags
[] =
683 {GTLV_Selected
, (IPTR
)&sel
},
687 GT_GetGadgetAttrsA(lvgad
, win
, NULL
, gettags
);
690 ForeachNode(&ci
->matchlist
, node
)
694 AddPart(ci
->match
, node
->ln_Name
, sizeof(ci
->match
));
706 } /* if ((win = OpenWindowTagList(NULL, wintags))) */
710 FreeGadgets(gadlist
);
713 } /* if ((vi = GetVisualInfoA(ci->fh->window->WScreen, NULL))) */
715 FreeScreenDrawInfo(ci
->fh
->window
->WScreen
, dri
);
717 } /* if ((dri = GetScreenDrawInfo(fh->window->WScreen))) */
722 /****************************************************************************************/
724 void Completion(struct filehandle
*fh
)
726 struct completioninfo
*ci
;
728 if ((ci
= InitCompletion(fh
)))
730 PrepareCompletion(fh
, ci
);
732 if (!ci
->dirpart
[0] && !ci
->filepart
[0])
738 if (PreparePattern(fh
, ci
))
740 BOOL doprint
= FALSE
;
744 strncpy(ci
->match
, ci
->dirpart
, sizeof(ci
->match
));
746 if (ci
->nummatchnodes
== 1)
749 ((struct matchnode
*)GetHead(&ci
->matchlist
))->name
,
754 else if (ci
->nummatchnodes
> 1)
756 doprint
= DoChooseReq(fh
, ci
);
764 if (strchr(ci
->match
, ' ')) AddQuotes(ci
, ci
->match
, sizeof(ci
->match
));
766 /* Insert as many backspaces in front of the string,
767 to erase whole "word" first (starting at ci->wordstart)
768 before reprinting expanded filename */
770 backspaces
= ci
->fh
->inputpos
- ci
->wordstart
;
772 memmove(ci
->match
+ backspaces
, ci
->match
, sizeof(ci
->match
) - backspaces
);
773 memset(ci
->match
, 8, backspaces
);
775 c
= ci
->match
[strlen(ci
->match
) - 1];
776 if ((c
!= '/') && (c
!= ':'))
778 strncat(ci
->match
, " ", sizeof(ci
->match
));
781 InsertIntoConBuffer(ci
, ci
->match
);
787 CleanupCompletion(ci
);
789 } /* if ((ci = InitCompletion())) */
793 /****************************************************************************************/