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
;
141 strncpy(ci
->dirpart
, &ci
->fh
->inputbuffer
[ci
->wordstart
], ci
->fh
->inputpos
- ci
->wordstart
);
142 strcpy(ci
->filepart
, FilePart(ci
->dirpart
));
144 *(PathPart(ci
->dirpart
)) = '\0';
146 ci
->wordquoted
= in_quotes
;
148 D(bug("PrepareCompletion: dirpart = \"%s\" filepart = \"%s\"\n", ci
->dirpart
, ci
->filepart
));
151 /****************************************************************************************/
153 static void AddQuotes(struct completioninfo
*ci
, STRPTR s
, LONG s_size
)
155 LONG len
= strlen(s
);
159 if (len
< s_size
- 1)
160 memmove(s
+ 1, s
, len
+ 1);
168 if (len
< s_size
- 3)
170 if ((s
[len
] != '/') && (s
[len
] != ':'))
178 /****************************************************************************************/
180 static void InsertIntoConBuffer(struct completioninfo
*ci
, STRPTR s
)
182 WORD i
= ci
->fh
->conbufferpos
;
184 /* Lame code, but speed is no issue here */
185 while ((ci
->fh
->conbuffersize
< CONSOLEBUFFER_SIZE
) && *s
)
187 memmove(&ci
->fh
->consolebuffer
[i
+ 1], &ci
->fh
->consolebuffer
[i
], ci
->fh
->conbuffersize
- i
);
189 ci
->fh
->consolebuffer
[i
++] = *s
++;
190 ci
->fh
->conbuffersize
++;
194 /****************************************************************************************/
196 static void DoFileReq(struct filehandle
*fh
, struct completioninfo
*ci
)
198 struct Library
*AslBase
;
201 if ((lock
= DupLock(((struct Process
*) ci
->fh
->lastwritetask
)->pr_CurrentDir
)))
203 olddir
= CurrentDir(lock
);
205 if ((AslBase
= OpenLibrary("asl.library", 36)))
207 struct FileRequester
*fr
;
208 struct TagItem tags
[] =
210 { ASLFR_Window
, (IPTR
) ci
->fh
->window
},
211 { ASLFR_DoPatterns
, (IPTR
) TRUE
},
212 { ASLFR_InitialPattern
, 0 },
215 tags
[2].ti_Data
= (IPTR
) (ci
->withinfo
? "#?" : "~(#?.info)");
217 if ((fr
= AllocAslRequest(ASL_FileRequest
, tags
)))
219 if (AslRequest(fr
, NULL
))
223 strcpy(ci
->match
, fr
->fr_Drawer
);
224 AddPart(ci
->match
, fr
->fr_File
, sizeof(ci
->match
));
228 if (strchr(ci
->match
, ' '))
229 AddQuotes(ci
, ci
->match
, sizeof(ci
->match
));
231 c
= ci
->match
[strlen(ci
->match
) - 1];
232 if ((c
!= '/') && (c
!= ':'))
234 strncat(ci
->match
, " ", sizeof(ci
->match
));
237 InsertIntoConBuffer(ci
, ci
->match
);
244 CloseLibrary(AslBase
);
246 } /* if ((AslBase = OpenLibrary("asl.library", 36))) */
253 /****************************************************************************************/
255 static BOOL
PreparePattern(struct filehandle
*fh
, struct completioninfo
*ci
)
259 parsecode
= ParsePatternNoCase(ci
->filepart
, ci
->pattern
, sizeof(ci
->pattern
));
266 strncat(ci
->filepart
, "#?", sizeof(ci
->filepart
));
270 strncat(ci
->filepart
, "~(#?.info)", sizeof(ci
->filepart
));
272 parsecode
= ParsePatternNoCase(ci
->filepart
, ci
->pattern
, sizeof(ci
->pattern
));
276 return (parsecode
== 1);
280 /****************************************************************************************/
282 static void AddMatchNode(struct filehandle
*fh
, struct completioninfo
*ci
, STRPTR name
, WORD type
)
284 struct matchnode
*mn
;
285 struct Node
*prev
, *check
;
289 size
= strlen(name
) + 1 + sizeof(struct matchnode
) + ((type
> 0) ? 1 : 0);
291 if ((mn
= AllocPooled(ci
->pool
, size
)))
293 strcpy(mn
->name
, name
);
294 mn
->node
.ln_Name
= mn
->name
;
297 strcat(mn
->name
, "/");
299 strcat(mn
->name
, ":");
301 /* Sort into matchlist */
304 ForeachNode(&ci
->matchlist
, check
)
308 match
= Stricmp(mn
->name
, ((struct matchnode
*) check
)->name
);
322 Insert(&ci
->matchlist
, (struct Node
*) mn
, prev
);
327 FreePooled(ci
->pool
, mn
, size
);
332 /****************************************************************************************/
334 static void ScanDir(struct filehandle
*fh
, struct completioninfo
*ci
)
336 struct FileInfoBlock
*fib
;
339 if ((fib
= AllocDosObject(DOS_FIB
, 0)))
341 if ((lock
= Lock(ci
->dirpart
, SHARED_LOCK
)))
343 if (Examine(lock
, fib
))
345 while (ExNext(lock
, fib
))
347 if (MatchPatternNoCase(ci
->pattern
, fib
->fib_FileName
))
349 BOOL isdir
= (fib
->fib_DirEntryType
> 0);
351 AddMatchNode(fh
, ci
, fib
->fib_FileName
, (isdir
? 1 : 0));
359 FreeDosObject(DOS_FIB
, fib
);
363 /****************************************************************************************/
365 static void ScanVol(struct filehandle
*fh
, struct completioninfo
*ci
)
367 struct DosList
*dlist
;
369 dlist
= LockDosList(LDF_READ
| LDF_VOLUMES
| LDF_DEVICES
| LDF_ASSIGNS
);
371 while ((dlist
= NextDosEntry(dlist
, LDF_VOLUMES
| LDF_ASSIGNS
| LDF_DEVICES
)) != NULL
)
373 STRPTR devname
= AROS_BSTR_ADDR(dlist
->dol_Name
);
375 if (MatchPatternNoCase(ci
->pattern
, devname
))
377 AddMatchNode(fh
, ci
, devname
, 2);
381 UnLockDosList(LDF_READ
| LDF_VOLUMES
| LDF_DEVICES
| LDF_ASSIGNS
);
384 static void DoScan(struct filehandle
*fh
, struct completioninfo
*ci
)
388 if ((lock
= DupLock(((struct Process
*) ci
->fh
->lastwritetask
)->pr_CurrentDir
)))
390 olddir
= CurrentDir(lock
);
392 if (ci
->dirpart
[0] == 0)
401 /****************************************************************************************/
403 #define BUTTON_EXTRA_WIDTH 16
404 #define BUTTON_EXTRA_HEIGHT 6
407 #define BUTTON_SPACING_X 8
408 #define LV_BUTTON_SPACING_Y 4
409 #define LV_EXTRA_HEIGHT 4
411 #define ID_LISTVIEW 1
415 /****************************************************************************************/
417 static BOOL
DoChooseReq(struct filehandle
*fh
, struct completioninfo
*ci
)
419 static const char oktext
[] = "Ok";
420 static const char canceltext
[] = "Cancel";
421 static const char titletext
[] = "Select filename";
423 struct RastPort temprp
;
424 struct DrawInfo
*dri
;
426 struct Gadget
*gadlist
, *gad
, *lvgad
;
429 WORD i
, buttonwidth
, buttonheight
, visible_lv_lines
;
430 WORD winwidth
, winheight
, winleft
, wintop
;
431 LONG sec
, micro
, secold
, microold
;
434 if ((dri
= GetScreenDrawInfo(ci
->fh
->window
->WScreen
)))
436 if ((vi
= GetVisualInfoA(ci
->fh
->window
->WScreen
, NULL
)))
438 InitRastPort(&temprp
);
439 SetFont(&temprp
, dri
->dri_Font
);
441 buttonwidth
= TextLength(&temprp
, oktext
, strlen(oktext
));
442 i
= TextLength(&temprp
, canceltext
, strlen(canceltext
));
443 buttonwidth
= (buttonwidth
> i
) ? buttonwidth
: i
;
444 buttonwidth
+= BUTTON_EXTRA_WIDTH
;
446 buttonheight
= dri
->dri_Font
->tf_YSize
+ BUTTON_EXTRA_HEIGHT
;
448 i
= ci
->nummatchnodes
> 15 ? 15 : ci
->nummatchnodes
;
452 winheight
= i
* (dri
->dri_Font
->tf_YSize
+ 1) + LV_EXTRA_HEIGHT
+ LV_BUTTON_SPACING_Y
+ buttonheight
455 winwidth
= buttonwidth
* 2 + BUTTON_SPACING_X
+ BORDER_X
* 2;
456 i
= ci
->fh
->window
->WScreen
->Width
* 1 / 3;
460 winleft
= ci
->fh
->window
->WScreen
->MouseX
461 - (winwidth
+ ci
->fh
->window
->WScreen
->WBorLeft
+ ci
->fh
->window
->WScreen
->WBorRight
) / 2;
462 wintop
= ci
->fh
->window
->WScreen
->MouseY
463 - (winheight
+ ci
->fh
->window
->WScreen
->WBorTop
+ dri
->dri_Font
->tf_YSize
+ 1
464 + ci
->fh
->window
->WScreen
->WBorBottom
) / 2;
466 gad
= CreateContext(&gadlist
);
468 ng
.ng_LeftEdge
= ci
->fh
->window
->WScreen
->WBorLeft
+ BORDER_X
;
469 ng
.ng_TopEdge
= ci
->fh
->window
->WScreen
->WBorTop
+ dri
->dri_Font
->tf_YSize
+ 1 + BORDER_Y
;
470 ng
.ng_Width
= winwidth
- BORDER_X
* 2;
471 ng
.ng_Height
= winheight
- BORDER_Y
* 2 - buttonheight
- LV_BUTTON_SPACING_Y
;
472 ng
.ng_GadgetText
= NULL
;
473 ng
.ng_TextAttr
= NULL
;
474 ng
.ng_GadgetID
= ID_LISTVIEW
;
476 ng
.ng_VisualInfo
= vi
;
480 struct TagItem lvtags
[] =
482 { GTLV_Labels
, (IPTR
) &ci
->matchlist
},
483 { GTLV_ShowSelected
, 0 },
484 { GTLV_Selected
, 0 },
487 gad
= lvgad
= CreateGadgetA(LISTVIEW_KIND
, gad
, &ng
, lvtags
);
489 visible_lv_lines
= (ng
.ng_Height
- LV_EXTRA_HEIGHT
) / (dri
->dri_Font
->tf_YSize
+ 1);
492 ng
.ng_TopEdge
+= ng
.ng_Height
+ LV_BUTTON_SPACING_Y
;
493 ng
.ng_Width
= buttonwidth
;
494 ng
.ng_Height
= buttonheight
;
495 ng
.ng_GadgetText
= oktext
;
496 ng
.ng_GadgetID
= ID_OK
;
498 gad
= CreateGadgetA(BUTTON_KIND
, gad
, &ng
, NULL
);
500 ng
.ng_LeftEdge
+= winwidth
- buttonwidth
- BORDER_X
* 2;
501 ng
.ng_GadgetText
= canceltext
;
502 ng
.ng_GadgetID
= ID_CANCEL
;
504 gad
= CreateGadgetA(BUTTON_KIND
, gad
, &ng
, NULL
);
508 struct TagItem wintags
[] =
510 { WA_CustomScreen
, (IPTR
) ci
->fh
->window
->WScreen
},
511 { WA_Title
, (IPTR
) titletext
},
512 { WA_CloseGadget
, TRUE
},
513 { WA_DragBar
, TRUE
},
514 { WA_DepthGadget
, TRUE
},
515 { WA_Activate
, TRUE
},
516 { WA_AutoAdjust
, TRUE
},
517 { WA_Left
, winleft
},
519 { WA_InnerWidth
, winwidth
},
520 { WA_InnerHeight
, winheight
},
521 { WA_IDCMP
, IDCMP_CLOSEWINDOW
| IDCMP_RAWKEY
| IDCMP_VANILLAKEY
| LISTVIEWIDCMP
| BUTTONIDCMP
},
522 { WA_Gadgets
, (IPTR
) gadlist
},
526 if ((win
= OpenWindowTagList(NULL
, wintags
)))
531 CurrentTime(&secold
, µold
);
533 while (!done
&& !doit
)
535 struct IntuiMessage
*msg
;
537 WaitPort(win
->UserPort
);
539 while ((msg
= GT_GetIMsg(win
->UserPort
)))
543 case IDCMP_CLOSEWINDOW
:
547 case IDCMP_VANILLAKEY
:
564 BOOL extreme
= FALSE
;
581 case RAWKEY_PAGEDOWN
:
596 case RAWKEY_NM_WHEEL_UP
:
600 case RAWKEY_NM_WHEEL_DOWN
:
605 if (msg
->Qualifier
& (IEQUALIFIER_LSHIFT
| IEQUALIFIER_RSHIFT
))
610 if (msg
->Qualifier
& (IEQUALIFIER_LALT
| IEQUALIFIER_RALT
| IEQUALIFIER_CONTROL
))
618 struct TagItem tags
[] =
620 { GTLV_Selected
, (IPTR
) &getsel
},
621 { TAG_IGNORE
, TRUE
},
625 GT_GetGadgetAttrsA(lvgad
, win
, NULL
, tags
);
630 scroll
*= ci
->nummatchnodes
;
634 scroll
*= visible_lv_lines
;
639 sel
= ci
->nummatchnodes
- 1;
640 if (sel
>= ci
->nummatchnodes
)
643 tags
[0].ti_Data
= (IPTR
) sel
;
644 tags
[1].ti_Tag
= GTLV_MakeVisible
;
645 tags
[1].ti_Data
= (IPTR
) sel
;
647 GT_SetGadgetAttrsA(lvgad
, win
, NULL
, tags
);
653 gad
= (struct Gadget
*) msg
->IAddress
;
655 switch (gad
->GadgetID
)
666 CurrentTime(&sec
, µ
);
667 if (DoubleClick(secold
, microold
, sec
, micro
))
675 } /* switch(msg->Class) */
679 } /* while((msg = GT_GetIMsg(win->UserPort))) */
681 } /* while (!done && !doit) */
688 struct TagItem gettags
[] =
690 { GTLV_Selected
, (IPTR
) &sel
},
693 GT_GetGadgetAttrsA(lvgad
, win
, NULL
, gettags
);
696 ForeachNode(&ci
->matchlist
, node
)
700 AddPart(ci
->match
, node
->ln_Name
, sizeof(ci
->match
));
712 } /* if ((win = OpenWindowTagList(NULL, wintags))) */
716 FreeGadgets(gadlist
);
719 } /* if ((vi = GetVisualInfoA(ci->fh->window->WScreen, NULL))) */
721 FreeScreenDrawInfo(ci
->fh
->window
->WScreen
, dri
);
723 } /* if ((dri = GetScreenDrawInfo(fh->window->WScreen))) */
728 /****************************************************************************************/
730 void Completion(struct filehandle
*fh
, BOOL withinfo
)
732 struct completioninfo
*ci
;
734 if ((ci
= InitCompletion(fh
, withinfo
)))
736 PrepareCompletion(fh
, ci
);
738 if (!ci
->dirpart
[0] && !ci
->filepart
[0])
744 if (PreparePattern(fh
, ci
))
746 BOOL doprint
= FALSE
;
750 strncpy(ci
->match
, ci
->dirpart
, sizeof(ci
->match
));
752 if (ci
->nummatchnodes
== 1)
754 AddPart(ci
->match
, ((struct matchnode
*) GetHead(&ci
->matchlist
))->name
, sizeof(ci
->match
));
758 else if (ci
->nummatchnodes
> 1)
760 doprint
= DoChooseReq(fh
, ci
);
768 if (strchr(ci
->match
, ' '))
769 AddQuotes(ci
, ci
->match
, sizeof(ci
->match
));
771 /* Insert as many backspaces in front of the string,
772 to erase whole "word" first (starting at ci->wordstart)
773 before reprinting expanded filename */
775 backspaces
= ci
->fh
->inputpos
- ci
->wordstart
;
777 memmove(ci
->match
+ backspaces
, ci
->match
, sizeof(ci
->match
) - backspaces
);
778 memset(ci
->match
, 8, backspaces
);
780 c
= ci
->match
[strlen(ci
->match
) - 1];
781 if ((c
!= '/') && (c
!= ':'))
783 strncat(ci
->match
, " ", sizeof(ci
->match
));
786 InsertIntoConBuffer(ci
, ci
->match
);
792 CleanupCompletion(ci
);
794 } /* if ((ci = InitCompletion())) */
798 /****************************************************************************************/