// EditCase is Far Manager plugin. It allows to change the case of selected text // or current (nearest) word in the internal editor. // This plugin can change case to: lower case, Title Case, UPPER CASE and tOGGLE cASE // Besides, it has ability of cyclic case change like MS Word by ShiftF3 #include #include #include #include "EditLng.hpp" #include "version.hpp" #include #include "guid.hpp" #if defined(__GNUC__) || defined (_MSC_VER) #include #undef MAXINT #define MAXINT INT_MAX #else #include //MAXINT #endif #if defined(__GNUC__) #ifdef __cplusplus extern "C" { #endif BOOL WINAPI DllMainCRTStartup(HANDLE hDll,DWORD dwReason,LPVOID lpReserved); #ifdef __cplusplus }; #endif BOOL WINAPI DllMainCRTStartup(HANDLE hDll,DWORD dwReason,LPVOID lpReserved) { (void) lpReserved; (void) dwReason; (void) hDll; return TRUE; } #endif static struct PluginStartupInfo Info; static FARSTANDARDFUNCTIONS FSF; // Menu item numbers enum ENUMCCTYPES { CCLower, CCTitle, CCUpper, CCToggle, CCCyclic, }; // This chars aren't letters static wchar_t* WordDiv; static int WordDivLen; const wchar_t *GetMsg(int MsgId); bool FindBounds(wchar_t *Str, int Len, int Pos, int &Start, int &End); int FindEnd(wchar_t *Str, int Len, int Pos); int FindStart(wchar_t *Str, int Len, int Pos); bool MyIsAlpha(int c); int GetNextCCType(wchar_t *Str, int StrLen, int Start, int End); int ChangeCase(wchar_t *NewString, int Start, int End, int CCType); void WINAPI GetGlobalInfoW(struct GlobalInfo *Info) { Info->StructSize=sizeof(GlobalInfo); Info->MinFarVersion=FARMANAGERVERSION; Info->Version=PLUGIN_VERSION; Info->Guid=MainGuid; Info->Title=PLUGIN_NAME; Info->Description=PLUGIN_DESC; Info->Author=PLUGIN_AUTHOR; } void WINAPI SetStartupInfoW(const struct PluginStartupInfo *Info) { ::Info=*Info; ::FSF=*Info->FSF; ::Info.FSF=&::FSF; PluginSettings plugSettings(MainGuid, ::Info.SettingsControl); const wchar_t* AddWordDiv=plugSettings.Get(0,L"AddWordDiv",L"#"); FarSettingsCreate settings={sizeof(FarSettingsCreate),FarGuid,INVALID_HANDLE_VALUE}; HANDLE Settings=::Info.SettingsControl(INVALID_HANDLE_VALUE,SCTL_CREATE,0,&settings)?settings.Handle:0; if(Settings) { FarSettingsItem item={FSSF_EDITOR,L"WordDiv",FST_UNKNOWN,{0}}; if(::Info.SettingsControl(Settings,SCTL_GET,0,&item)&&FST_STRING==item.Type) { WordDivLen=lstrlen(item.String)+lstrlen(AddWordDiv)+ARRAYSIZE(L" \n\r\t"); WordDiv=(wchar_t*)malloc((WordDivLen+1)*sizeof(wchar_t)); if(WordDiv) { lstrcpy(WordDiv,item.String); lstrcat(WordDiv, AddWordDiv); lstrcat(WordDiv, L" \n\r\t"); } } ::Info.SettingsControl(Settings,SCTL_FREE,0,0); } } HANDLE WINAPI OpenW(const struct OpenInfo *OInfo) { int MenuCode=-1; if (OInfo->OpenFrom==OPEN_FROMMACRO) { OpenMacroInfo* mi=(OpenMacroInfo*)OInfo->Data; if (mi->Count) { if (FMVT_INTEGER == mi->Values[0].Type || FMVT_UNKNOWN == mi->Values[0].Type) { MenuCode=(int)mi->Values[0].Integer; if (MenuCode < 0 || MenuCode > 4) return nullptr; } else // other var type ==> $Recycle.Bin return nullptr; } } if (MenuCode == -1) { size_t i; struct FarMenuItem MenuItems[5] = {}, *MenuItem; int Msgs[]={MCaseLower, MCaseTitle, MCaseUpper, MCaseToggle, MCaseCyclic}; for (MenuItem=MenuItems,i=0; i < ARRAYSIZE(MenuItems); ++i, ++MenuItem) { MenuItem->Text = GetMsg(Msgs[i]); // Text in menu } // First item is selected MenuItems[0].Flags=MIF_SELECTED; // Show menu MenuCode=Info.Menu(&MainGuid, nullptr,-1,-1,0,FMENU_AUTOHIGHLIGHT|FMENU_WRAPMODE, GetMsg(MCaseConversion),NULL,L"Contents",NULL,NULL, MenuItems,ARRAYSIZE(MenuItems)); } switch (MenuCode) { // If menu Escaped case -1: break; default: EditorInfo ei; Info.EditorControl(-1,ECTL_GETINFO,0,&ei); // Current line number int CurLine=ei.CurLine; // Is anything selected bool IsBlock=false; // Nothing selected? if (ei.BlockType!=BTYPE_NONE) { IsBlock=true; CurLine=ei.BlockStartLine; } // Type of Case Change int CCType=MenuCode; // Temporary string wchar_t *NewString=0; // Forever :-) (Line processing loop) for (;;) { if (IsBlock) { if (CurLine >= ei.TotalLines) break; struct EditorSetPosition esp = {CurLine++,-1,-1,-1,-1,-1}; Info.EditorControl(-1,ECTL_SETPOSITION,0,&esp); } struct EditorGetString egs; egs.StringNumber=-1; // If can't get line if (!Info.EditorControl(-1,ECTL_GETSTRING,0,&egs)) break; // Exit // If last selected line was processed or // nothing selected and line is empty if ((IsBlock && egs.SelStart==-1) || (!IsBlock && egs.StringLength<=0)) break; // Exit // If something selected, but line is empty if (egs.StringLength<=0) continue; // Get next line // If whole line (with EOL) is selected if (egs.SelEnd==-1 || egs.SelEnd>egs.StringLength) { egs.SelEnd=egs.StringLength; if (egs.SelEndStructSize=sizeof(*Info); Info->Flags=PF_EDITOR|PF_DISABLEPANELS; static const wchar_t *PluginMenuStrings[1]; PluginMenuStrings[0]=GetMsg(MCaseConversion); Info->PluginMenu.Guids=&MenuGuid; Info->PluginMenu.Strings=PluginMenuStrings; Info->PluginMenu.Count=ARRAYSIZE(PluginMenuStrings); } void WINAPI ExitFARW(const struct ExitInfo *Info) { free(WordDiv); } const wchar_t *GetMsg(int MsgId) { return Info.GetMsg(&MainGuid,MsgId); } // What we consider as letter bool MyIsAlpha(int c) { return (WordDiv&&wmemchr(WordDiv, c, WordDivLen)==NULL) ? true : false; } // Finding word bounds (what'll be converted) (Str is in OEM) bool FindBounds(wchar_t *Str, int Len, int Pos, int &Start, int &End) { int i=1; bool ret = false; // If line isn't empty if (Len>Start) { End=Min(End,Len); // Pos between [Start, End] ? Pos=Max(Pos,Start); Pos=Min(End,Pos); // If current character is non letter if (!MyIsAlpha(Str[Pos])) { // Looking for letter on the left and counting radius while ((Start<=Pos-i) && (!MyIsAlpha(Str[Pos-i]))) i++; // Radius int r=MAXINT; // Letter was found on the left if (Start<=Pos-i) r=i; // Storing radius i=1; // Looking for letter on the right and counting radius while ((Pos+i<=End) && (!MyIsAlpha(Str[Pos+i]))) i++; // Letter was not found if (Pos+i>End) i=MAXINT; // Here r is left radius and i is right radius // If no letters was found if (Min(r,i)!=MAXINT) { // What radius is less? Left? if (r <= i) { End=Pos-r+1; Start=FindStart(Str, Start, End); } else // Right! { Start=Pos+i; End=FindEnd(Str, Start, End); } ret=true; } } else // Current character is letter! { Start=FindStart(Str, Start, Pos); End=FindEnd(Str, Pos, End); ret=true; } } if (!ret) Start=End=-1; return ret; } int FindStart(wchar_t *Str, int Start, int End) { // Current pos in Str int CurPos=End-1; // While current character is letter while (CurPos>=Start && MyIsAlpha(Str[CurPos])) CurPos--; // Moving to left return CurPos+1; } int FindEnd(wchar_t *Str, int Start, int End) { // Current pos in Str int CurPos=Start; // While current character is letter while (CurPosTitle->UPPER // If Str contains no letters, then return CCCyclic int GetNextCCType(wchar_t *Str, int StrLen, int Start, int End) { int SignalWordStart=Start, SignalWordEnd=End; int SignalWordLen=Max(Start,End); // Default conversion is to lower case int CCType=CCLower; Start=Min(Start,End); End=SignalWordLen; if (StrLen