#include <windows.h>
#include <aygshell.h> // link against aygshell.lib
#include <doclist.h> // link against doclist.lib
#include <projects.h> // link against note_prj.lib
#ifndef IDM_NEWMENUMAX
#include <newmenu.h> // under PPC2000, it was already included in aygshell
#endif
#include <commctrl.h>
#include <shellapi.h>
HINSTANCE hInstance;
HWND hmain=0;
HWND hcard=0;
HWND hdlc=0;
HWND hmbar=0;
HWND hed=0;
wchar_t dlcfolder[MAX_PATH]={0}; // the full path of the current DLC folder
SHACTIVATEINFO shai={sizeof(SHACTIVATEINFO),0,0,0,0,0};
bool close_on_ok=false;
bool isalphanum(const wchar_t c)
{ if (c>='a' && c<='z') return true;
if (c>='A' && c<='Z') return true;
if (c>='0' && c<='9') return true;
if (c==' ' || c=='-') return true;
return false;
}
struct TDocument
{ wchar_t fn[MAX_PATH];
bool open, changed, readonly;
bool unicode;
TDocument() : open(false) {*fn=0;}
void New();
void Save();
bool Open(const wchar_t *ofn);
} doc;
void TDocument::New()
{ *fn=0; SetWindowText(hed,L"");
open=true; changed=false; readonly=false; unicode=true;
}
void TDocument::Save()
{ if (!open || !changed) return;
int len=GetWindowTextLength(hed); if (len==0) return;
wchar_t *unc=new wchar_t[len+1];
GetWindowText(hed,unc,len+1);
// We may have to construct the filename from the doc's content...
if (*fn==0)
{ wcscpy(fn,dlcfolder); wchar_t *d=fn+wcslen(fn);
*d='\\'; d++; bool any=false;
int i=0; wchar_t *c=unc;
for (; !isalphanum(*c) && *c!=0 && i<32; i++) {}
for (; isalphanum(c[i]) && c[i]!=0 && i<32; i++) {*d=c[i]; d++; any=true;}
if (!any) {wcscpy(d,L"unnamed"); d+=wcslen(d);}
wcscpy(d,L".txt");
for (i=1; GetFileAttributes(fn)!=0xFFFFFFFF; i++) wsprintf(d,L" (%i).txt",i);
}
// really, if it's being saved on SD, we should use FILE_FLAG_WRITE_THROUGH
// to hopefully prevent data loss due to buggy SD card filesystems.
HANDLE hf = CreateFile(fn,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
if (unicode)
{ const char byteorder[2]={-1,-2};
DWORD writ; WriteFile(hf,byteorder,2,&writ,NULL);
WriteFile(hf,unc,sizeof(wchar_t)*len,&writ,NULL);
}
else
{ int size = WideCharToMultiByte(CP_ACP,WC_DEFAULTCHAR,unc,-1,0,0,NULL,NULL);
char *buf = new char[size];
WideCharToMultiByte(CP_ACP,WC_DEFAULTCHAR,unc,-1,buf,size,NULL,NULL);
DWORD writ; WriteFile(hf,buf,size,&writ,NULL);
delete[] buf;
}
CloseHandle(hf);
delete[] unc;
changed=false;
}
bool TDocument::Open(const wchar_t *ofn)
{ HANDLE hf = CreateFile(ofn,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if (hf==INVALID_HANDLE_VALUE) return false;
DWORD size=GetFileSize(hf,0); char *buf=new char[size+2];
DWORD red; ReadFile(hf,buf,size,&red,NULL); buf[size]=0; buf[size+1]=0;
CloseHandle(hf);
// if there's a BOM we'll know it's unicode. If not, we'll guess, based on
// whether the first 256bytes look ascii-ish or not.
wchar_t *unc=0;
if (buf[0]==-1 && buf[1]==-2) unc=(wchar_t*)(buf+2);
else
{ for (unsigned int i=3; i<256 && i<size && unc==0; i+=2) {if (buf[i]==0) unc=(wchar_t*)buf;}
}
unicode = (unc!=0);
//
if (unicode) SetWindowText(hed,unc);
else
{ unc = new wchar_t[size];
MultiByteToWideChar(CP_ACP,0,buf,-1,unc,size);
SetWindowText(hed,unc);
delete[] unc;
}
wcscpy(fn,ofn);
open=true; changed=false; readonly=true;
return true;
}
// MakeLayout - this shows/hides the edit/doclist as appropriate,
// resizes according to the SIP, and refreshes the DocList.
//
const DWORD mlShowHide=0, mlRefreshDocList=1, mlResize=2;
//
void MakeLayout(DWORD flags, LPARAM wmsc_lparam=0)
{ ShowWindow(hcard, doc.open?SW_SHOW:SW_HIDE);
ShowWindow(hdlc, doc.open?SW_HIDE:SW_SHOW);
// the 'edit' button
TBBUTTONINFO tbbi; ZeroMemory(&tbbi,sizeof(tbbi)); tbbi.cbSize=sizeof(tbbi);
tbbi.dwMask=TBIF_STATE; tbbi.fsState= (doc.open&&doc.readonly)?TBSTATE_ENABLED:TBSTATE_HIDDEN;
SendMessage(hmbar,TB_SETBUTTONINFO,203,(LPARAM)&tbbi);
// readonly in the card itself
SendMessage(hed,EM_SETREADONLY,doc.readonly?TRUE:FALSE,0);
if (doc.open && !doc.readonly) SetFocus(hed);
// OK/X. When we hide the OK, that lets the X underneath show up
SHDoneButton(hmain, doc.open?SHDB_SHOW:SHDB_HIDE);
// Now resize the windows to accommodate SIP. If this is called from
// WM_SETTINGSCHANGE, then use the same lParam for efficency; otherwise 0
if (flags&mlResize)
{ SIPINFO si; SHSipInfo(SPI_GETSIPINFO,wmsc_lparam,&si,0);
int x=si.rcVisibleDesktop.left, y=si.rcVisibleDesktop.top;
int w=si.rcVisibleDesktop.right-x, h=si.rcVisibleDesktop.bottom-y;
RECT rc; GetWindowRect(hmbar,&rc); int mh=rc.bottom-rc.top;
if ((si.fdwFlags&SIPF_ON)) h+=1; else h-=mh-1;
MoveWindow(hmain,x,y,w,h,FALSE);
GetClientRect(hmain,&rc);
MoveWindow(hcard,0,0,rc.right,rc.bottom,FALSE);
MoveWindow(hdlc,0,0,rc.right,rc.bottom,TRUE);
MoveWindow(hed,0,0,rc.right,rc.bottom,FALSE);
}
// We need to refresh after files have been changed, to reshow their time/size
if (!doc.open && (flags&mlRefreshDocList)) DocList_Refresh(hdlc);
}
// GetPathForFolder(const wchar_t *folder, wchar_t *path):
// Given a folder name we return its path (assume path>=MAX_PATH).
// All Folders --> \My Documents
// Business --> \My Documents\Business
// MySdFolder --> \Storage Card\MySdFolder
//
struct EnumProjectsInfo {const wchar_t *folder; wchar_t *path;};
//
BOOL CALLBACK EnumCallback(PAstruct *pa, LPARAM lp)
{ wchar_t *fn;
if (pa->m_IDtype!=FILE_ID_TYPE_OID) fn=pa->m_szPathname;
else
{ CEOIDINFO cinf;
CeOidGetInfo(pa->m_fileOID,&cinf);
fn = cinf.infDirectory.szDirName;
}
const wchar_t *c=fn, *lastslash=c;
while (*c!=0) {if (*c=='\\') lastslash=c+1; c++;}
EnumProjectsInfo *epi = (EnumProjectsInfo*)lp;
if (wcsicmp(epi->folder,lastslash)!=0) return TRUE; // keep enumerating
wcscpy(epi->path,fn); return FALSE;
}
//
void GetPathForFolder(const wchar_t *folder, wchar_t *path)
{ // assume 'path' is at least MAX_PATH.
*path=0;
EnumProjectsInfo epi; epi.folder=folder; epi.path=path;
EnumProjectsEx(EnumCallback,0,PRJ_ENUM_ALL_DEVICES,(LPARAM)&epi);
if (*path==0) SHGetSpecialFolderPath(NULL,epi.path,CSIDL_PERSONAL,FALSE);
}
LRESULT CALLBACK CardWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{ if (msg==WM_COMMAND)
{ int id=LOWORD(wParam), code=HIWORD(wParam);
if (id==104 && code==EN_CHANGE) doc.changed=true;
}
return DefWindowProc(hwnd,msg,wParam,lParam);
}
LRESULT CALLBACK MainWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
switch (msg)
{ case WM_CREATE:
{ SHMENUBARINFO mbi; ZeroMemory(&mbi,sizeof(mbi));
mbi.cbSize=sizeof(mbi);
mbi.hwndParent = hwnd;
mbi.nToolBarId = 101;
mbi.hInstRes = hInstance;
SHCreateMenuBar(&mbi);
hmbar=mbi.hwndMB;
SHGetSpecialFolderPath(hwnd,dlcfolder,CSIDL_PERSONAL,TRUE);
DOCLISTCREATE dlc; ZeroMemory(&dlc,sizeof(dlc));
dlc.dwStructSize=sizeof(dlc);
dlc.hwndParent=hwnd;
dlc.pstrFilter = L"Text\0*.txt\0";
dlc.wId=102;
hdlc = DocList_Create(&dlc);
hcard=CreateWindow(L"LuCardClass",L"",WS_CHILD,0,0,0,0,hwnd,(HMENU)103,hInstance,0);
hed = CreateWindow(L"CAPEDIT",L"",WS_CHILD|WS_VISIBLE|WS_HSCROLL|WS_VSCROLL|ES_AUTOHSCROLL|ES_AUTOVSCROLL|ES_MULTILINE|WS_TABSTOP,0,0,0,0,hcard,(HMENU)104,hInstance,0);
CreateWindow(WC_SIPPREF,L"",WS_CHILD,0,0,0,0,hcard,(HMENU)105,hInstance,0);
MakeLayout(mlShowHide|mlRefreshDocList|mlResize,0);
int nitems=DocList_GetItemCount(hdlc);
if (nitems==0) SendMessage(hwnd,WM_COMMAND,IDM_NEWMENUMAX+1,0);
} return 0;
case WM_COMMAND:
{ int id=LOWORD(wParam);
if (id==IDOK && doc.open) // OK button
{ if (doc.open && doc.changed) doc.Save();
SHSipPreference(hmain,SIP_FORCEDOWN);
doc.open=false; MakeLayout(mlShowHide|mlRefreshDocList);
if (close_on_ok) ShowWindow(hwnd,SW_MINIMIZE);
}
if (id==301) // tools.exit
{ DestroyWindow(hwnd);
}
else if (id==203) // tools.edit
{ if (doc.open && doc.readonly) {doc.readonly=false; MakeLayout(mlShowHide);}
}
else if (id==IDM_NEWMENUMAX+1) // new.text
{ if (doc.open && doc.changed) doc.Save();
doc.New(); MakeLayout(mlShowHide);
}
} return 0;
case WM_ACTIVATE:
{ SHHandleWMActivate(hwnd, wParam, lParam, &shai, FALSE);
} return 0;
case WM_SETTINGCHANGE:
{ SHHandleWMSettingChange(hwnd, wParam, lParam, &shai);
MakeLayout(mlResize,lParam);
} return 0;
case WM_HIBERNATE:
{ if (!doc.open) SetWindowText(hed,L"");
} return 0;
case WM_DESTROY:
{ if (doc.open && doc.changed) doc.Save();
CommandBar_Destroy(hmbar);
PostQuitMessage(0);
} return 0;
case WM_APP:
{ // this messages gets sent to activate a previous instance
close_on_ok=false;
// maybe have to refresh the doclist.
if (!doc.open) MakeLayout(mlRefreshDocList);
} return 0;
case WM_COPYDATA:
{ // this message gets sent to open a document.
close_on_ok=true;
// it might be sent by a new process to an existing one - hence
// the need for WM_COPYDATA to share data between processes.
if (doc.open && doc.changed) doc.Save();
COPYDATASTRUCT *cs = (COPYDATASTRUCT*)lParam;
doc.Open((wchar_t*)cs->lpData);
MakeLayout(mlShowHide);
} return 0;
case WM_NOTIFY:
{ NMHDR *nmhdr = (NMHDR*)lParam;
// This first is called by the menubar for the "shared new"
if (nmhdr->code==NMN_GETAPPREGKEY)
{ NMNEWMENU *nmnew = (NMNEWMENU*)lParam;
AppendMenu(nmnew->hMenu,MF_ENABLED,IDM_NEWMENUMAX+1,L"Text");
AppendMenu (nmnew->hMenu, MF_SEPARATOR, 0, 0);
}
else if (nmhdr->code==DLN_FOLDER)
{ DLNHDR *dln = (DLNHDR*)lParam;
GetPathForFolder(dln->pszPath,dlcfolder);
}
else if (nmhdr->code==DLN_ITEMACTIVATED)
{ DLNHDR *dln = (DLNHDR*)lParam;
doc.Open(dln->pszPath);
MakeLayout(mlShowHide);
}
} return 0;
}
return DefWindowProc(hwnd,msg,wParam,lParam);
}
int WINAPI WinMain(HINSTANCE h, HINSTANCE, LPTSTR arg, int nCmdShow)
{ hInstance=h;
hmain = FindWindow(L"LuMainClass",L"My App");
if (hmain!=0)
{ SetForegroundWindow((HWND)((ULONG)hmain|1)); // the |1 will activate any owned windows
if (arg!=0 && wcslen(arg)!=0)
{ COPYDATASTRUCT cs; cs.dwData=0; cs.cbData=2+2*wcslen(arg); cs.lpData=arg;
SendMessage(hmain,WM_COPYDATA,NULL,(LPARAM)&cs);
}
else SendMessage(hmain,WM_APP,0,0);
return 0;
}
WNDCLASS wc; ZeroMemory(&wc,sizeof(wc));
wc.lpfnWndProc = MainWndProc;
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
wc.lpszClassName= L"LuMainClass";
RegisterClass(&wc);
//
wc.lpfnWndProc = CardWndProc;
wc.lpszClassName = L"LuCardClass";
RegisterClass(&wc);
//
SHInitExtraControls();
hmain = CreateWindow(L"LuMainClass", L"My App", WS_VISIBLE,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
ShowWindow(hmain,nCmdShow);
UpdateWindow(hmain);
if (arg!=0 && wcslen(arg)!=0)
{ COPYDATASTRUCT cs; cs.dwData=0; cs.cbData=2+2*wcslen(arg); cs.lpData=arg;
SendMessage(hmain,WM_COPYDATA,NULL,(LPARAM)&cs);
}
MSG msg;
while (GetMessage(&msg,NULL,0,0))
{ TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}