Introduction
The DocList
is what you see when you launch Pocket Word or Pocket Excel or the other standard PocketPC apps. It shows a list of files, and the user can choose which folder to view via the drop-down in the top left. It's the PocketPC's answer to FileOpen.
This article shows how to customize the folder list. Here, on a whim, I made it show only those folders that have an "L" in their names, and hides the "All Folder" and "Add/Remove Folder" options. More seriously, for an app that always saves documents on SD card, you could limit it to showing SD-card folders. Or remove the silly 'Templates' and 'Annotations' folders from the list.
Background
The DocList
control has some bugs in it. First, although it shows folders that exist on every storage card as well as under My Documents, it doesn't let the user select folders from anything beyond the first storage card. This is problematic on systems such as the Mitac, who consider their built-in backup flash disk to be primary and the SD card to be secondary. Doh! To be friendly to the user, you might hide all folders that are on secondary storage cards.
Second, if there is a folder of the same name in more than one location (e.g. \My Documents\Lu as well as Storage Card\Lu) then the drop-down folder list will merge the two together. But if one happens to be written with different capitalization, then they two are merged for selection purposes but are left unmerged for display purposes.
Preliminaries
This code uses the Standard Template Library (STL). Everyone should use the STL. Download the STL, ported to eVC++ by Giuseppe Govi. Unzip it into a subdirectory called stl_eVC of your project. Next, under Project > Settings > Compiler > Preprocessor, add the directory stl_eVC to the list of search paths.
The following shows which libraries to include. I have disabled warning 4018 within the STL, because it is spurious. Also, under Project > Settings > Linker > Input, link with doclist.lib and note_prj.lib. The first is for the DocList
control, and the second is for enumerating folders.
#include <windows.h>
#include <doclist.h>
#include <projects.h>
#pragma warning( push )
#pragma warning( disable: 4018 )
#include <string>
#include <list>
#pragma warning( pop )
using namespace std;
HINSTANCE hInstance;
HWND hdlc=0;
The global variable hdlc
is the DocList
window.
Building a fake folder-list
The first problem is, although we can manipulate the DocList
's drop-down folder list, we can't retrieve the names of the items. Our solution is to build our own "fake" list of what we reckon the DocList
will end up containing, and in the same order. That way we won't have to manipulate the DocList
blind.
struct TProjectInfo
{ wstring fn; bool valid; int id;
bool operator<(const TProjectInfo &b) const
{ return _wcsicmp(fn.c_str(),b.fn.c_str())<0;
}
};
list<TProjectInfo> dlcprojects;
The global variable dlcprojects
maintains our fake list. Note that DocList
sorts folders insensitive to case; we must do the same, hence the comparison operator. The variable id
will be used later when we subclass the DocList
window...
The following function populates the list. (It also returns the name of a valid entry in the list.)
wstring BuildProjectsList()
{ wstring validfn;
dlcprojects.clear();
EnumProjectsEx(EnumCallback,0,PRJ_ENUM_ALL_DEVICES,0);
dlcprojects.sort();
TProjectInfo pi; pi.valid=false;
pi.fn=L"All Folders"; dlcprojects.push_front(pi);
pi.fn=L"---"; dlcprojects.push_back(pi);
pi.fn=L"Add/Delete..."; dlcprojects.push_back(pi);
for (list<TProjectInfo>::iterator i=dlcprojects.begin();
i!=dlcprojects.end(); i++)
{ list<TProjectInfo>::iterator j=i; j++;
while (j!=dlcprojects.end() && (*i).fn==(*j).fn)
{ if ((*j).valid && !(*i).valid) *i=*j;
j=dlcprojects.erase(j);
}
if (validfn==L"" && (*i).valid) validfn=(*i).fn;
}
return validfn;
}
Note the function EnumProjectsEx
. In PocketPC-speak, a "project" is any immediate (non-nested) subfolder of My Documents, or any immediate subfolder of any storage card. It uses the following callback:
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;
}
TProjectInfo pi;
wchar_t *c=fn, *lastslash=c;
while (*c!=0) {if (*c=='\\') lastslash=c+1; c++;}
pi.fn = lastslash;
pi.valid = (wcschr(lastslash,'l')!=0);
pi.valid |= (wcschr(lastslash,'L')!=0);
dlcprojects.push_back(pi);
return TRUE;
}
Subclassing the DocList
Next, we will subclass the DocList
control. We will intercept it's Menu
event, and change the menu as we see fit. We use a cunning ploy to hide the ones we don't want: namely, we intercept WM_MEASUREITEM
and set their height to zero! Here is the subclass procedure:
WNDPROC OldDLCProc=0;
LRESULT CALLBACK NewDLCProc(HWND hs,UINT msg,
WPARAM wParam,LPARAM lParam)
{ if (msg==WM_INITMENUPOPUP)
{ BuildProjectsList(); LRESULT ret = CallWindowProc(OldDLCProc,hs,msg,
wParam,lParam);
HMENU hm = (HMENU)wParam; wstring ws;
list<TProjectInfo>::iterator it=dlcprojects.begin();
for (unsigned int pos=0; ; pos++,it++)
{ MENUITEMINFO minf; ZeroMemory(&minf,sizeof(minf));
minf.cbSize=sizeof(minf); minf.fMask=MIIM_ID;
BOOL res=GetMenuItemInfo(hm,pos,TRUE,&minf);
if (!res) break;
(*it).id=minf.wID;
}
return ret;
}
else if (msg==WM_MEASUREITEM)
{ MEASUREITEMSTRUCT *ms=(MEASUREITEMSTRUCT*)lParam;
bool valid=false;
for (list<TProjectInfo>::const_iterator i=
dlcprojects.begin(); i!=dlcprojects.end(); i++)
{ if ((*i).id==(int)ms->itemID) valid=(*i).valid;
}
LONG ret = CallWindowProc(OldDLCProc,hs,msg,
wParam,lParam);
if (!valid) {ms->itemWidth=1; ms->itemHeight=0;}
return ret;
}
return CallWindowProc(OldDLCProc,hs,msg,wParam,lParam);
}
And the following is how to install the subclass, in response to WM_CREATE
of our main window (i.e. the normal place to create the DocList
). Observe our choice of initial folder. That's because, in this example app, I have disallowed the "All Folders" option -- and it would be rude for the DocList
to start in the default All Folders if the user couldn't subsequently select it.
case WM_CREATE:
{ wstring initf=BuildProjectsList();
DOCLISTCREATE dlc; ZeroMemory(&dlc,sizeof(dlc));
dlc.dwStructSize=sizeof(dlc);
dlc.hwndParent=hwnd;
dlc.pszFolder = initf.c_str();
dlc.pstrFilter = L"All files\0*.*\0Text\0*.pwd;*.txt\0";
dlc.wId=102;
hdlc = DocList_Create(&dlc);
OldDLCProc=(WNDPROC)SetWindowLong(hdlc,GWL_WNDPROC,
(LONG)NewDLCProc);
RECT rc; GetClientRect(hwnd,&rc);
MoveWindow(hdlc,0,0,rc.right,rc.bottom,false);
DocList_Refresh(hdlc);