Original Post
Long source box ahead. I'm cleaning up a lot of my source code for my IM client. Attached is the header file for the contact list window. Normally I do pretty well to constrain a class to one responsibility, but my eyebrows are raised when the implementation file for this particular class is over 1,700 lines long. Does this make it a candidate for refactoring? It is pretty unwieldy code, as it stands. I'm not sure if its even possible to split up, however. I think the main problem is the fact that it both presents the contact list, as well as having a number of bindings to the main app event system. Can anyone suggest a way to break this up into more manageable pieces?
class ContactListWindow :
public CFrameWindowImpl<ContactListWindow, CWindow, CFrameWinTraits>
{
public:
ContactListWindow(ContactListPrefs& prefs);
~ContactListWindow();
DECLARE_FRAME_WND_CLASS(L"ContactListWindow", 0)
BEGIN_MSG_MAP(ContactListWindow)
COMMAND_ID_HANDLER_EX(ID_OPENIM_ACCOUNTS, OnAccounts)
COMMAND_ID_HANDLER_EX(ID_OPENIM_OPTIONS, OnOptions)
COMMAND_ID_HANDLER_EX(ID_OPENIM_EXIT, OnExit)
COMMAND_ID_HANDLER_EX(ID_VIEW_SHOWOFFLINECONTACTS, OnShowOfflineContacts)
COMMAND_ID_HANDLER_EX(ID_HELP_FEEDBACK, OnFeedback)
COMMAND_ID_HANDLER_EX(ID_HELP_ABOUT, OnAbout)
MSG_WM_CLOSE(OnClose)
MSG_WM_CONTEXTMENU(OnContextMenu)
MSG_WM_CREATE(OnCreate)
MESSAGE_HANDLER(WM_DEFERREDUPDATE, OnDeferredUpdate)
//MSG_WM_DISPLAYCHANGE(OnDisplayChange)
MESSAGE_HANDLER(WM_EDITITEM, OnEditItem)
MESSAGE_HANDLER(WM_SHOWSTATUSMENU, OnShowStatusMenu)
MSG_WM_LBUTTONUP(OnLButtonUp)
MSG_WM_RBUTTONDOWN(OnRButtonDown)
MSG_WM_PARENTNOTIFY(OnParentNotify)
MSG_WM_MOUSEMOVE(OnMouseMove)
MSG_WM_MENUSELECT(OnMenuSelect)
MSG_WM_SIZE(OnSize)
MESSAGE_HANDLER(WM_STARTCONVERSATION, OnStartConversation)
MESSAGE_HANDLER(WM_TIMER, OnTimer)
NOTIFY_CODE_HANDLER(NM_CUSTOMDRAW, OnCustomDraw)
NOTIFY_CODE_HANDLER(NM_DBLCLK, OnDoubleClick)
NOTIFY_CODE_HANDLER(TTN_GETDISPINFO, OnToolTipGetDispInfo);
NOTIFY_CODE_HANDLER(TVN_KEYDOWN, OnKeyDown)
NOTIFY_CODE_HANDLER(TVN_BEGINDRAG, OnBeginDrag)
NOTIFY_CODE_HANDLER(TVN_BEGINLABELEDIT, OnBeginLabelEdit)
NOTIFY_CODE_HANDLER(TVN_ENDLABELEDIT, OnEndLabelEdit)
CHAIN_MSG_MAP(CFrameWindowImpl<ContactListWindow>)
END_MSG_MAP()
void ApplyPreferences();
void EnforceConstraints();
void OnEnumWindows(HWND wnd);
private:
enum NodeType
{
AccountNodeType,
ContactNodeType,
GroupNodeType
};
template<typename T>
struct Node
{
NodeType Type;
T* Data;
CTreeItem Item;
HTREEITEM Parent;
int Position;
};
struct AccountNode :
public Node<Account>
{
UINT_PTR SignOnTimer;
bool SigningOn;
};
typedef std::deque<Node<Contact>*> ContactNodes;
bool CanMove(CTreeItem movingItem, CTreeItem targetItem);
void Expand(CTreeItem root);
Account* GetAccountFromNode(Node<void>*);
int GetIconIndex(Account* account);
int GetIconIndex(Contact* contact);
String GetLabel(Account*);
String GetLabel(Contact*);
String GetLabel(Group*);
HTREEITEM GetInsertPosition(HTREEITEM parent, int position);
void OnAbout(UINT, int, HWND);
void OnAccounts(UINT, int, HWND);
void OnAccountConnect(AccountConnectEvent&);
void OnAccountConnectError(AccountConnectErrorEvent&);
void OnAccountCreate(AccountCreateEvent&);
void OnAccountDestroy(AccountDestroyEvent&);
void OnAccountDisconnect(AccountDisconnectEvent&);
void OnAccountError(AccountErrorEvent&);
void OnAccountStatusUpdate(AccountStatusUpdateEvent&);
void OnAccountUpdate(AccountUpdateEvent&);
void OnAppGlobalSetStatus(AppGlobalSetStatusEvent&);
LRESULT OnBeginDrag(WPARAM, NMHDR* nmhdr, BOOL&);
LRESULT OnBeginLabelEdit(WPARAM, NMHDR* nmhdr, BOOL&);
void OnClose();
void OnContactCreate(ContactCreateEvent&);
void OnContactDelete(Node<Contact>* node);
void OnContactDestroy(ContactDestroyEvent&);
void OnContactUpdate(ContactUpdateEvent&);
void OnContextMenu(HWND, CPoint);
void OnContextMenuAccount(AccountNode* node, CPoint pt);
void OnContextMenuContact(Node<Contact>* node, CPoint pt);
void OnContextMenuGroup(Node<Group>* node, CPoint pt);
LRESULT OnCreate(CREATESTRUCT*);
LRESULT OnCustomDraw(WPARAM, NMHDR* nmhdr, BOOL&);
LRESULT OnDeferredUpdate(UINT, WPARAM, LPARAM, BOOL&);
void OnDisplayChange(UINT imageDepth, CSize resolution);
LRESULT OnDoubleClick(WPARAM, NMHDR* nmhdr, BOOL&);
LRESULT OnEditItem(UINT, WPARAM, LPARAM, BOOL&);
LRESULT OnEndLabelEdit(WPARAM, NMHDR* nmhdr, BOOL&);
void OnExit(UINT, int, HWND);
void OnFeedback(UINT, int, HWND);
void OnFinalMessage(HWND);
void OnGroupCreate(GroupCreateEvent&);
void OnGroupDelete(Node<Group>* node);
void OnGroupDestroy(GroupDestroyEvent&);
void OnGroupUpdate(GroupUpdateEvent&);
LRESULT OnKeyDown(WPARAM, NMHDR* nmhdr, BOOL&);
void OnLButtonUp(UINT nFlags, CPoint point);
void OnRButtonDown(UINT nFlags, CPoint point);
void OnMenuSelect(UINT, UINT, HMENU ) {}
void OnMouseMove(UINT nFlags, CPoint point);
void OnOptions(UINT, int, HWND);
void OnParentNotify(UINT message, UINT /*msg2*/, LPARAM lParam);
void OnPreferencesUpdated(PreferencesUpdatedEvent&);
void OnSize(UINT, CSize);
void OnShowOfflineContacts(UINT, int, HWND);
LRESULT OnShowStatusMenu(UINT, WPARAM, LPARAM, BOOL&);
void OnShutdown(ShutdownEvent&);
LRESULT OnStartConversation(UINT, WPARAM, LPARAM, BOOL&);
LRESULT OnTimer(UINT, WPARAM, LPARAM, BOOL&);
void OnTimerEvent(TimerEvent&);
LRESULT OnToolTipGetDispInfo(WPARAM, NMHDR* nmhdr, BOOL&);
void Refresh(HTREEITEM item = TVI_ROOT);
int ShowContextMenu(CMenu& menu, CPoint where);
void Update(Account* account);
void Update(Contact* contact);
void Update(Group* group);
void Update(HTREEITEM item);
private:
enum Transaction
{
None,
AddContact,
AddGroup,
RenameContact,
RenameGroup
};
typedef std::map<String, AccountNode* > AccountTable;
typedef std::map<String, ContactNodes* > ContactTable;
typedef std::map<String, Node<Group>* > GroupTable;
CTreeViewCtrlEx m_tree;
CMultiPaneStatusBarCtrl m_statusBar;
ImageMap m_imageMap;
CToolTipCtrl m_toolTip;
ContactListPrefs& m_prefs;
PreferencesPane m_pane;
Transaction m_transaction;
AccountTable m_accounts;
ContactTable m_contacts;
GroupTable m_groups;
CMenu m_accountContextMenu;
CMenu m_contactContextMenu;
CMenu m_groupContextMenu;
CMenu m_treeContextMenu;
CMenu m_statusChangeMenu;
bool m_dragging;
CTreeItem m_draggedItem;
bool m_disableAlwaysOnTop;
};